package kr.lunaticbum.back.lun.controllers

import kotlinx.coroutines.reactor.awaitSingleOrNull
import kr.lunaticbum.back.lun.model.* // 필요한 모든 모델 클래스를 import
import org.springframework.beans.factory.annotation.Value
import org.springframework.core.io.UrlResource
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.ui.Model
import org.springframework.web.bind.annotation.*
import org.springframework.web.multipart.MultipartFile
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
import java.nio.file.Paths

/**
 * [통합 게임 API 허브 컨트롤러]
 * 1. 모든 게임의 HTML 페이지 서빙
 * 2. 모든 게임의 플레이 로직 API (게임 시작, 검증, 상태 업데이트 등) 제공
 * (기존 SudokuController, SpiderController의 기능을 모두 통합)
 */
@RestController
@RequestMapping("/puzzle") // 모든 게임 API는 /puzzle 공통 경로 하위에 배치
class PuzzleController(
    // 모든 게임 로직이 통합된 PuzzleService 하나만 주입받음
    private val puzzleService: PuzzleService,
    @Value("\${puzzle.image.path}") private val puzzleImagePath: String
) {

    // [신규 추가] 저장된 퍼즐 이미지를 제공하는 API
    @GetMapping("/images/{filename}")
    fun getPuzzleImage(@PathVariable filename: String): ResponseEntity<Any> {
        return try {
            val path = Paths.get(puzzleImagePath).resolve(filename)
            val resource = UrlResource(path.toUri())

            if (resource.exists() || resource.isReadable) {
                ResponseEntity.ok()
                    .contentType(MediaType.IMAGE_PNG) // 이미지는 PNG로 저장했으므로
                    .body(resource)
            } else {
                ResponseEntity.notFound().build()
            }
        } catch (e: Exception) {
            ResponseEntity.internalServerError().build()
        }
    }

    // ======================================================
    // 1. NONOGRAM API (기존 엔드포인트 유지)
    // ======================================================

    /**
     * 노노그램: 이미지 업로드 및 퍼즐 생성
     */
    @PostMapping("upload.bjx")
    suspend fun createPuzzleFromImage(@RequestParam("imageFile") imageFile: MultipartFile): ResponseEntity<Any> {
        return try {
            val savedPuzzle = puzzleService.generateAndSavePuzzle(imageFile)
            ResponseEntity.ok(savedPuzzle)
        } catch (e: Exception) {
            e.printStackTrace()
            ResponseEntity.internalServerError().body("이미지 처리 중 오류 발생: ${e.message}")
        }
    }

    /**
     * 노노그램: 퍼즐 ID로 삭제
     */
    @DeleteMapping("/{id}.bjx")
    suspend fun deletePuzzle(@PathVariable id: String): ResponseEntity<Void> {
        return try {
            puzzleService.deletePuzzle(id)
            ResponseEntity.noContent().build()
        } catch (e: Exception) {
            ResponseEntity.internalServerError().build()
        }
    }

    // ======================================================
    // 2. SUDOKU API (★ SudokuController에서 마이그레이션됨)
    // ======================================================

    /**
     * 스도쿠: 새 게임 시작 (난이도별 문제 반환)
     */
    @GetMapping("/sudoku/start")
    suspend fun sudokuStartGame(
        // 🔽 [수정] 'level' -> 'difficulty', 기본값 '4'
        @RequestParam(defaultValue = "4") difficulty: String
        // ❌ 'blockSizeStr' 파라미터 제거
    ): PuzzleService.SudokuGameDto {

        // 🔽 [수정] 파라미터 1개만 전달
        return puzzleService.sudoku_startGame(difficulty)
    }

    /**
     * 스도쿠: 사용자가 제출한 답안 검증
     */
    @PostMapping("/sudoku/validate")
    suspend fun sudokuValidate(@RequestBody validateDto: PuzzleService.SudokuValidateDto): Map<String, Boolean> {
        val isCorrect = puzzleService.sudoku_validateSolution(validateDto)
        return mapOf("correct" to isCorrect)
    }

    /**
     * 스도쿠: (관리용) 새 퍼즐 문제 생성 및 DB 저장
     */
//    @GetMapping("/sudoku/sudoku_gen")
//    suspend fun sudokuGenerateSinglePuzzle(): SudokuPuzzle {
//        puzzleService.sudoku_generateAndSavePuzzle()
//        return SudokuPuzzle()
//    }

    @GetMapping("/admin/generate-puzzles")
    suspend fun generateAdminPuzzles(
        @RequestParam(defaultValue = "4") blockSize: Int,
        @RequestParam(defaultValue = "5") count: Int
    ): String {
        // 예: /admin/generate-puzzles?blockSize=4&count=10
        // 16x16 퍼즐 10개 생성
        repeat(count) {
            try {
                puzzleService.sudoku_generateAndSavePuzzle(blockSize)
            } catch (e: Exception) {
                // (퍼즐 문자열이 unique=true이므로 중복되면 예외 발생 가능)
            }
        }
        return "$count 개의 $blockSize x $blockSize 퍼즐 생성 완료."
    }

    // ======================================================
    // 3. SPIDER API (★ SpiderController에서 마이그레이션 및 Coroutine 변환됨)
    // ======================================================

    /**
     * 스파이더: 새 게임 시작 (무늬 수, 카드 장 수 기반)
     */
    @GetMapping("/spider/new")
    suspend fun spiderNewGame(@RequestParam numSuits: Int, @RequestParam numCards: String): SpiderGame {
        return puzzleService.spider_newGame(numSuits, numCards)
    }

    /**
     * 스파이더: ID로 기존 게임 불러오기
     */
    @GetMapping("/spider/{id}")
    suspend fun spiderGetGame(@PathVariable id: String): ResponseEntity<SpiderGame> {
        val game = puzzleService.spider_getGame(id)
        return if (game != null) ResponseEntity.ok(game) else ResponseEntity.notFound().build()
    }

    /**
     * 스파이더: 게임 상태 업데이트 (카드 이동 시)
     */
    @PostMapping("/spider/update")
    suspend fun spiderUpdateGame(@RequestBody game: SpiderGame): SpiderGame {
        return puzzleService.spider_updateGame(game)
    }

    /**
     * 스파이더: 스톡에서 새 카드 분배
     */
    @PostMapping("/spider/deal")
    suspend fun spiderDealCards(@RequestBody request: Map<String, String>): SpiderGame {
        val gameId = request["gameId"] ?: throw IllegalArgumentException("Game ID is required.")
        return puzzleService.spider_dealCardsFromStock(gameId)
    }

    /**
     * 스파이더: 실행 취소 (Undo)
     */
    @PostMapping("/spider/undo")
    suspend fun spiderUndo(@RequestBody request: Map<String, String>): SpiderGame {
        val gameId = request["gameId"] ?: throw IllegalArgumentException("Game ID is required.")
        return puzzleService.spider_undoGame(gameId)
    }

    // ======================================================
    // 4. 페이지 서빙 엔드포인트 (기존 로직 유지)
    // ======================================================

    /**
     * 노노그램: 특정 ID의 퍼즐 플레이 페이지
     */
    @GetMapping("/play/{id}")
    suspend fun playPuzzlePage(@PathVariable id: String, model: Model): ResultMV {
        val puzzle = puzzleService.findById(id).awaitSingleOrNull()
        val vm = ResultMV("content/puzzle/nonogram")
        return if (puzzle != null) {
            vm.model.put("puzzle", puzzle)
            vm
        } else {
            vm.viewName =  "redirect:/"
            vm
        }
    }

    /**
     * 노노그램: 랜덤 퍼즐 플레이 페이지
     */
    @GetMapping("/play")
    suspend fun playRandomPuzzlePage(): ResultMV {
        val puzzle = puzzleService.findRandomPuzzle()
        val vm = ResultMV("content/puzzle/nonogram")
        return if (puzzle != null) {
            vm.model.put("puzzle", puzzle)
            vm
        } else {
            vm.viewName =  "redirect:/"
            vm
        }
    }

    /**
     * 2048: 게임 페이지 서빙
     */
    @GetMapping("/2048")
    suspend fun play2048(): ResultMV {
        val vm = ResultMV("content/puzzle/2048")
        return   vm
    }

    /**
     * 스도쿠: 게임 페이지 서빙
     */
    @GetMapping("/sudoku")
    suspend fun sudoku(): ResultMV {
        val vm = ResultMV("content/puzzle/sudoku")
        return vm
    }

    /**
     * 스도쿠: 게임 페이지 서빙
     */
    @GetMapping("/sudoku_gen.bs")
    suspend fun sudoku_gen(): ResultMV {
        val vm = ResultMV("content/puzzle/sudoku_gen")
        return vm
    }


    /**
     * 스파이더: 게임 페이지 서빙
     */
    @GetMapping("/spider")
    suspend fun spider(): ResultMV {
        val vm = ResultMV("content/puzzle/spider")
        return vm
    }

    /**
     * 메인 페이지 (노노그램 업로드)
     */
    @GetMapping("/", "/upload.bs")
    suspend fun uploadPuzzle() : ResultMV {
        val vm = ResultMV("content/puzzle/upload")
        return vm
    }
}


