"""
pattern-detector.py

보고서 디렉토리에서 패턴을 감지하고 팀별로 집계하여
memory/whisper/team-patterns.json에 저장한다.

사용법:
    python3 pattern-detector.py [--workspace /path] [--days 30]

옵션:
    --workspace : 워크스페이스 루트 경로 (기본: /home/jay/workspace)
    --days      : 최근 N일 내 수정된 보고서만 분석 (기본: 30, 0이면 전체)
"""

import argparse
import json
import os
import re
import sys
from collections import defaultdict
from datetime import datetime, timedelta, timezone
from pathlib import Path
from typing import Any

# ---------------------------------------------------------------------------
# 패턴 정의
# ---------------------------------------------------------------------------

PATTERN_KEYWORDS: dict[str, list[str]] = {
    "test_missing": [
        r"테스트\s*없",
        r"test\s*없",
        r"테스트\s*미작성",
        r"테스트\s*SKIP",
        r"테스트\s*생략",
    ],
    "pyright_error": [
        r"pyright\s*에러",
        r"pyright\s*error",
        r"타입\s*에러",
    ],
    "scope_exceeded": [
        r"scope\s*초과",
        r"범위\s*초과",
        r"추가\s*작업\s*발생",
        r"예상보다\s*커짐",
    ],
    "qc_fail": [
        r"QC\s*FAIL",
        r"qc_verify.*FAIL",
        r"(?<!없음\s)(?<!없는\s)(?<!\w)FAIL(?!\w)",
    ],
    "regression": [
        r"회귀(?!\s*없)",
        r"(?<!\w)regression(?!\s*없)",
        r"기존\s*테스트\s*실패",
    ],
}

# 패턴별 가중치
PATTERN_WEIGHTS: dict[str, float] = {
    "qc_fail": 0.3,
    "test_missing": 0.25,
    "pyright_error": 0.2,
    "scope_exceeded": 0.15,
    "regression": 0.1,
}

# ---------------------------------------------------------------------------
# 핵심 함수
# ---------------------------------------------------------------------------


def detect_patterns(content: str) -> list[str]:
    """
    보고서 내용에서 패턴 타입 목록을 반환한다.
    동일 패턴은 한 번만 반환 (중복 제거).
    """
    found: list[str] = []
    for pattern_type, keywords in PATTERN_KEYWORDS.items():
        for kw in keywords:
            if re.search(kw, content, re.IGNORECASE):
                found.append(pattern_type)
                break  # 해당 타입은 한 번만
    return found


def extract_task_id_from_filename(filename: str) -> str | None:
    """
    파일명에서 task-id를 추출한다.
    예: "task-100.1.md" → "task-100"
    매칭 실패 시 None 반환.
    """
    m = re.match(r"^(task-\d+)", filename)
    if m:
        return m.group(1)
    return None


def extract_team_from_content(
    content: str,
    filename: str,
    tasks: dict[str, Any],
) -> str:
    """
    보고서 내용 또는 task-timers.json에서 팀 이름을 추출한다.
    우선순위: 보고서 내용 > task-timers.json > "unknown"
    """
    # 보고서 내용에서 팀 추출
    # **팀:** dev1-team, 팀: dev1-team, team: dev1-team, team_id: dev1-team
    team_patterns = [
        r"\*\*팀:\*\*\s*([\w-]+)",
        r"^팀:\s*([\w-]+)",
        r"^team:\s*([\w-]+)",
        r"^team_id:\s*([\w-]+)",
        r"팀:\s*([\w-]+)",
        r"team:\s*([\w-]+)",
        r"team_id:\s*([\w-]+)",
    ]
    for tp in team_patterns:
        m = re.search(tp, content, re.IGNORECASE | re.MULTILINE)
        if m:
            team = m.group(1).strip()
            if team:
                return team

    # task-timers.json 역참조
    task_id = extract_task_id_from_filename(filename)
    if task_id and task_id in tasks:
        tid = tasks[task_id].get("team_id", "")
        if tid:
            return str(tid)

    return "unknown"


def calculate_risk_score(
    pattern_counts: dict[str, int],
    total_reports: int,
) -> float:
    """
    risk_score = sum(count * weight) / total_reports
    total_reports가 0이면 0.0 반환.
    """
    if total_reports == 0:
        return 0.0
    total = sum(pattern_counts.get(ptype, 0) * weight for ptype, weight in PATTERN_WEIGHTS.items())
    return total / total_reports


