from __future__ import annotations

import json
import uuid
from collections import deque
from datetime import datetime
from typing import Annotated, Any, Dict, List, Optional

from fastapi import APIRouter, Depends, Header, HTTPException, Query, status
from pydantic import BaseModel, Field
from sqlmodel import select
from sqlmodel.ext.asyncio.session import AsyncSession

from app.config import settings
from app.db import get_session
from app.models.entities import Poll, PollStatus
from app.routers import ws as ws_router
from app.services import hihaho_client, voting as voting_svc
from app.services.interaction_schedule import parse_branches

router = APIRouter(prefix="/hihaho", tags=["hihaho"])

SessionDep = Annotated[AsyncSession, Depends(get_session)]
_DEBUG_EVENTS = deque(maxlen=200)
_DEBUG_SEQ = 0
HIHAHO_EVENT_SPECS = {
    "init": {
        "description": "The hihaho interactive layer / API has been loaded.",
        "fields": [],
    },
    "hihaho_started": {
        "description": "The viewer has started the video.",
        "fields": ["actor", "percentageToSucceed", "session_id", "video_id", "video_uuid"],
    },
    "hihaho_ended": {
        "description": "The viewer has reached the end of the video.",
        "fields": [
            "actor",
            "history",
            "score",
            "maxScore",
            "scaledResult",
            "percentageToSucceed",
            "passed",
            "session_id",
            "video_id",
            "video_uuid",
        ],
    },
    "hihaho_answer": {
        "description": "The viewer has answered a question.",
        "fields": [
            "actor",
            "correct",
            "type",
            "answerText",
            "startTime",
            "endTime",
            "points",
            "history",
            "session_id",
            "video_id",
            "video_uuid",
        ],
    },
    "hihaho_optional_variable_submitted": {
        "description": "The viewer has submitted an optional variable.",
        "fields": ["name", "value", "session_id", "video_id", "video_uuid"],
    },
    "hihaho_interaction_clicked": {
        "description": "The viewer has clicked on an interaction.",
        "fields": ["interaction_name", "value", "action", "session_id", "video_uuid"],
    },
    "hihaho_menu_item_clicked": {
        "description": "The viewer has clicked on a menu item.",
        "fields": ["title", "action", "session_id", "video_id", "video_uuid"],
    },
    "hihaho_chapter_item_clicked": {
        "description": "The viewer has clicked on a chapter item.",
        "fields": ["title", "start_time", "session_id", "video_uuid"],
    },
    "onTime": {
        "description": "Playback heartbeat called roughly every 100ms while the video plays.",
        "fields": ["currentTime", "volume", "duration"],
    },
}


class BranchNotifyBody(BaseModel):
    film_session_id: str = ""
    branch_key: str
    metadata: Optional[Dict[str, Any]] = None


class DebugEventBody(BaseModel):
    room_id: str = ""
    poll_id: str = ""
    film_url: str = ""
    event_id: str = ""
    origin: str = ""
    event_type: str = ""
    source_is_iframe: bool = False
    received_at_client: str = ""
    raw: Any = None
    metadata: Optional[Dict[str, Any]] = None


@router.post("/branch")
async def notify_branch(body: BranchNotifyBody):
    """Proxy to hihaho when API is configured; otherwise returns skipped payload."""
    return await hihaho_client.notify_branch_result(
        film_session_id=body.film_session_id or "default-session",
        branch_key=body.branch_key,
        metadata=body.metadata,
    )


@router.post("/events/debug")
async def ingest_debug_event(body: DebugEventBody):
    global _DEBUG_SEQ
    _DEBUG_SEQ += 1
    payload = body.model_dump()
    payload["server_seq"] = _DEBUG_SEQ
    payload["received_at_server"] = datetime.utcnow().isoformat() + "Z"
    _DEBUG_EVENTS.append(payload)
    return {
        "ok": True,
        "stored": True,
        "count": len(_DEBUG_EVENTS),
        "latest_event_id": body.event_id,
    }


@router.get("/events/spec")
async def hihaho_event_specs():
    return {
        "ok": True,
        "items": HIHAHO_EVENT_SPECS,
    }


@router.get("/events/debug/latest")
async def latest_debug_events(
    limit: int = Query(20, ge=1, le=100),
    room_id: str = Query("", description="filter by room id"),
    poll_id: str = Query("", description="filter by poll id"),
    event_type: str = Query("", description="substring match"),
):
    rows = list(_DEBUG_EVENTS)
    if room_id:
        rows = [r for r in rows if str(r.get("room_id", "")) == room_id]
    if poll_id:
        rows = [r for r in rows if str(r.get("poll_id", "")) == poll_id]
    if event_type:
        needle = event_type.lower()
        rows = [r for r in rows if needle in str(r.get("event_type", "")).lower()]
    rows = rows[-limit:]
    return {
        "ok": True,
        "count": len(rows),
        "items": rows,
    }


# ---------------------------------------------------------------------------
# Auto-setup: parse interaction JSON → create room + polls per branch
# ---------------------------------------------------------------------------

