"""
app/api/v1/stream.py — SSE ticket 签发 + SSE 流路由
安全红线（倩倩姐2026-06-08拍板）：SSE 认证用一次性短票 ticket，禁止 ?token= 直传 JWT。

流程：
  1. FE 带 JWT 调 POST /tasks/{id}/sse-ticket → 拿 ticket（60s有效，一次性）
  2. FE 用 ticket 建 EventSource: GET /tasks/{id}/stream?ticket=<ticket>
  3. BE 验 ticket（原子 getdel），用后即失效，换不了其他接口
"""

import logging
from typing import Annotated

import redis as sync_redis
import redis.asyncio as aioredis
from fastapi import APIRouter, Depends, Query
from fastapi.responses import StreamingResponse
from sqlalchemy.orm import Session

from app.core.config import get_settings
from app.core.database import get_db
from app.core.response import ok, raise_not_found, raise_unauthorized
from app.core.sse_ticket import consume_ticket, issue_ticket
from app.middleware.workspace_guard import CurrentUser, get_current_user
from app.models.task import GenerationTask
from app.utils.sse_utils import stream_events

logger = logging.getLogger(__name__)
router = APIRouter()
settings = get_settings()


# ── 签发接口（用 JWT 换 ticket）────────────────────────────
@router.post("/tasks/{task_id}/sse-ticket")
def create_sse_ticket(
    task_id: int,
    current_user: Annotated[CurrentUser, Depends(get_current_user)],
    db: Session = Depends(get_db),
):
    """
    签发一次性 SSE ticket（60s 有效，仅对该 task 有效）。
    FE 拿到 ticket 后立即建 EventSource，不要存起来复用。
    """
    task = (
        db.query(GenerationTask)
        .filter(
            GenerationTask.id == task_id,
            GenerationTask.workspace_id == current_user.workspace_id,
        )
        .first()
    )
    if not task:
        raise_not_found("任务不存在")

    r = sync_redis.from_url(settings.REDIS_URL, decode_responses=True)
    ticket = issue_ticket(r, task_id, current_user.workspace_id)
    return ok({"ticket": ticket, "expires_in": 60})


# ── SSE 流（ticket 认证，绝不传 JWT）─────────────────────
@router.get("/tasks/{task_id}/stream")
async def task_stream(
    task_id: int,
    ticket: str = Query(...),
    last_seq: int = Query(default=0),
):
    """
    SSE 实时进度推送。
    ticket 验证：原子 getdel，用后即失效，无法重放、无法访问其他接口。
    支持断线重连补发（?last_seq=<上次收到的seq>）。
    断线重连时 FE 须重新调 sse-ticket 换新 ticket。
    """
    r_sync = sync_redis.from_url(settings.REDIS_URL, decode_responses=True)
    result = consume_ticket(r_sync, ticket)
    if not result:
        raise_unauthorized("ticket 无效或已过期")

    verified_task_id, workspace_id = result
    if verified_task_id != task_id:
        raise_unauthorized("ticket 与任务不匹配")

    redis_async = aioredis.from_url(settings.REDIS_URL, decode_responses=True)

    return StreamingResponse(
        stream_events(
            redis_client=redis_async,
            task_id=task_id,
            workspace_id=workspace_id,
            last_seq=last_seq,
        ),
        media_type="text/event-stream",
        headers={
            "Cache-Control": "no-cache",
            "X-Accel-Buffering": "no",
            "Connection": "keep-alive",
        },
    )
