"""도메인 헬퍼 — 투표 단계(phase) 계산 + ORM→응답 스키마 직렬화.

phase 는 now 기준 동적 계산 (lib/schedule.ts matchPhase 와 동일 규칙):
  result 존재 → finished
  now >= lockAt   → locked
  now <  opensAt  → scheduled
  그 외           → open
"""
from __future__ import annotations

from datetime import datetime, timedelta, timezone

from .config import settings
from .models import AIPrediction, CrowdStats, Match, UserPrediction
from .schemas import (
    AIPredictionOut,
    CrowdStatsOut,
    MatchOut,
    MatchResult,
    MyPredictionOut,
    MyResultOut,
    Team,
)


def now_utc() -> datetime:
    return datetime.now(timezone.utc)


KST = timezone(timedelta(hours=9))


def ensure_aware(dt: datetime) -> datetime:
    """naive datetime(일부 DB 드라이버 반환)을 UTC aware 로 보정."""
    return dt.replace(tzinfo=timezone.utc) if dt.tzinfo is None else dt


def kst_iso(dt: datetime) -> str:
    """저장값(UTC)을 KST(+09:00) ISO 문자열로 직렬화 — 프론트 표시 기준 통일."""
    return ensure_aware(dt).astimezone(KST).isoformat()


def compute_phase(m: Match, now: datetime | None = None) -> str:
    """투표/경기 단계:
      finished  결과 입력됨 → "종료"
      live      킥오프 이후, 결과 입력 전 → "경기중"
      locked    투표 마감(킥오프 1h 전) ~ 킥오프 → "투표 종료"(비활성)
      scheduled 오픈 전 → "오픈 예정"
      open      투표 중
    """
    now = now or now_utc()
    if m.result_outcome is not None:
        return "finished"
    if now >= ensure_aware(m.kickoff_at):
        return "live"
    if now >= ensure_aware(m.lock_at):
        return "locked"
    if now < ensure_aware(m.opens_at):
        return "scheduled"
    return "open"


def is_votable(m: Match) -> bool:
    """투표 지원 경기 여부 — 전체 조 투표 가능.
    실제 오픈/마감은 is_open_for_voting 의 시간창(D-2 오픈 ~ 킥오프 60분 전 마감)이 결정."""
    return True


def is_open_for_voting(m: Match, now: datetime | None = None) -> bool:
    """제출 허용 여부. demo_force_open 이면 마감 전까지 항상 오픈."""
    now = now or now_utc()
    if not is_votable(m):
        return False
    if m.result_outcome is not None:
        return False
    if now >= ensure_aware(m.lock_at):
        return False
    if settings.demo_force_open:
        return True
    return now >= ensure_aware(m.opens_at)


def _team_a(m: Match) -> Team:
    return Team(
        name=m.team_a_name, shortName=m.team_a_short, code=m.team_a_code, flag=m.team_a_flag
    )


def _team_b(m: Match) -> Team:
    return Team(
        name=m.team_b_name, shortName=m.team_b_short, code=m.team_b_code, flag=m.team_b_flag
    )


def prediction_out(p: AIPrediction, lang: str = "ko") -> AIPredictionOut:
    reason = p.reason_en if lang == "en" and p.reason_en else p.reason_ko
    return AIPredictionOut(
        matchId=p.match_id,
        model=p.model,  # type: ignore[arg-type]
        outcome=p.outcome,  # type: ignore[arg-type]
        scoreA=p.score_a,
        scoreB=p.score_b,
        confidencePct=p.confidence_pct,
        reasonShort=reason,
        generatedAt=p.generated_at.date().isoformat() if p.generated_at else "",
    )


def crowd_out(c: CrowdStats | None, match_id: str) -> CrowdStatsOut:
    if c is None:
        return CrowdStatsOut(matchId=match_id, total=0, teamAWin=0, draw=0, teamBWin=0)
    return CrowdStatsOut(
        matchId=match_id,
        total=c.total,
        teamAWin=c.team_a_win,
        draw=c.draw,
        teamBWin=c.team_b_win,
    )


def my_prediction_out(p: UserPrediction, m: Match) -> MyPredictionOut:
    """유저 픽 1건을 경기 정보와 합쳐 직렬화 (내 지난 예측 목록용)."""
    result = None
    if m.result_outcome is not None:
        result = MyResultOut(
            scoreA=m.result_score_a or 0,
            scoreB=m.result_score_b or 0,
            outcome=m.result_outcome,  # type: ignore[arg-type]
            hitOutcome=p.outcome == m.result_outcome,
        )
    return MyPredictionOut(
        matchId=p.match_id,
        teamA=_team_a(m),
        teamB=_team_b(m),
        kickoffKst=kst_iso(m.kickoff_at),
        outcome=p.outcome,  # type: ignore[arg-type]
        scoreA=p.score_a,
        scoreB=p.score_b,
        submittedAt=kst_iso(p.updated_at or p.created_at),
        result=result,
    )


def match_out(
    m: Match,
    lang: str = "ko",
    include_predictions: bool = True,
    now: datetime | None = None,
) -> MatchOut:
    result = None
    if m.result_outcome is not None:
        result = MatchResult(
            scoreA=m.result_score_a or 0,
            scoreB=m.result_score_b or 0,
            outcome=m.result_outcome,  # type: ignore[arg-type]
        )
    preds: list[AIPredictionOut] = []
    if include_predictions:
        # 모델 순서 고정: GPT, Claude, Gemini
        order = {"GPT": 0, "Claude": 1, "Gemini": 2}
        for p in sorted(m.predictions, key=lambda x: order.get(x.model, 9)):
            preds.append(prediction_out(p, lang))
    # status·phase 를 동일한 실시간 계산값으로 통일 — 워커 틱 지연과 무관하게 일관.
    phase = compute_phase(m, now)
    return MatchOut(
        matchId=m.match_id,
        roundLabel=m.round_label,
        group=m.group,
        teamA=_team_a(m),
        teamB=_team_b(m),
        kickoffKst=kst_iso(m.kickoff_at),
        venue=m.venue,
        opensAt=kst_iso(m.opens_at),
        lockAt=kst_iso(m.lock_at),
        status=phase,
        phase=phase,
        votable=is_votable(m),
        votingOpen=is_open_for_voting(m, now),
        hookText=m.hook_text,
        result=result,
        predictions=preds,
        crowd=crowd_out(m.crowd, m.match_id),
    )
