import asyncio
import json
from datetime import date, timedelta

from fastapi import APIRouter, HTTPException
from pydantic import PositiveInt
from starlette import status

from zangramru.db.models.puzzle import Puzzle
from zangramru.services.puzzles import PuzzleGeneratorDep
from zangramru.settings import settings

from .schema import DailyPuzzleDTO, PuzzleOutputDTO

router = APIRouter()


@router.get("/random")
def random_puzzle(
    generator: PuzzleGeneratorDep,
    board: str | None = None,
) -> PuzzleOutputDTO:
    """Generate a random puzzle."""
    if board:
        puzzle = generator.check_board(board, target=10)
        if puzzle is None:
            raise HTTPException(
                status_code=status.HTTP_421_MISDIRECTED_REQUEST,
                detail="Given board has no solution",
            )
    else:
        puzzle = generator.generate(10)
    return PuzzleOutputDTO.model_validate(puzzle, from_attributes=True)


@router.get("/daily")
async def daily_puzzle(
    generator: PuzzleGeneratorDep,
    number: PositiveInt | None = None,
) -> DailyPuzzleDTO:
    """Generate a random puzzle."""
    # Because they start at one, so we need to subtract one.
    target_date = date.today()
    if number is not None:
        target_date = settings.start_date + timedelta(days=number)

    if target_date > date.today():
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Doesn't exist",
        )

    number = (target_date - settings.start_date).days

    daily_puzzle = await Puzzle.objects().where(Puzzle.number == number).first()

    if daily_puzzle is not None:
        board = [list(daily_puzzle.board[i * 4 : i * 4 + 4]) for i in range(4)]
        return DailyPuzzleDTO(
            number=daily_puzzle.number,
            board=board,
            solutions=json.loads(daily_puzzle.solutions),
            extra=json.loads(daily_puzzle.extra),
            word_defs=json.loads(daily_puzzle.definitions),
        )

    loop = asyncio.get_running_loop()
    puzzle = await loop.run_in_executor(None, generator.generate, 10)
    compressedboard = "".join("".join(row) for row in puzzle.board)
    await Puzzle(
        {
            Puzzle.board: compressedboard,
            Puzzle.number: number,
            Puzzle.solutions: json.dumps(puzzle.solutions),
            Puzzle.definitions: json.dumps(puzzle.word_defs),
            Puzzle.extra: json.dumps(puzzle.extra),
        }
    ).save()
    return DailyPuzzleDTO(
        number=number,
        board=puzzle.board,
        solutions=puzzle.solutions,
        extra=puzzle.extra,
        word_defs=puzzle.word_defs,
    )
