"""ORM 모델 — 기능정의서 데이터모델 + lib/types.ts 와 정합.

테이블:
- matches            경기 (팀/시각/투표윈도우/상태/결과)
- ai_predictions     GPT/Claude/Gemini 예측 (경기 × 모델)
- crowd_stats        군중 투표 분포 (경기당 1행, 원자적 증분)
- user_predictions   유저 픽 (이메일 식별, 채점/알림 플래그 포함)
- user_points        유저별 누적 포인트 (채점 시 갱신, 이메일당 1행)
"""
from __future__ import annotations

from datetime import date, datetime

from sqlalchemy import (
    JSON,
    Boolean,
    Date,
    DateTime,
    ForeignKey,
    Integer,
    String,
    UniqueConstraint,
    func,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship

from .database import Base

# Outcome: TEAM_A_WIN | DRAW | TEAM_B_WIN
# ModelName: GPT | Claude | Gemini
# MatchStatus: scheduled | open | locked | live | finished


class Match(Base):
    __tablename__ = "matches"

    match_id: Mapped[str] = mapped_column(String, primary_key=True)
    round_label: Mapped[str] = mapped_column(String, default="")
    group: Mapped[str] = mapped_column(String, default="A")

    # 팀 정보 (표시명/약식/코드/이모지) — 분리 컬럼으로 저장
    team_a_name: Mapped[str] = mapped_column(String)
    team_a_short: Mapped[str] = mapped_column(String)
    team_a_code: Mapped[str] = mapped_column(String)
    team_a_flag: Mapped[str] = mapped_column(String, default="")
    team_b_name: Mapped[str] = mapped_column(String)
    team_b_short: Mapped[str] = mapped_column(String)
    team_b_code: Mapped[str] = mapped_column(String)
    team_b_flag: Mapped[str] = mapped_column(String, default="")

    venue: Mapped[str] = mapped_column(String, default="")
    hook_text: Mapped[str] = mapped_column(String, default="")

    # 모든 시각은 timezone-aware (UTC 저장, KST 환산은 표현 계층)
    kickoff_at: Mapped[datetime] = mapped_column(DateTime(timezone=True))
    opens_at: Mapped[datetime] = mapped_column(DateTime(timezone=True))
    lock_at: Mapped[datetime] = mapped_column(DateTime(timezone=True))

    status: Mapped[str] = mapped_column(String, default="scheduled")

    # 결과 (입력 전 None)
    result_score_a: Mapped[int | None] = mapped_column(Integer, nullable=True)
    result_score_b: Mapped[int | None] = mapped_column(Integer, nullable=True)
    result_outcome: Mapped[str | None] = mapped_column(String, nullable=True)
    finished_at: Mapped[datetime | None] = mapped_column(
        DateTime(timezone=True), nullable=True
    )
    results_emailed_at: Mapped[datetime | None] = mapped_column(
        DateTime(timezone=True), nullable=True
    )

    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now()
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
    )

    predictions: Mapped[list["AIPrediction"]] = relationship(
        back_populates="match", cascade="all, delete-orphan"
    )
    crowd: Mapped["CrowdStats | None"] = relationship(
        back_populates="match", uselist=False, cascade="all, delete-orphan"
    )


class AIPrediction(Base):
    __tablename__ = "ai_predictions"
    __table_args__ = (UniqueConstraint("match_id", "model", name="uq_match_model"),)

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    match_id: Mapped[str] = mapped_column(
        ForeignKey("matches.match_id", ondelete="CASCADE")
    )
    model: Mapped[str] = mapped_column(String)  # GPT | Claude | Gemini
    outcome: Mapped[str] = mapped_column(String)
    score_a: Mapped[int] = mapped_column(Integer)
    score_b: Mapped[int] = mapped_column(Integer)
    confidence_pct: Mapped[int] = mapped_column(Integer)
    reason_ko: Mapped[str] = mapped_column(String, default="")
    reason_en: Mapped[str] = mapped_column(String, default="")
    generated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now()
    )
    # 실연동 LLM 출력인지 시드 데이터인지 구분 (재생성 제어용)
    source: Mapped[str] = mapped_column(String, default="seed")  # seed | llm

    match: Mapped["Match"] = relationship(back_populates="predictions")


class CrowdStats(Base):
    __tablename__ = "crowd_stats"

    match_id: Mapped[str] = mapped_column(
        ForeignKey("matches.match_id", ondelete="CASCADE"), primary_key=True
    )
    total: Mapped[int] = mapped_column(Integer, default=0)
    team_a_win: Mapped[int] = mapped_column(Integer, default=0)
    draw: Mapped[int] = mapped_column(Integer, default=0)
    team_b_win: Mapped[int] = mapped_column(Integer, default=0)
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
    )

    match: Mapped["Match"] = relationship(back_populates="crowd")


