"""AI 최종 검토 — 4축(메타/정책/시청/트렌드) 가중 평균."""
import os
import json
import logging

import httpx

logger = logging.getLogger("music-lab.review")

CLAUDE_SONNET_MODEL_DEFAULT = "claude-sonnet-4-6"
TIMEOUT_S = 60

POLICY_BANNED = {"f-word", "n-word"}  # 운영 시 별도 파일로 — 데모용 자리


def _get_api_key() -> str:
    return os.getenv("ANTHROPIC_API_KEY", "")


def _get_model() -> str:
    return os.getenv("CLAUDE_SONNET_MODEL", CLAUDE_SONNET_MODEL_DEFAULT)


async def run_4_axis(*, pipeline: dict, track: dict, video_meta: dict,
                      metadata: dict, thumbnail_url: str, trend_top: list[str],
                      weights: dict, threshold: int) -> dict:
    api_key = _get_api_key()
    if not api_key:
        return _heuristic(metadata, video_meta, track, trend_top, weights, threshold,
                          fallback_reason="no api key")
    try:
        scores = await _call_claude(pipeline, track, video_meta, metadata,
                                    thumbnail_url, trend_top,
                                    api_key=api_key, model=_get_model())
        return _weighted_verdict(scores, weights, threshold, used_fallback=False)
    except (httpx.HTTPError, httpx.TimeoutException, KeyError, ValueError, json.JSONDecodeError) as e:
        logger.warning("검토 LLM 실패 — 휴리스틱: %s", e)
        return _heuristic(metadata, video_meta, track, trend_top, weights, threshold,
                          fallback_reason=str(e))


def _weighted_verdict(scores: dict, weights: dict, threshold: int,
                       used_fallback: bool) -> dict:
    total = (
        weights["meta"]   / 100 * scores["metadata_quality"]["score"] +
        weights["policy"] / 100 * scores["policy_compliance"]["score"] +
        weights["viewer"] / 100 * scores["viewer_experience"]["score"] +
        weights["trend"]  / 100 * scores["trend_alignment"]["score"]
    )
    return {
        **scores,
        "weighted_total": round(total, 2),
        "verdict": "pass" if total >= threshold else "fail",
        "used_fallback": used_fallback,
    }


async def _call_claude(pipeline, track, video_meta, metadata, thumbnail_url, trend_top,
                        *, api_key: str, model: str):
    user = (
        "트랙·영상·메타데이터를 4축으로 평가하고 JSON만 응답:\n"
        f"트랙: {json.dumps(track, ensure_ascii=False)}\n"
        f"영상: {json.dumps(video_meta)}\n"
        f"메타: {json.dumps(metadata, ensure_ascii=False)}\n"
        f"썸네일: {thumbnail_url}\n"
        f"트렌드: {trend_top}\n"
        '출력: {"metadata_quality":{"score":0-100,"notes":""},'
        '"policy_compliance":{"score":0-100,"issues":[]},'
        '"viewer_experience":{"score":0-100,"notes":""},'
        '"trend_alignment":{"score":0-100,"matched_keywords":[]},'
        '"summary":""}'
    )
    async with httpx.AsyncClient(timeout=TIMEOUT_S) as client:
        resp = await client.post(
            "https://api.anthropic.com/v1/messages",
            headers={
                "x-api-key": api_key,
                "anthropic-version": "2023-06-01",
                "content-type": "application/json",
            },
            json={"model": model, "max_tokens": 1024,
                  "messages": [{"role": "user", "content": user}]},
        )
        resp.raise_for_status()
        text = resp.json()["content"][0]["text"]
        start = text.find("{")
        end = text.rfind("}") + 1
        if start < 0 or end <= start:
            raise ValueError("Claude 응답 JSON 없음")
        return json.loads(text[start:end])


def _heuristic(metadata, video_meta, track, trend_top, weights, threshold, fallback_reason):
    # 메타: 길이·태그 카운트
    title_len = len(metadata.get("title", ""))
    desc_len = len(metadata.get("description", ""))
    tag_n = len(metadata.get("tags", []))
    meta_score = 100 if 5 <= title_len <= 60 and 50 <= desc_len <= 1000 and 5 <= tag_n <= 15 else 50

    # 정책: 금칙어 매치
    text_blob = (metadata.get("title", "") + metadata.get("description", "")).lower()
    policy_score = 100 if not any(w in text_blob for w in POLICY_BANNED) else 30

    # 시청: 영상 길이가 트랙과 큰 차이 없는지 휴리스틱(±5초)
    expected = track.get("duration_sec", video_meta.get("length_sec", 0))
    delta = abs(video_meta.get("length_sec", 0) - expected)
    viewer_score = 90 if delta <= 5 else 60

    # 트렌드: 태그가 트렌드와 겹치는지
    overlap = set(metadata.get("tags", [])) & set(trend_top)
    trend_score = 100 if overlap else 40

    scores = {
        "metadata_quality": {"score": meta_score, "notes": "휴리스틱"},
        "policy_compliance": {"score": policy_score, "issues": []},
        "viewer_experience": {"score": viewer_score, "notes": "휴리스틱"},
        "trend_alignment": {"score": trend_score, "matched_keywords": list(overlap)},
        "summary": f"휴리스틱 fallback: {fallback_reason}",
    }
    return _weighted_verdict(scores, weights, threshold, used_fallback=True)
