"""채점 로직 — docs/SCORING.md (SSOT) 그대로 구현.

배점: 정확 스코어 5 · 근접(승패+득실차) 3 · 승패 2 · 부분(한 팀 득점 일치) 1 · 빗나감 0.
단조 증가(정확>근접>승패>부분>빗나감).
배점·배제 대상은 backend/data/scoring.json 에서 기동 시 로드(없으면 아래 기본값).
"""
from __future__ import annotations

import json
import logging
from pathlib import Path

log = logging.getLogger("triplepick.scoring")

# 기본 배점 — backend/data/scoring.json 로드 시 덮어씀 (docs/SCORING.md §2)
SCORE = {"EXACT": 5, "CLOSE": 3, "OUTCOME": 2, "PARTIAL": 1, "MISS": 0}


def outcome_of(score_a: int, score_b: int) -> str:
    if score_a > score_b:
        return "TEAM_A_WIN"
    if score_a < score_b:
        return "TEAM_B_WIN"
    return "DRAW"


def score_prediction(
    pick_a: int, pick_b: int, result_a: int, result_b: int
) -> int:
    """내 예측 vs 실제 결과 → 적중 포인트 (결정론적)."""
    if pick_a == result_a and pick_b == result_b:
        return SCORE["EXACT"]
    if outcome_of(pick_a, pick_b) == outcome_of(result_a, result_b):
        if (pick_a - pick_b) == (result_a - result_b):
            return SCORE["CLOSE"]
        return SCORE["OUTCOME"]
    if pick_a == result_a or pick_b == result_b:
        return SCORE["PARTIAL"]
    return SCORE["MISS"]


# ── 자격 · 배제 (docs/SCORING.md §4, P1 구현) ──────────────────
STAFF_EMAILS: set[str] = set()  # 운영진/임직원 블록리스트
STAFF_DOMAINS = ["o2o.kr", "aio2o.kr"]


def is_excluded(email: str) -> bool:
    e = email.strip().lower()
    return e in STAFF_EMAILS or any(e.endswith("@" + d) for d in STAFF_DOMAINS)


# ── 외부 설정 로드 (backend/data/scoring.json, key-value) ──────
DATA_FILE = Path(__file__).resolve().parent.parent / "data" / "scoring.json"

# 등급 → scoring.json 의 key
_JSON_KEY = {
    "EXACT": "score_exact",
    "CLOSE": "score_close",
    "OUTCOME": "score_outcome",
    "PARTIAL": "score_partial",
    "MISS": "score_miss",
}


def grade_prediction(
    pick_a: int, pick_b: int, result_a: int, result_b: int,
    path: Path | None = None,
) -> tuple[str, int]:
    """유저 투표(픽) vs 실제 결과 → scoring.json 에서 해당 등급의 (key, value).

    score_prediction 과 같은 판정 기준. 예: 승패+득실차 일치 → ("score_close", 3).
    scoring.json 이 없거나 키가 빠지면 기본 SCORE 값으로 대체.
    """
    p = path or DATA_FILE
    try:
        data = json.loads(p.read_text(encoding="utf-8"))
    except (OSError, json.JSONDecodeError) as e:
        log.warning("scoring data 읽기 실패 (%s): %s — 기본 배점 사용", p, e)
        data = {}

    if pick_a == result_a and pick_b == result_b:
        grade = "EXACT"
    elif outcome_of(pick_a, pick_b) == outcome_of(result_a, result_b):
        grade = "CLOSE" if (pick_a - pick_b) == (result_a - result_b) else "OUTCOME"
    elif pick_a == result_a or pick_b == result_b:
        grade = "PARTIAL"
    else:
        grade = "MISS"

    key = _JSON_KEY[grade]
    value = data[key] if isinstance(data.get(key), int) else SCORE[grade]
    return key, value

_SCORE_KEYS = {
    "score_exact": "EXACT",
    "score_close": "CLOSE",
    "score_outcome": "OUTCOME",
    "score_partial": "PARTIAL",
    "score_miss": "MISS",
}


def load_scoring_data(path: Path | None = None) -> dict:
    """data/scoring.json 을 읽어 배점(SCORE)·배제 대상을 갱신. 기동 시 1회 호출.

    SCORE/STAFF_EMAILS/STAFF_DOMAINS 는 타 모듈이 import 한 객체라 재할당 대신
    in-place 갱신. 파일이 없거나 깨져도 기본값으로 동작(부팅 실패 방지).
    """
    p = path or DATA_FILE
    try:
        data = json.loads(p.read_text(encoding="utf-8"))
    except FileNotFoundError:
        log.warning("scoring data 없음 (%s) — 기본 배점 사용", p)
        return {}
    except (OSError, json.JSONDecodeError) as e:
        log.warning("scoring data 읽기 실패 (%s): %s — 기본 배점 사용", p, e)
        return {}

    for key, score_key in _SCORE_KEYS.items():
        if isinstance(data.get(key), int):
            SCORE[score_key] = data[key]
    if isinstance(data.get("excluded_emails"), list):
        STAFF_EMAILS.clear()
        STAFF_EMAILS.update(e.strip().lower() for e in data["excluded_emails"])
    if isinstance(data.get("excluded_domains"), list):
        STAFF_DOMAINS[:] = [
            d.strip().lower().lstrip("@") for d in data["excluded_domains"]
        ]
    log.info(
        "scoring data 로드 (%s): 배점 %s · 배제 이메일 %d · 도메인 %s",
        p, SCORE, len(STAFF_EMAILS), STAFF_DOMAINS,
    )
    return data
