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.nuzlocke_run import NuzlockeRun
from app.models.pokemon import Pokemon
from app.models.route import Route
from app.schemas.encounter import (
    EncounterCreate,
    EncounterDetailResponse,
    EncounterResponse,
    EncounterUpdate,
)

router = APIRouter()


@router.post(
    "/runs/{run_id}/encounters",
    response_model=EncounterResponse,
    status_code=201,
)
async def create_encounter(
    run_id: int,
    data: EncounterCreate,
    session: AsyncSession = Depends(get_session),
):
    # Validate run exists
    run = await session.get(NuzlockeRun, run_id)
    if run is None:
        raise HTTPException(status_code=404, detail="Run not found")

    # Validate route exists and load its children
    result = await session.execute(
        select(Route)
        .where(Route.id == data.route_id)
        .options(selectinload(Route.children))
    )
    route = result.scalar_one_or_none()
    if route is None:
        raise HTTPException(status_code=404, detail="Route not found")

    # Cannot create encounter on a parent route (routes with children)
    if route.children:
        raise HTTPException(
            status_code=400,
            detail="Cannot create encounter on a parent route. Use a child route instead.",
        )

    # If this route has a parent, check if any sibling already has an encounter
    if route.parent_route_id is not None:
        # Get all sibling route IDs (routes with same parent, including this one)
        siblings_result = await session.execute(
            select(Route.id).where(Route.parent_route_id == route.parent_route_id)
        )
        sibling_ids = [r for r in siblings_result.scalars().all()]

        # Check if any sibling already has an encounter in this run
        existing_encounter = await session.execute(
            select(Encounter)
            .where(
                Encounter.run_id == run_id,
                Encounter.route_id.in_(sibling_ids),
            )
        )
        if existing_encounter.scalar_one_or_none() is not None:
            raise HTTPException(
                status_code=409,
                detail="This location group already has an encounter. Only one encounter per location group is allowed.",
            )

    # Validate pokemon exists
    pokemon = await session.get(Pokemon, data.pokemon_id)
    if pokemon is None:
        raise HTTPException(status_code=404, detail="Pokemon not found")

    encounter = Encounter(
        run_id=run_id,
        route_id=data.route_id,
        pokemon_id=data.pokemon_id,
        nickname=data.nickname,
        status=data.status,
        catch_level=data.catch_level,
    )
    session.add(encounter)
    await session.commit()
    await session.refresh(encounter)
    return encounter


@router.patch("/encounters/{encounter_id}", response_model=EncounterDetailResponse)
async def update_encounter(
    encounter_id: int,
    data: EncounterUpdate,
    session: AsyncSession = Depends(get_session),
):
    encounter = await session.get(Encounter, encounter_id)
    if encounter is None:
        raise HTTPException(status_code=404, detail="Encounter not found")

    update_data = data.model_dump(exclude_unset=True)
    for field, value in update_data.items():
        setattr(encounter, field, value)

    await session.commit()

    # Reload with relationships for detail response
    result = await session.execute(
        select(Encounter)
        .where(Encounter.id == encounter_id)
        .options(
            joinedload(Encounter.pokemon),
            joinedload(Encounter.current_pokemon),
            joinedload(Encounter.route),
        )
    )
    return result.scalar_one()


@router.delete("/encounters/{encounter_id}", status_code=204)
async def delete_encounter(
    encounter_id: int, session: AsyncSession = Depends(get_session)
):
    encounter = await session.get(Encounter, encounter_id)
    if encounter is None:
        raise HTTPException(status_code=404, detail="Encounter not found")

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