@RestController
@RequestMapping("/api/ranks") // 모든 랭킹 API는 이 공통 경로를 사용
class GameRankController(private val gameRankService: GameRankService) {

    /**
     * [전체 수정]
     * 서비스가 반환하는 Mono<RankSubmissionResult>를 그대로 받아 Ok(200)로 반환합니다.
     */
    @PostMapping("/submit") // 👈 [중요] /api/ranks/submit이 아닌 /submit
    fun submitRank(@RequestBody rankDto: UnifiedRankDto): Mono<ResponseEntity<Any>> {

        return gameRankService.submitRank(rankDto) // 1. 반환 타입: Mono<RankSubmissionResult>
            .map { rankResult ->                   // 2. 🔽 .collectList() 제거
                // 3. 성공 시 RankSubmissionResult 객체를 body에 담아 OK(200) 응답
                ResponseEntity.ok<Any>(rankResult)
            }
            .onErrorResume { e -> // 👈 이름 중복 등 서비스 레벨의 예외 처리
                // 4. 실패 시 예외 메시지를 400 Bad Request로 반환
                Mono.just(ResponseEntity.badRequest().body(e.message ?: "랭킹 등록 중 오류 발생"))
            }
    }

    /**
     * 모든 게임을 위한 통합 랭킹 조회 엔드포인트
     * 예: /api/ranks/list?gameType=SUDOKU&contextId=123
     * 예: /api/ranks/list?gameType=GAME_2048
     */
    @GetMapping("/list")
    fun getUnifiedRanks(
        @RequestParam gameType: GameType,
        @RequestParam contextId: String? = null
    ): Flux<GameRank> {
        // contextId가 "null" 문자열로 오는 경우를 방지하여 실제 null로 처리
        val effectiveContextId = if (contextId == "null") null else contextId
        return gameRankService.getRanks(gameType, effectiveContextId)
    }
}