"""Morning Briefing API — read-only + 수동 regenerate.

엔드포인트:
- GET  /api/briefing/latest                : 가장 최근 briefing
- GET  /api/briefing?date=YYYY-MM-DD       : 특정 날짜 briefing
- POST /api/briefing/regenerate?date=...   : 동기 워커 트리거 (admin), DELETE+INSERT tx

응답은 topic 평면 list (axis 반대 — Phase 4 와 달리 country 그룹 X).
각 topic 안에 country_perspectives JSONB 가 들어있어 cross-country 비교 분석을 표현.
"""

from datetime import date as date_type
from datetime import datetime
from typing import Annotated

from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload

from core.auth import get_current_user, require_admin
from core.database import get_session
from models.briefing import BriefingTopic, MorningBriefing
from models.user import User

router = APIRouter()


# ─── Pydantic 응답 모델 ───


class CountryPerspective(BaseModel):
    country: str
    summary: str
    article_ids: list[int] = []


class KeyQuote(BaseModel):
    country: str = ""
    source: str = ""
    quote: str


class TopicResponse(BaseModel):
    id: int  # 2026-05-13 카드 액션 (read/highlight) 호출용 식별자
    topic_rank: int
    topic_label: str
    headline: str
    country_perspectives: list[CountryPerspective]
    divergences: list[str]
    convergences: list[str]
    key_quotes: list[KeyQuote]
    historical_context: str | None = None
    cluster_members: list[int] = []
    article_count: int
    country_count: int
    importance_score: float
    llm_fallback_used: bool
    # 2026-05-13 사용자 액션 — UI 의 카드별 토글
    is_read: bool = False
    read_at: datetime | None = None
    highlighted: bool = False
    highlighted_at: datetime | None = None


class BriefingResponse(BaseModel):
    briefing_date: date_type
    window_start: datetime
    window_end: datetime
    decay_lambda: float
    total_articles: int
    total_countries: int
    total_topics: int
    generation_ms: int | None
    llm_calls: int
    llm_failures: int
    status: str
    headline_oneliner: str | None = None
    topics: list[TopicResponse]


class RegenerateResponse(BaseModel):
    status: str
    briefing_id: int | None
    briefing_date: date_type
    total_topics: int
    total_articles: int
    llm_calls: int
    llm_failures: int
    generation_ms: int
    regenerated: bool


# ─── helpers ───


def _build_response(b: MorningBriefing) -> BriefingResponse:
    topics = []
    for t in sorted(b.topics, key=lambda x: x.topic_rank):
        topics.append(
            TopicResponse(
                id=t.id,
                topic_rank=t.topic_rank,
                topic_label=t.topic_label,
                headline=t.headline,
                country_perspectives=[
                    CountryPerspective(**cp) for cp in (t.country_perspectives or [])
                ],
                divergences=list(t.divergences or []),
                convergences=list(t.convergences or []),
                key_quotes=[KeyQuote(**q) for q in (t.key_quotes or [])],
                historical_context=t.historical_context,
                cluster_members=list(t.cluster_members or []),
                article_count=t.article_count,
                country_count=t.country_count,
                importance_score=t.importance_score,
                llm_fallback_used=t.llm_fallback_used,
                is_read=t.is_read,
                read_at=t.read_at,
                highlighted=t.highlighted,
                highlighted_at=t.highlighted_at,
            )
        )

    return BriefingResponse(
        briefing_date=b.briefing_date,
        window_start=b.window_start,
        window_end=b.window_end,
        decay_lambda=b.decay_lambda,
        total_articles=b.total_articles,
        total_countries=b.total_countries,
        total_topics=b.total_topics,
        generation_ms=b.generation_ms,
        llm_calls=b.llm_calls,
        llm_failures=b.llm_failures,
        status=b.status,
        headline_oneliner=b.headline_oneliner,
        topics=topics,
    )


async def _load_briefing(
    session: AsyncSession,
    target_date: date_type | None,
) -> MorningBriefing | None:
    query = select(MorningBriefing).options(selectinload(MorningBriefing.topics))
    if target_date is not None:
        query = query.where(MorningBriefing.briefing_date == target_date)
    else:
        query = query.order_by(MorningBriefing.briefing_date.desc())
    query = query.limit(1)
    result = await session.execute(query)
    return result.scalar_one_or_none()


# ─── Routes ───


@router.get("/latest", response_model=BriefingResponse)
async def get_latest(
    user: Annotated[User, Depends(get_current_user)],
    session: Annotated[AsyncSession, Depends(get_session)],
):
    """가장 최근 morning briefing."""
    b = await _load_briefing(session, target_date=None)
    if b is None:
        raise HTTPException(status_code=404, detail="아직 생성된 briefing 없음")
    return _build_response(b)


@router.get("", response_model=BriefingResponse)
async def get_briefing(
    user: Annotated[User, Depends(get_current_user)],
    session: Annotated[AsyncSession, Depends(get_session)],
    date: date_type | None = Query(default=None, description="YYYY-MM-DD (KST briefing_date)"),
):
    """특정 날짜 briefing (date 미지정 시 최신)."""
    b = await _load_briefing(session, target_date=date)
    if b is None:
        raise HTTPException(
            status_code=404,
            detail=f"briefing 없음 (date={date})" if date else "아직 생성된 briefing 없음",
        )
    return _build_response(b)