class UserPrediction(Base):
    __tablename__ = "user_predictions"
    __table_args__ = (
        UniqueConstraint("match_id", "device_id", name="uq_match_device"),
        # 같은 이메일은 같은 경기에 1픽만 (NULL 이메일은 다수 허용 — NULL 은 서로 구별).
        # 신규 DB 에만 자동 적용. 기존 테이블은 앱 로직(이메일 우선 식별)으로 보장.
        UniqueConstraint("match_id", "email", name="uq_match_email"),
    )

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    match_id: Mapped[str] = mapped_column(
        ForeignKey("matches.match_id", ondelete="CASCADE")
    )
    device_id: Mapped[str] = mapped_column(String)  # 비로그인 식별 (클라 생성 uuid)
    outcome: Mapped[str] = mapped_column(String)
    score_a: Mapped[int] = mapped_column(Integer)
    score_b: Mapped[int] = mapped_column(Integer)
    email: Mapped[str | None] = mapped_column(String, nullable=True)
    notify: Mapped[bool] = mapped_column(Boolean, default=False)

    # 채점 (결과 입력 후 갱신)
    points: Mapped[int | None] = mapped_column(Integer, nullable=True)
    scored_at: Mapped[datetime | None] = mapped_column(
        DateTime(timezone=True), nullable=True
    )
    notified: Mapped[bool] = mapped_column(Boolean, default=False)

    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now()
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
    )


class UserPoints(Base):
    """유저별 누적 포인트 — 채점(grade_prediction) 결과를 이메일 단위로 집계.

    등급별 횟수 컬럼은 scoring.json 의 key(score_*) 와 1:1 대응.
    exact_count 는 리더보드 동점 보정 1순위(정확 스코어 횟수)에 사용.
    """

    __tablename__ = "user_points"

    email: Mapped[str] = mapped_column(String, primary_key=True)  # 소문자 정규화
    total_points: Mapped[int] = mapped_column(Integer, default=0)
    exact_count: Mapped[int] = mapped_column(Integer, default=0)
    close_count: Mapped[int] = mapped_column(Integer, default=0)
    outcome_count: Mapped[int] = mapped_column(Integer, default=0)
    partial_count: Mapped[int] = mapped_column(Integer, default=0)
    miss_count: Mapped[int] = mapped_column(Integer, default=0)
    matches_played: Mapped[int] = mapped_column(Integer, default=0)
    first_scored_at: Mapped[datetime | None] = mapped_column(
        DateTime(timezone=True), nullable=True
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
    )


class PageVisit(Base):
    """페이지 방문 — 하루(KST)에 같은 기기(device_id)는 1회만 기록(순수 방문자 수).

    일별 집계 = visit_date 로 GROUP BY COUNT. 같은 날 재방문은 유니크 제약으로 무시.
    """

    __tablename__ = "page_visits"
    __table_args__ = (
        UniqueConstraint("visit_date", "device_id", name="uq_visit_date_device"),
    )

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    visit_date: Mapped[date] = mapped_column(Date, index=True)  # KST 기준 날짜
    device_id: Mapped[str] = mapped_column(String)  # 비로그인 식별 (localStorage uuid)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now()
    )


class Comment(Base):
    """경기별 한마디(댓글). 완전 익명 — 인증/이메일 없음.

    - author_hash: sha256(device_id). 원본 기기ID는 저장하지 않음(추적 불가). 쿨다운 식별용.
    - nickname: 축구+코믹 한국어 5글자(기기 해시로 자동 배정, 기기당 고정).
    - id(PK)·author_hash 는 내부용으로 API 응답에 노출하지 않음.
    - 최신순 조회(created_at DESC) + limit/offset 페이징. is_hidden=True 는 조회 제외.
    """

    __tablename__ = "comments"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
    match_id: Mapped[str] = mapped_column(
        ForeignKey("matches.match_id", ondelete="CASCADE"), index=True
    )
    author_hash: Mapped[str] = mapped_column(String, index=True)  # sha256(device_id) — 원본 비저장
    nickname: Mapped[str] = mapped_column(String)  # 축구 코믹 한국어 5글자
    body: Mapped[str] = mapped_column(String)  # 길이 제한은 스키마(200자)에서
    is_hidden: Mapped[bool] = mapped_column(Boolean, default=False)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), index=True
    )


class FootballCache(Base):
    """축구 데이터 캐시 (API-Football 수집 결과) — 예측 프롬프트 조립용.

    key 규칙:
      team:{CODE}      팀 베이스라인(폼·평균득실·클린시트·스쿼드)
      h2h:{A}-{B}      상대전적
      teamid:{CODE}    팀코드→API팀ID 매핑
    payload 는 가공된 압축 JSON. fetched_at 으로 캐시 신선도(TTL) 판단.
    api·worker 가 공유하는 유일한 영속 저장소가 DB 라 여기에 둔다.
    """

    __tablename__ = "football_cache"

    key: Mapped[str] = mapped_column(String, primary_key=True)
    payload: Mapped[dict] = mapped_column(JSON, default=dict)
    fetched_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now()
    )
