"""
LLM Player - handles LLM interaction for playing Sudoku.
"""

from __future__ import annotations

import time
from typing import Optional, Tuple
from ..core import LLMConfig, PlayResult, PlayMode, SudokuGame
from ..sudoku.board import SudokuBoard
from ..sudoku.validator import SudokuValidator
from .client import LLMClient
from .prompts import build_step_prompt, build_oneshot_prompt, parse_move, parse_solution
from .recorder import GameRecorder


class LLMPlayer:
    """Handles LLM gameplay for Sudoku."""

    MAX_MOVES = 500
    MAX_RETRIES = 3

    def __init__(self, config: LLMConfig, recorder: Optional[GameRecorder] = None):
        self.config = config
        self.client = LLMClient(config)
        self.recorder = recorder or GameRecorder()

    def play(self, game: SudokuGame, mode: PlayMode = PlayMode.STEP_BY_STEP,
             max_moves: int = None) -> PlayResult:
        start_time = time.time()
        max_moves = max_moves or self.MAX_MOVES

        if mode == PlayMode.STEP_BY_STEP:
            result = self._play_step_by_step(game, max_moves)
        else:
            result = self._play_one_shot(game)

        result.time_elapsed = time.time() - start_time
        self.recorder.save_play_result(result)
        return result

    def _play_step_by_step(self, game: SudokuGame, max_moves: int) -> PlayResult:
        board = SudokuBoard(game.puzzle)
        solution = SudokuBoard(game.solution)
        moves = []
        step = 0

        while step < max_moves:
            empty_cells = board.get_empty_cells()
            if not empty_cells:
                break

            prompt = build_step_prompt(board.grid, step + 1, moves)
            try:
                response = self.client.chat(prompt)
                parsed = parse_move(response.content)

                if not parsed:
                    board_copy = board.copy()
                    self._try_random_valid_move(board, solution)
                    step += 1
                    continue

                row, col, value, reasoning = parsed

                if not (0 <= row < 9 and 0 <= col < 9 and 1 <= value <= 9):
                    continue

                is_valid = SudokuValidator.is_valid_move(board, row, col, value)

                move = self.recorder.log_move(
                    game_id=game.id,
                    step=step + 1,
                    row=row,
                    col=col,
                    value=value,
                    is_valid=is_valid,
                    reasoning=reasoning,
                )
                moves.append(move)

                if is_valid:
                    board.set(row, col, value)
                else:
                    correct_value = solution.get(row, col)
                    if correct_value == value:
                        board.set(row, col, value)

                step += 1

            except Exception as e:
                continue

        return PlayResult(
            game_id=game.id,
            model_name=self.config.model,
            mode=PlayMode.STEP_BY_STEP.value,
            completed=len(board.get_empty_cells()) == 0,
            correct=board == solution,
            total_moves=len(moves),
            valid_moves=sum(1 for m in moves if m.is_valid),
            invalid_moves=sum(1 for m in moves if not m.is_valid),
            time_elapsed=0,
            difficulty=game.difficulty,
            moves=moves,
            final_board=board.grid,
        )

    def _play_one_shot(self, game: SudokuGame) -> PlayResult:
        prompt = build_oneshot_prompt(game.puzzle)
        
        try:
            response = self.client.chat(prompt, max_tokens=4096)
            solution_grid = parse_solution(response.content)

            if solution_grid and len(solution_grid) == 9:
                is_correct = solution_grid == game.solution
                is_valid = SudokuValidator.is_valid_solution(solution_grid)

                move = self.recorder.log_move(
                    game_id=game.id,
                    step=1,
                    row=0,
                    col=0,
                    value=1,
                    is_valid=is_valid,
                    reasoning=response.content[:500],
                )

                return PlayResult(
                    game_id=game.id,
                    model_name=self.config.model,
                    mode=PlayMode.ONE_SHOT.value,
                    completed=True,
                    correct=is_correct,
                    total_moves=1,
                    valid_moves=1 if is_valid else 0,
                    invalid_moves=0 if is_valid else 1,
                    time_elapsed=0,
                    difficulty=game.difficulty,
                    moves=[move],
                    final_board=solution_grid if solution_grid else game.puzzle,
                )
        except Exception:
            pass

        return PlayResult(
            game_id=game.id,
            model_name=self.config.model,
            mode=PlayMode.ONE_SHOT.value,
            completed=False,
            correct=False,
            total_moves=0,
            valid_moves=0,
            invalid_moves=0,
            time_elapsed=0,
            difficulty=game.difficulty,
            moves=[],
            final_board=game.puzzle,
        )

    def _try_random_valid_move(self, board: SudokuBoard, solution: SudokuBoard) -> bool:
        empty = board.get_empty_cells()
        if not empty:
            return False
        
        for row, col in empty:
            correct = solution.get(row, col)
            if SudokuValidator.is_valid_move(board, row, col, correct):
                board.set(row, col, correct)
                return True
        return False


def play_game(game: SudokuGame, config: LLMConfig, 
              mode: PlayMode = PlayMode.STEP_BY_STEP) -> PlayResult:
    player = LLMPlayer(config)
    return player.play(game, mode)