@router.post("/regenerate", response_model=RegenerateResponse)
async def regenerate(
    user: Annotated[User, Depends(require_admin)],
    date: date_type | None = Query(default=None, description="YYYY-MM-DD KST 기준 briefing_date"),
):
    """수동 트리거 (admin). 동기 실행 — delete+insert transaction.

    date 미지정 시 오늘 KST. 같은 날 row 존재 시 transaction 안에서 삭제 후 신규 생성.
    응답 status='success' | 'partial' | 'failed' | 'empty'.
    """
    from workers.briefing_worker import run

    result = await run(target_date=date)
    if result is None:
        raise HTTPException(status_code=500, detail="briefing 워커 실행 실패 (로그 확인)")

    return RegenerateResponse(
        status=result["status"],
        briefing_id=result.get("briefing_id"),
        briefing_date=date or datetime.now().date(),
        total_topics=result["total_topics"],
        total_articles=result["total_articles"],
        llm_calls=result["llm_calls"],
        llm_failures=result["llm_failures"],
        generation_ms=result["generation_ms"],
        regenerated=result.get("regenerated", True),
    )


# ─── 2026-05-13 신규: 날짜 선택 + 카드 액션 ───


class BriefingDateSummary(BaseModel):
    briefing_date: date_type
    total_topics: int
    total_articles: int
    status: str
    read_count: int      # 사용자가 읽음 처리한 토픽 수
    highlighted_count: int


class TopicActionRequest(BaseModel):
    value: bool


class TopicActionResponse(BaseModel):
    id: int
    is_read: bool
    read_at: datetime | None
    highlighted: bool
    highlighted_at: datetime | None


@router.get("/dates", response_model=list[BriefingDateSummary])
async def list_dates(
    user: Annotated[User, Depends(get_current_user)],
    session: Annotated[AsyncSession, Depends(get_session)],
    limit: int = Query(default=60, ge=1, le=365),
):
    """사용 가능한 briefing 날짜 목록 (최신 desc). UI date picker 의 데이터 소스."""
    from sqlalchemy import func, case

    stmt = (
        select(
            MorningBriefing.briefing_date,
            MorningBriefing.total_topics,
            MorningBriefing.total_articles,
            MorningBriefing.status,
            func.count(case((BriefingTopic.is_read.is_(True), 1))).label("read_count"),
            func.count(case((BriefingTopic.highlighted.is_(True), 1))).label("highlighted_count"),
        )
        .outerjoin(BriefingTopic, BriefingTopic.briefing_id == MorningBriefing.id)
        .group_by(MorningBriefing.id)
        .order_by(MorningBriefing.briefing_date.desc())
        .limit(limit)
    )
    rows = (await session.execute(stmt)).all()
    return [
        BriefingDateSummary(
            briefing_date=r.briefing_date,
            total_topics=r.total_topics,
            total_articles=r.total_articles,
            status=r.status,
            read_count=r.read_count or 0,
            highlighted_count=r.highlighted_count or 0,
        )
        for r in rows
    ]


@router.patch("/topics/{topic_id}/read", response_model=TopicActionResponse)
async def set_topic_read(
    topic_id: int,
    body: TopicActionRequest,
    user: Annotated[User, Depends(get_current_user)],
    session: Annotated[AsyncSession, Depends(get_session)],
):
    """토픽 카드 읽음 토글. value=true → 읽음 + read_at=now / false → 해제 + read_at=NULL."""
    topic = await session.get(BriefingTopic, topic_id)
    if topic is None:
        raise HTTPException(status_code=404, detail=f"topic 없음 id={topic_id}")
    topic.is_read = body.value
    topic.read_at = datetime.now() if body.value else None
    await session.commit()
    await session.refresh(topic)
    return TopicActionResponse(
        id=topic.id,
        is_read=topic.is_read,
        read_at=topic.read_at,
        highlighted=topic.highlighted,
        highlighted_at=topic.highlighted_at,
    )


@router.patch("/topics/{topic_id}/highlight", response_model=TopicActionResponse)
async def set_topic_highlight(
    topic_id: int,
    body: TopicActionRequest,
    user: Annotated[User, Depends(get_current_user)],
    session: Annotated[AsyncSession, Depends(get_session)],
):
    """토픽 카드 하이라이트 토글. value=true → highlighted + highlighted_at=now / false → 해제."""
    topic = await session.get(BriefingTopic, topic_id)
    if topic is None:
        raise HTTPException(status_code=404, detail=f"topic 없음 id={topic_id}")
    topic.highlighted = body.value
    topic.highlighted_at = datetime.now() if body.value else None
    await session.commit()
    await session.refresh(topic)
    return TopicActionResponse(
        id=topic.id,
        is_read=topic.is_read,
        read_at=topic.read_at,
        highlighted=topic.highlighted,
        highlighted_at=topic.highlighted_at,
    )