class BranchOptionNames(BaseModel):
    title: str = ""
    option_names: List[str] = Field(min_length=2)


class InteractionSetupBody(BaseModel):
    interactions: Dict[str, Any]
    branches: List[BranchOptionNames] = Field(default_factory=list)
    default_option_names: List[str] = Field(
        default_factory=lambda: ["選項A", "選項B"],
        description="Fallback option names when branches list is shorter than detected branches.",
    )


async def _require_host_key(
    x_host_key: Annotated[Optional[str], Header(alias="X-Host-Key")] = None,
):
    if not settings.host_api_key:
        return
    if x_host_key != settings.host_api_key:
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="invalid host key")


@router.post(
    "/rooms/{room_id}/reset",
    dependencies=[Depends(_require_host_key)],
)
async def reset_room(room_id: str, session: SessionDep):
    """Delete all polls/votes for a room and broadcast a clean snapshot."""
    from sqlalchemy import delete as sa_delete
    from app.models.entities import Vote

    existing = (await session.exec(
        select(Poll).where(Poll.room_id == room_id)
    )).all()
    if existing:
        poll_ids = [p.id for p in existing]
        await session.exec(sa_delete(Vote).where(Vote.poll_id.in_(poll_ids)))
        for p in existing:
            await session.delete(p)
        await session.commit()
    await ws_router.broadcast_room_snapshot(room_id)
    return {"ok": True, "deleted_polls": len(existing)}


@router.post("/interactions/parse")
async def parse_interaction_json(body: InteractionSetupBody):
    """Parse interaction JSON and return detected branch schedules (read-only)."""
    raw_interactions = body.interactions.get("interactions", [])
    if not isinstance(raw_interactions, list):
        raise HTTPException(400, "interactions.interactions must be an array")
    branches = parse_branches(raw_interactions)
    return {"ok": True, "branch_count": len(branches), "branches": branches}


@router.post(
    "/rooms/{room_id}/setup-from-interactions",
    dependencies=[Depends(_require_host_key)],
)
async def setup_from_interactions(
    room_id: str,
    body: InteractionSetupBody,
    session: SessionDep,
):
    """Parse interaction JSON, create a room and one draft poll per branch.

    Each poll is pre-configured with the correct trigger time, voting duration,
    pause action, and winner jump actions derived from the JSON hotspots.
    """
    from sqlalchemy import delete as sa_delete
    from app.models.entities import Vote
    from app.services.voting import ensure_room, set_poll_config

    raw_interactions = body.interactions.get("interactions", [])
    if not isinstance(raw_interactions, list):
        raise HTTPException(400, "interactions.interactions must be an array")

    branches = parse_branches(raw_interactions)
    if not branches:
        raise HTTPException(400, "No branch decision points detected in interaction JSON")

    await ensure_room(session, room_id, room_id)

    existing = (await session.exec(
        select(Poll).where(Poll.room_id == room_id)
    )).all()
    if existing:
        poll_ids = [p.id for p in existing]
        await session.exec(sa_delete(Vote).where(Vote.poll_id.in_(poll_ids)))
        for p in existing:
            await session.delete(p)
        await session.flush()

    created_polls = []
    for idx, branch in enumerate(branches):
        branch_cfg = body.branches[idx] if idx < len(body.branches) else None
        option_names = (
            branch_cfg.option_names
            if branch_cfg and len(branch_cfg.option_names) >= len(branch["options"])
            else body.default_option_names
        )
        title = (
            branch_cfg.title
            if branch_cfg and branch_cfg.title
            else f"劇情選擇 {idx + 1}"
        )

        winner_actions: Dict[str, Any] = {}
        for j, opt_info in enumerate(branch["options"]):
            if j >= len(option_names):
                break
            name = option_names[j]
            winner_actions[name] = {
                "type": "postmessage",
                "payload": {
                    "command": "jump",
                    "position": opt_info["position"],
                },
            }

        poll = Poll(
            id=str(uuid.uuid4()),
            room_id=room_id,
            title=title,
            options_json=json.dumps(
                option_names[: len(branch["options"])], ensure_ascii=False
            ),
            status=PollStatus.draft,
        )
        set_poll_config(poll, {
            "trigger_names": ["onTime"],
            "trigger_keys": [f"currentTime>={branch['start_time']}"],
            "pause_action": {
                "type": "postmessage",
                "payload": {"command": "pause"},
            },
            "winner_actions": winner_actions,
            "duration_seconds": int(branch["duration_seconds"]),
            "branch_start_time": branch["start_time"],
            "branch_end_time": branch["end_time"],
            "branch_default_jump": branch["default_jump_time"],
            "branch_options": branch["options"],
        })
        session.add(poll)
        created_polls.append(poll)

    await session.commit()
    for p in created_polls:
        await session.refresh(p)

    await ws_router.broadcast_room_snapshot(room_id)

    from app.routers.polls import poll_out

    return {
        "ok": True,
        "room_id": room_id,
        "branch_count": len(branches),
        "branches": branches,
        "polls": [poll_out(p) for p in created_polls],
    }
