from datetime import datetime, timezone

from fastapi import APIRouter, Depends, HTTPException, Response
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import joinedload, selectinload

from app.core.database import get_session
from app.models.encounter import Encounter
from app.models.game import Game
from app.models.nuzlocke_run import NuzlockeRun
from app.schemas.run import RunCreate, RunDetailResponse, RunResponse, RunUpdate

router = APIRouter()


@router.post("", response_model=RunResponse, status_code=201)
async def create_run(
    data: RunCreate, session: AsyncSession = Depends(get_session)
):
    # Validate game exists
    game = await session.get(Game, data.game_id)
    if game is None:
        raise HTTPException(status_code=404, detail="Game not found")

    run = NuzlockeRun(
        game_id=data.game_id,
        name=data.name,
        status="active",
        rules=data.rules,
    )
    session.add(run)
    await session.commit()
    await session.refresh(run)
    return run


@router.get("", response_model=list[RunResponse])
async def list_runs(session: AsyncSession = Depends(get_session)):
    result = await session.execute(
        select(NuzlockeRun).order_by(NuzlockeRun.started_at.desc())
    )
    return result.scalars().all()


@router.get("/{run_id}", response_model=RunDetailResponse)
async def get_run(run_id: int, session: AsyncSession = Depends(get_session)):
    result = await session.execute(
        select(NuzlockeRun)
        .where(NuzlockeRun.id == run_id)
        .options(
            joinedload(NuzlockeRun.game),
            selectinload(NuzlockeRun.encounters)
            .joinedload(Encounter.pokemon),
            selectinload(NuzlockeRun.encounters)
            .joinedload(Encounter.current_pokemon),
            selectinload(NuzlockeRun.encounters)
            .joinedload(Encounter.route),
        )
    )
    run = result.scalar_one_or_none()
    if run is None:
        raise HTTPException(status_code=404, detail="Run not found")
    return run


@router.patch("/{run_id}", response_model=RunResponse)
async def update_run(
    run_id: int,
    data: RunUpdate,
    session: AsyncSession = Depends(get_session),
):
    run = await session.get(NuzlockeRun, run_id)
    if run is None:
        raise HTTPException(status_code=404, detail="Run not found")

    update_data = data.model_dump(exclude_unset=True)

    # Auto-set completed_at when ending a run
    if "status" in update_data and update_data["status"] in ("completed", "failed"):
        if run.status != "active":
            raise HTTPException(
                status_code=400, detail="Only active runs can be ended"
            )
        update_data["completed_at"] = datetime.now(timezone.utc)

    for field, value in update_data.items():
        setattr(run, field, value)

    await session.commit()
    await session.refresh(run)
    return run


@router.delete("/{run_id}", status_code=204)
async def delete_run(
    run_id: int, session: AsyncSession = Depends(get_session)
):
    run = await session.get(NuzlockeRun, run_id)
    if run is None:
        raise HTTPException(status_code=404, detail="Run not found")

    # Delete associated encounters first
    encounters = await session.execute(
        select(Encounter).where(Encounter.run_id == run_id)
    )
    for enc in encounters.scalars():
        await session.delete(enc)

    await session.delete(run)
    await session.commit()
    return Response(status_code=204)