def load_task_timers(workspace: str) -> dict[str, Any]:
    """task-timers.json을 로드한다. 실패 시 빈 dict 반환."""
    path = Path(workspace) / "memory" / "task-timers.json"
    try:
        text = path.read_text(encoding="utf-8")
        data = json.loads(text)
        return data.get("tasks", {})
    except Exception:
        return {}


def collect_report_files(reports_dir: Path, days: int) -> list[Path]:
    """
    보고서 디렉토리에서 .md 파일을 수집한다.
    days > 0이면 최근 days일 내 수정된 파일만 반환.
    days == 0이면 전체 반환.
    """
    if not reports_dir.exists():
        return []

    files = list(reports_dir.glob("*.md"))

    if days > 0:
        cutoff = datetime.now(tz=timezone.utc) - timedelta(days=days)
        filtered: list[Path] = []
        for f in files:
            try:
                mtime = datetime.fromtimestamp(f.stat().st_mtime, tz=timezone.utc)
                if mtime >= cutoff:
                    filtered.append(f)
            except Exception:
                pass
        return filtered

    return files


def analyze_reports(workspace: str, days: int) -> dict[str, Any]:
    """
    보고서 디렉토리를 분석하여 패턴 결과 dict를 반환한다.
    """
    tasks = load_task_timers(workspace)
    reports_dir = Path(workspace) / "memory" / "reports"
    report_files = collect_report_files(reports_dir, days)

    # 팀별 집계: team → pattern_type → set of filenames
    team_pattern_files: dict[str, dict[str, list[str]]] = defaultdict(lambda: defaultdict(list))
    total_analyzed = 0

    for report_path in report_files:
        try:
            content = report_path.read_text(encoding="utf-8")
        except Exception:
            continue

        total_analyzed += 1
        filename = report_path.name
        team = extract_team_from_content(content, filename, tasks)
        patterns = detect_patterns(content)

        for ptype in patterns:
            team_pattern_files[team][ptype].append(filename)

    # 결과 조립
    teams_result: dict[str, Any] = {}
    for team, pattern_map in team_pattern_files.items():
        pattern_counts = {ptype: len(files) for ptype, files in pattern_map.items()}
        risk = calculate_risk_score(pattern_counts, total_analyzed)

        patterns_list = [
            {
                "type": ptype,
                "count": len(files),
                "recent_reports": sorted(files),
            }
            for ptype, files in sorted(pattern_map.items())
        ]

        teams_result[team] = {
            "patterns": patterns_list,
            "risk_score": round(risk, 6),
        }

    # 총 패턴 수
    total_patterns = sum(p["count"] for team_data in teams_result.values() for p in team_data["patterns"])

    # highest_risk_team
    highest_risk_team = ""
    if teams_result:
        highest_risk_team = max(
            teams_result.keys(),
            key=lambda t: teams_result[t]["risk_score"],
        )

    result: dict[str, Any] = {
        "version": 1,
        "last_updated": datetime.now(tz=timezone.utc).strftime("%Y-%m-%dT%H:%M:%S"),
        "teams": teams_result,
        "summary": {
            "total_reports_analyzed": total_analyzed,
            "total_patterns_found": total_patterns,
            "highest_risk_team": highest_risk_team,
        },
    }

    return result


def save_results(result: dict[str, Any], output_path: str) -> None:
    """결과를 JSON 파일로 저장한다. 디렉토리가 없으면 생성한다."""
    path = Path(output_path)
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(
        json.dumps(result, ensure_ascii=False, indent=2),
        encoding="utf-8",
    )


# ---------------------------------------------------------------------------
# CLI 진입점
# ---------------------------------------------------------------------------


def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
    parser = argparse.ArgumentParser(description="보고서에서 세션 패턴을 감지하고 팀별로 집계한다.")
    parser.add_argument(
        "--workspace",
        default=os.environ.get("WORKSPACE_ROOT", str(Path(__file__).resolve().parent.parent)),
        help="워크스페이스 루트 경로 (기본: $WORKSPACE_ROOT 또는 /home/jay/workspace)",
    )
    parser.add_argument(
        "--days",
        type=int,
        default=30,
        help="최근 N일 내 수정된 보고서만 분석 (기본: 30, 0이면 전체)",
    )
    return parser.parse_args(argv)


def main(argv: list[str] | None = None) -> None:
    args = parse_args(argv)
    result = analyze_reports(workspace=args.workspace, days=args.days)

    output_path = str(Path(args.workspace) / "memory" / "whisper" / "team-patterns.json")
    save_results(result, output_path)

    print(json.dumps(result, ensure_ascii=False, indent=2))


if __name__ == "__main__":
    main()
