"""
health_score.py - 0-100 Health Score verifier

8개 카테고리 × 가중치로 종합 점수 산출.
초기 버전: 코드 기반 정적 분석 가능 항목만 (Console, Functional, Performance).
나머지는 NA 또는 수동 입력.

출처: gstack /qa 스킬 Health Score 패턴 (MIT 라이선스, https://github.com/garrytan/gstack)
"""

import os
import re
import subprocess
from typing import Optional


# 카테고리별 가중치 (합계 100%)
CATEGORIES = {
    "Console": 15,       # JS 에러 수
    "Links": 10,         # 깨진 링크 수
    "Visual": 10,        # UI 이슈
    "Functional": 20,    # 기능 버그 (★ 최고 가중치)
    "UX": 15,            # 네비게이션/피드백
    "Performance": 10,   # 로드시간
    "Content": 5,        # 오타/placeholder
    "Accessibility": 15, # ARIA/키보드
}

DEFAULT_REPORTS_DIR = "/home/jay/workspace/memory/reports"


def _score_functional(task_id: str) -> tuple[float, str]:
    """기능 테스트 결과 기반 점수. pytest 결과 활용."""
    workspace = os.environ.get("WORKSPACE_ROOT", "/home/jay/workspace")

    # .done 파일에서 qc_result 확인
    done_path = os.path.join(workspace, "memory", "events", f"{task_id}.done")
    if os.path.exists(done_path):
        try:
            import json
            with open(done_path, "r", encoding="utf-8") as f:
                done_data = json.load(f)
            qc_result = done_data.get("qc_result", "")
            if qc_result == "PASS":
                return 100.0, "QC PASS (tests passed)"
            elif qc_result == "WARN":
                return 70.0, "QC WARN (partial issues)"
            elif qc_result == "FAIL":
                return 30.0, "QC FAIL"
        except (OSError, ValueError):
            pass

    # 보고서에서 테스트 결과 패턴 탐지
    report_path = os.path.join(DEFAULT_REPORTS_DIR, f"{task_id}.md")
    if os.path.exists(report_path):
        try:
            with open(report_path, "r", encoding="utf-8") as f:
                content = f.read()
            if "전체 통과" in content or "all passed" in content.lower():
                return 90.0, "Tests reported as passed in report"
            if "FAIL" in content or "실패" in content:
                return 40.0, "Test failures mentioned in report"
        except OSError:
            pass

    return -1.0, "NA (no test data available)"


def _score_console(task_id: str) -> tuple[float, str]:
    """콘솔 에러 기반 점수. 보고서에서 에러 키워드 추출."""
    report_path = os.path.join(DEFAULT_REPORTS_DIR, f"{task_id}.md")
    if not os.path.exists(report_path):
        return -1.0, "NA (no report)"

    try:
        with open(report_path, "r", encoding="utf-8") as f:
            content = f.read()
        error_count = len(re.findall(r"(?:error|에러|exception|traceback)", content, re.IGNORECASE))
        if error_count == 0:
            return 100.0, "No errors detected in report"
        elif error_count <= 3:
            return 70.0, f"{error_count} error mention(s) in report"
        else:
            return 40.0, f"{error_count} error mention(s) in report"
    except OSError:
        return -1.0, "NA (report read failed)"


def _score_performance(task_id: str) -> tuple[float, str]:
    """성능 기반 점수. pyright 결과 활용."""
    report_path = os.path.join(DEFAULT_REPORTS_DIR, f"{task_id}.md")
    if not os.path.exists(report_path):
        return -1.0, "NA (no report)"

    try:
        with open(report_path, "r", encoding="utf-8") as f:
            content = f.read()
        # pyright 에러 수 추출
        pyright_match = re.search(r"pyright.*?(\d+)\s*error", content, re.IGNORECASE)
        if pyright_match:
            errors = int(pyright_match.group(1))
            if errors == 0:
                return 100.0, "pyright 0 errors"
            else:
                return max(0, 100 - errors * 10), f"pyright {errors} error(s)"
        if "pyright" in content.lower() and "0 error" in content.lower():
            return 100.0, "pyright 0 errors"
    except OSError:
        pass

    return -1.0, "NA (no performance data)"


def verify(task_id: str, **kwargs) -> dict:
    """
    Health Score 산출.

    0-100 점수를 8개 카테고리 가중 평균으로 계산.
    NA 카테고리는 가중치에서 제외하고 나머지로 재분배.

    Args:
        task_id: 검증할 task ID

    Returns:
        {"status": "PASS"|"WARN"|"SKIP", "details": [...], "health_score": float}
    """
    if not task_id:
        return {"status": "SKIP", "details": ["No task_id provided"]}

    scores: dict[str, tuple[float, str]] = {}

    # 측정 가능한 카테고리
    scores["Console"] = _score_console(task_id)
    scores["Functional"] = _score_functional(task_id)
    scores["Performance"] = _score_performance(task_id)

    # 수동 입력 필드 (NA로 남김)
    for cat in ["Links", "Visual", "UX", "Content", "Accessibility"]:
        scores[cat] = (-1.0, "NA (manual input required)")

    # 가중 평균 계산 (NA 제외)
    total_weight = 0
    weighted_sum = 0.0
    details = []

    for cat, (score, reason) in scores.items():
        weight = CATEGORIES[cat]
        if score < 0:
            details.append(f"  {cat} ({weight}%): NA — {reason}")
        else:
            total_weight += weight
            weighted_sum += score * weight
            details.append(f"  {cat} ({weight}%): {score:.0f}/100 — {reason}")

    if total_weight == 0:
        details.insert(0, "Health Score: N/A (no measurable categories)")
        return {"status": "SKIP", "details": details}

    health_score = weighted_sum / total_weight
    details.insert(0, f"Health Score: {health_score:.0f}/100 (based on {total_weight}% of categories)")

    # 판정: 80+ PASS, 50-80 WARN, <50 FAIL은 아님 (WARN)
    if health_score >= 80:
        status = "PASS"
    else:
        status = "WARN"

    result = {"status": status, "details": details}
    result["health_score"] = round(health_score, 1)
    return result
