"""Playback control endpoints — play, pause, seek, complete, now-playing, speed."""

from __future__ import annotations

import uuid
from datetime import datetime
from typing import Optional

from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from sqlalchemy import select, and_
from sqlalchemy.ext.asyncio import AsyncSession

from app.api.deps import get_user_id, get_db_session
from app.models import Episode, Show, Progress, PlaybackEvent

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


# ── Schemas ──

class PlayRequest(BaseModel):
    episode_id: str
    position_seconds: float = 0


class PauseRequest(BaseModel):
    episode_id: str
    position_seconds: float


class SeekRequest(BaseModel):
    episode_id: str
    position_seconds: float


class CompleteRequest(BaseModel):
    episode_id: str


class SpeedRequest(BaseModel):
    speed: float


# ── Helpers ──

async def _get_or_create_progress(
    db: AsyncSession, user_id: str, episode_id: uuid.UUID
) -> Progress:
    """Get existing progress or create a new one."""
    stmt = select(Progress).where(
        Progress.user_id == user_id,
        Progress.episode_id == episode_id,
    )
    prog = (await db.execute(stmt)).scalar_one_or_none()
    if not prog:
        ep = await db.get(Episode, episode_id)
        prog = Progress(
            user_id=user_id,
            episode_id=episode_id,
            duration_seconds=ep.duration_seconds if ep else None,
        )
        db.add(prog)
        await db.flush()
    return prog


async def _log_event(
    db: AsyncSession, user_id: str, episode_id: uuid.UUID,
    event_type: str, position: float, speed: float = 1.0,
):
    db.add(PlaybackEvent(
        user_id=user_id,
        episode_id=episode_id,
        event_type=event_type,
        position_seconds=position,
        playback_speed=speed,
    ))


async def _validate_episode(db: AsyncSession, user_id: str, episode_id_str: str) -> Episode:
    eid = uuid.UUID(episode_id_str)
    ep = await db.get(Episode, eid)
    if not ep or ep.user_id != user_id:
        raise HTTPException(404, "Episode not found")
    return ep


# ── Endpoints ──

@router.post("/play")
async def play(
    body: PlayRequest,
    user_id: str = Depends(get_user_id),
    db: AsyncSession = Depends(get_db_session),
):
    """Set episode as currently playing."""
    ep = await _validate_episode(db, user_id, body.episode_id)
    prog = await _get_or_create_progress(db, user_id, ep.id)
    prog.position_seconds = body.position_seconds
    prog.last_played_at = datetime.utcnow()
    prog.is_completed = False
    await _log_event(db, user_id, ep.id, "play", body.position_seconds, prog.playback_speed)
    await db.commit()
    return {"status": "playing", "position_seconds": prog.position_seconds}


@router.post("/pause")
async def pause(
    body: PauseRequest,
    user_id: str = Depends(get_user_id),
    db: AsyncSession = Depends(get_db_session),
):
    """Pause and save position."""
    ep = await _validate_episode(db, user_id, body.episode_id)
    prog = await _get_or_create_progress(db, user_id, ep.id)
    prog.position_seconds = body.position_seconds
    prog.last_played_at = datetime.utcnow()
    await _log_event(db, user_id, ep.id, "pause", body.position_seconds, prog.playback_speed)
    await db.commit()
    return {"status": "paused", "position_seconds": prog.position_seconds}


@router.post("/seek")
async def seek(
    body: SeekRequest,
    user_id: str = Depends(get_user_id),
    db: AsyncSession = Depends(get_db_session),
):
    """Seek to position."""
    ep = await _validate_episode(db, user_id, body.episode_id)
    prog = await _get_or_create_progress(db, user_id, ep.id)
    prog.position_seconds = body.position_seconds
    prog.last_played_at = datetime.utcnow()
    await _log_event(db, user_id, ep.id, "seek", body.position_seconds, prog.playback_speed)
    await db.commit()
    return {"status": "seeked", "position_seconds": prog.position_seconds}


@router.post("/complete")
async def complete(
    body: CompleteRequest,
    user_id: str = Depends(get_user_id),
    db: AsyncSession = Depends(get_db_session),
):
    """Mark episode as completed."""
    ep = await _validate_episode(db, user_id, body.episode_id)
    prog = await _get_or_create_progress(db, user_id, ep.id)
    prog.is_completed = True
    prog.position_seconds = prog.duration_seconds or prog.position_seconds
    prog.last_played_at = datetime.utcnow()
    await _log_event(db, user_id, ep.id, "complete", prog.position_seconds, prog.playback_speed)
    await db.commit()
    return {"status": "completed"}


@router.get("/now-playing")
async def now_playing(
    user_id: str = Depends(get_user_id),
    db: AsyncSession = Depends(get_db_session),
):
    """Get the most recently played episode with progress."""
    stmt = (
        select(Episode, Show, Progress)
        .join(Progress, Progress.episode_id == Episode.id)
        .join(Show, Show.id == Episode.show_id)
        .where(
            Progress.user_id == user_id,
            Progress.is_completed == False,  # noqa: E712
        )
        .order_by(Progress.last_played_at.desc())
        .limit(1)
    )
    row = (await db.execute(stmt)).first()
    if not row:
        return None

    ep, show, prog = row
    return {
        "episode": {
            "id": str(ep.id),
            "title": ep.title,
            "audio_url": ep.audio_url,
            "duration_seconds": ep.duration_seconds,
            "artwork_url": ep.artwork_url or show.artwork_url,
        },
        "show": {
            "id": str(show.id),
            "title": show.title,
            "author": show.author,
            "artwork_url": show.artwork_url,
            "show_type": show.show_type,
        },
        "progress": {
            "position_seconds": prog.position_seconds,
            "duration_seconds": prog.duration_seconds,
            "is_completed": prog.is_completed,
            "playback_speed": prog.playback_speed,
            "last_played_at": prog.last_played_at.isoformat() if prog.last_played_at else None,
        },
    }


@router.post("/speed")
async def set_speed(
    body: SpeedRequest,
    user_id: str = Depends(get_user_id),
    db: AsyncSession = Depends(get_db_session),
):
    """Update playback speed for all user's active progress records."""
    if body.speed < 0.5 or body.speed > 3.0:
        raise HTTPException(400, "Speed must be between 0.5 and 3.0")

    # Update the most recent (now-playing) progress record's speed
    stmt = (
        select(Progress)
        .where(
            Progress.user_id == user_id,
            Progress.is_completed == False,  # noqa: E712
        )
        .order_by(Progress.last_played_at.desc())
        .limit(1)
    )
    prog = (await db.execute(stmt)).scalar_one_or_none()
    if prog:
        prog.playback_speed = body.speed
        await db.commit()

    return {"speed": body.speed}
