from __future__ import annotations

from pathlib import Path
from typing import Annotated, Optional
from urllib.parse import urlencode

from fastapi import APIRouter, Cookie, Depends, HTTPException, Request, Response, status
from fastapi.responses import FileResponse, RedirectResponse
from pydantic import BaseModel
from sqlmodel.ext.asyncio.session import AsyncSession

from app.config import settings
from app.db import get_session
from app.routers.ws import broadcast_room_snapshot
from app.services import participants as participants_svc

router = APIRouter(tags=["join"])

SESSION_COOKIE_NAME = "session_id"
FRONTEND_DIR = Path(__file__).resolve().parents[3] / "frontend"
SessionDep = Annotated[AsyncSession, Depends(get_session)]


class ParticipantJoinBody(BaseModel):
    room_id: str = ""
    poll_id: str = ""
    join_token: str = ""
    line_user_id: str = ""
    display_name: str = ""


def _frontend_file_or_404(name: str) -> FileResponse:
    path = FRONTEND_DIR / name
    if not path.is_file():
        raise HTTPException(status_code=404, detail=f"missing frontend file: {name}")
    return FileResponse(path)


@router.get("/join")
async def join_page():
    return _frontend_file_or_404("join.html")


@router.get("/host")
async def host_page(request: Request):
    q = request.url.query
    return RedirectResponse("/ui/host.html" + (f"?{q}" if q else ""))


@router.get("/player")
async def player_page(request: Request):
    q = request.url.query
    return RedirectResponse("/ui/player.html" + (f"?{q}" if q else ""))


@router.get("/display")
async def display_page(request: Request):
    q = request.url.query
    return RedirectResponse("/ui/display.html" + (f"?{q}" if q else ""))


@router.get("/voter")
async def voter_page(request: Request):
    q = request.url.query
    return RedirectResponse("/ui/voter.html" + (f"?{q}" if q else ""))


@router.get("/branch-debug")
async def branch_debug_page(request: Request):
    q = request.url.query
    return RedirectResponse("/ui/branch-debug.html" + (f"?{q}" if q else ""))


@router.get("/api/config/public")
async def public_config():
    return {
        "default_room_id": settings.line_default_room_id,
        "hihaho_player_url": settings.hihaho_player_url,
    }


@router.post("/api/participants/join")
async def participant_join(
    body: ParticipantJoinBody,
    response: Response,
    session: SessionDep,
    session_id: Optional[str] = Cookie(default=None, alias=SESSION_COOKIE_NAME),
):
    join_token = body.join_token.strip()
    room_id = body.room_id.strip()
    participant = None

    if join_token:
        try:
            token_rec, participant = await participants_svc.consume_join_token(session, join_token)
        except ValueError as exc:
            raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=str(exc))
        room_id = participant.room_id
    else:
        if not room_id:
            raise HTTPException(status_code=400, detail="room_id is required")
        participant = await participants_svc.get_or_create_participant(
            session,
            room_id=room_id,
            line_user_id=body.line_user_id.strip(),
            display_name=body.display_name.strip(),
            source="join_page_line" if body.line_user_id.strip() else "join_page_manual",
        )

    rec = await participants_svc.create_or_reuse_session(
        session,
        participant=participant,
        existing_session_id=session_id,
        source="join_page",
    )

    voter_query = {
        "room_id": room_id,
        "voter_id": participant.id,
    }
    if body.poll_id.strip():
        voter_query["poll_id"] = body.poll_id.strip()
    voter_url = "/voter?" + urlencode(voter_query)

    await session.commit()
    await broadcast_room_snapshot(participant.room_id)

    response.set_cookie(
        key=SESSION_COOKIE_NAME,
        value=rec.session_id,
        max_age=settings.audience_session_ttl_seconds,
        httponly=True,
        samesite="lax",
    )
    return {
        "session_id": rec.session_id,
        "participant_id": participant.id,
        "room_id": participant.room_id,
        "join_token_used": bool(join_token),
        "voter_url": voter_url,
        "player_url": voter_url,
    }


@router.post("/api/participants/session/heartbeat")
async def participant_session_heartbeat(
    session: SessionDep,
    session_id: Optional[str] = Cookie(default=None, alias=SESSION_COOKIE_NAME),
):
    rec = await participants_svc.get_session_record(session, session_id)
    await session.commit()
    if not rec:
        return {"ok": False, "reason": "session_not_found"}
    return {
        "ok": True,
        "session_id": rec.session_id,
        "participant_id": rec.participant_id,
        "room_id": rec.room_id,
    }


@router.post("/api/participants/session/disconnect")
async def participant_session_disconnect(
    session: SessionDep,
    session_id: Optional[str] = Cookie(default=None, alias=SESSION_COOKIE_NAME),
):
    rec = await participants_svc.delete_session_record(session, session_id)
    await session.commit()
    if rec:
        await broadcast_room_snapshot(rec.room_id)
    return {
        "ok": bool(rec),
        "session_id": rec.session_id if rec else None,
        "room_id": rec.room_id if rec else None,
    }
