"""
meeting-audit.py
미팅 기록 파일을 자동 파싱하여 SKILL.md 규칙 준수 여부를 검증하는 standalone 스크립트.
작성자: 카르티케야 (개발4팀 백엔드)
"""

import argparse
import json
import re
from pathlib import Path

DEFAULT_PERSONA_LIST = "/home/jay/workspace/skills/agent-meeting/references/persona-list.md"
DEFAULT_3DOCS_BASE = "/home/jay/workspace/memory/3docs"


# ---------------------------------------------------------------------------
# parse_meeting_file
# ---------------------------------------------------------------------------


def parse_meeting_file(filepath: str) -> dict:
    """미팅 파일을 파싱하여 메타데이터를 반환

    returns: {"total_cycles": int, "personas": list[str], "mode": str, "depth": str, "content": str}
    """
    path = Path(filepath)
    if not path.exists():
        raise FileNotFoundError(f"미팅 파일을 찾을 수 없습니다: {filepath}")

    content = path.read_text(encoding="utf-8")

    if not content.strip():
        return {
            "total_cycles": 0,
            "personas": [],
            "mode": "",
            "depth": "",
            "content": content,
        }

    # 총 사이클 수
    cycles_match = re.search(r"\*\*총 사이클 수\*\*\s*:\s*(\d+)", content)
    total_cycles = int(cycles_match.group(1)) if cycles_match else 0

    # 참여 페르소나
    personas_match = re.search(r"\*\*참여 페르소나\*\*\s*:\s*(.+)", content)
    if personas_match:
        raw_personas = personas_match.group(1).strip()
        personas: list[str] = [p.strip() for p in raw_personas.split(",") if p.strip()]
    else:
        personas = []

    # 미팅 모드
    mode_match = re.search(r"\*\*미팅 모드\*\*\s*:\s*(.+)", content)
    mode = mode_match.group(1).strip() if mode_match else ""

    # 토론 깊이
    depth_match = re.search(r"\*\*토론 깊이\*\*\s*:\s*(.+)", content)
    depth = depth_match.group(1).strip() if depth_match else ""

    return {
        "total_cycles": total_cycles,
        "personas": personas,
        "mode": mode,
        "depth": depth,
        "content": content,
    }


# ---------------------------------------------------------------------------
# check_cycle_count
# ---------------------------------------------------------------------------


def check_cycle_count(parsed: dict, level: int) -> dict:
    """사이클 수 vs 레벨 기대값 검증

    returns: {"status": "PASS"|"WARN"|"FAIL", "expected_min": int, "actual": int, "message": str}
    """
    expected_min_map = {1: 0, 2: 1, 3: 2, 4: 3}
    expected_min = expected_min_map.get(level, 0)
    actual = parsed.get("total_cycles", 0)

    if level == 1:
        return {
            "status": "PASS",
            "expected_min": expected_min,
            "actual": actual,
            "message": "Lv.1은 사이클 수 체크를 건너뜁니다.",
        }

    if actual >= expected_min:
        return {
            "status": "PASS",
            "expected_min": expected_min,
            "actual": actual,
            "message": f"사이클 수 충족: actual={actual} >= expected_min={expected_min}",
        }
    else:
        return {
            "status": "FAIL",
            "expected_min": expected_min,
            "actual": actual,
            "message": f"사이클 수 부족: actual={actual} < expected_min={expected_min} (Lv.{level} 기준)",
        }


# ---------------------------------------------------------------------------
# check_persona_validation
# ---------------------------------------------------------------------------


def _extract_korean_name(persona_str: str) -> str:
    """페르소나 문자열에서 한글명을 추출.
    예: "토르 (Thor)" → "토르", "엔키(백엔드)" → "엔키"
    """
    # 괄호 앞 부분을 한글명으로 간주
    match = re.match(r"([가-힣]+)", persona_str.strip())
    if match:
        return match.group(1)
    return persona_str.strip()


def _parse_registered_personas(persona_list_path: str) -> list[str]:
    """persona-list.md에서 등록된 한글명 목록을 파싱."""
    path = Path(persona_list_path)
    if not path.exists():
        return []

    content = path.read_text(encoding="utf-8")
    registered: list[str] = []

    # 테이블 행에서 첫 번째 셀(페르소나 이름) 추출
    # 형식: | 토르 (Thor) | 팀 | 전문성 |
    for line in content.splitlines():
        line = line.strip()
        if not line.startswith("|"):
            continue
        # 헤더/구분자 제외
        if re.match(r"\|[-| ]+\|", line):
            continue
        cells = [c.strip() for c in line.split("|") if c.strip()]
        if not cells:
            continue
        first_cell = cells[0]
        # "페르소나" 헤더 제외
        if first_cell == "페르소나":
            continue
        korean = _extract_korean_name(first_cell)
        if korean:
            registered.append(korean)

    return registered


def check_persona_validation(parsed: dict, persona_list_path: str) -> dict:
    """참여 페르소나 vs 등록 목록 교차 확인

    returns: {"status": "PASS"|"WARN"|"FAIL", "registered": list, "unregistered": list, "loki_present": bool}
    """
    registered_pool = _parse_registered_personas(persona_list_path)
    personas: list[str] = parsed.get("personas", [])

    registered: list[str] = []
    unregistered: list[str] = []

    for persona in personas:
        korean = _extract_korean_name(persona)
        if korean in registered_pool:
            registered.append(korean)
        else:
            unregistered.append(korean)

    # 로키 참석 여부: personas 목록의 어떤 항목에 "로키"가 포함되어 있는지 확인
    loki_present = any("로키" in p for p in personas)

    if not loki_present:
        status = "FAIL"
    elif unregistered:
        status = "WARN"
    else:
        status = "PASS"

    return {
        "status": status,
        "registered": registered,
        "unregistered": unregistered,
        "loki_present": loki_present,
    }


# ---------------------------------------------------------------------------
# check_da
# ---------------------------------------------------------------------------


def check_da(parsed: dict, level: int) -> dict:
    """Devil's Advocate 수행 여부 검증

    returns: {"status": "PASS"|"WARN"|"FAIL", "da_sections_found": int,
              "three_questions": bool, "rebuttal_found": bool, "verdict_found": bool}
    """
    content: str = parsed.get("content", "")

    # DA 섹션 헤더 탐색: "Devil's Advocate" 포함 헤더 또는
    # 헤더 제목 자체가 "DA"로 끝나거나 "DA"만 있는 경우 (예: "## DA", "### Cycle 3 — DA")
    # 단, "DA 없는", "DA 없음" 처럼 뒤에 한글 텍스트가 이어지는 경우는 제외
    da_header_pattern = re.compile(
        r"^#{1,6}\s+.*Devil'?s?\s+Advocate.*$",
        re.MULTILINE | re.IGNORECASE,
    )
    # "DA"가 헤더의 마지막 단어이거나 뒤에 구분자(공백+괄호, 줄 끝)만 오는 경우
    da_standalone_pattern = re.compile(
        r"^#{1,6}\s+.*\bDA\b\s*(?:\([^)]*\))?\s*$",
        re.MULTILINE,
    )
    da_matches = da_header_pattern.findall(content)
    da_standalone_matches = [m for m in da_standalone_pattern.findall(content) if not da_header_pattern.search(m)]
    da_sections_found = len(da_matches) + len(da_standalone_matches)

    # 3대 질문 확인
    has_failure = bool(re.search(r"실패", content))
    has_regret = bool(re.search(r"후회", content))
    has_simple_alt = bool(re.search(r"단순한 대안|더 단순", content))
    three_questions = has_failure and has_regret and has_simple_alt

    rebuttal_found = bool(re.search(r"반박", content))
    verdict_found = bool(re.search(r"판정", content))

    # Lv.1-2: DA 없어도 PASS
    if level <= 2:
        status = "PASS"
    else:
        # Lv.3-4: DA 필수
        if da_sections_found == 0:
            status = "FAIL"
        elif not three_questions:
            status = "WARN"
        else:
            status = "PASS"

    return {
        "status": status,
        "da_sections_found": da_sections_found,
        "three_questions": three_questions,
        "rebuttal_found": rebuttal_found,
        "verdict_found": verdict_found,
    }


# ---------------------------------------------------------------------------
# check_three_docs
# ---------------------------------------------------------------------------


def check_three_docs(parsed: dict, task_id: str | None = None) -> dict:
    """3문서 연결 여부 검증

    returns: {"status": "PASS"|"WARN"|"FAIL", "mentioned": bool, "files_exist": bool | None}
    """
    content: str = parsed.get("content", "")

    # "3문서 반영" 또는 계획서+맥락노트+체크리스트 모두 포함 여부
    has_three_docs_phrase = bool(re.search(r"3문서 반영", content))
    has_all_keywords = (
        bool(re.search(r"계획서", content))
        and bool(re.search(r"맥락노트", content))
        and bool(re.search(r"체크리스트", content))
    )
    mentioned = has_three_docs_phrase or has_all_keywords

    # task_id 제공 시 파일 존재 여부 확인
    files_exist: bool | None = None
    if task_id is not None:
        docs_dir = Path(DEFAULT_3DOCS_BASE) / task_id
        if docs_dir.exists() and docs_dir.is_dir():
            files = list(docs_dir.iterdir())
            files_exist = len(files) > 0
        else:
            files_exist = False

    status = "PASS" if mentioned else "WARN"

    return {
        "status": status,
        "mentioned": mentioned,
        "files_exist": files_exist,
    }


# ---------------------------------------------------------------------------
# run_audit
# ---------------------------------------------------------------------------


def run_audit(meeting_file: str, level: int, task_id: str | None = None) -> dict:
    """전체 감사 실행

    returns: {"meeting_file": str, "level": int, "checks": {...4개 체크 결과...},
              "overall": "PASS"|"WARN"|"FAIL", "summary": str}
    """
    parsed = parse_meeting_file(meeting_file)

    cycle_count = check_cycle_count(parsed, level)
    persona_validation = check_persona_validation(parsed, DEFAULT_PERSONA_LIST)
    da_check = check_da(parsed, level)
    three_docs = check_three_docs(parsed, task_id=task_id)

    checks = {
        "cycle_count": cycle_count,
        "persona_validation": persona_validation,
        "da_check": da_check,
        "three_docs": three_docs,
    }

    statuses = [c["status"] for c in checks.values()]
    pass_count = statuses.count("PASS")
    warn_count = statuses.count("WARN")
    fail_count = statuses.count("FAIL")

    if fail_count > 0:
        overall = "FAIL"
    elif warn_count > 0:
        overall = "WARN"
    else:
        overall = "PASS"

    summary = f"{pass_count}/4 PASS, {warn_count} WARN, {fail_count} FAIL"

    result = {
        "meeting_file": meeting_file,
        "level": level,
        "cycle_count": cycle_count,
        "persona_validation": persona_validation,
        "da_check": da_check,
        "three_docs": three_docs,
        "overall": overall,
        "summary": summary,
    }
    return result


# ---------------------------------------------------------------------------
# CLI entrypoint
# ---------------------------------------------------------------------------

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="미팅 기록 파일을 파싱하여 SKILL.md 규칙 준수 여부를 검증합니다.")
    parser.add_argument(
        "--meeting-file",
        required=True,
        help="검증할 미팅 마크다운 파일 경로",
    )
    parser.add_argument(
        "--level",
        type=int,
        choices=[1, 2, 3, 4],
        required=True,
        help="미팅 레벨 (1-4)",
    )
    parser.add_argument(
        "--task-id",
        default=None,
        help="3문서 경로 검증을 위한 태스크 ID (선택)",
    )

    args = parser.parse_args()

    try:
        result = run_audit(args.meeting_file, args.level, task_id=args.task_id)
        print(json.dumps(result, ensure_ascii=False, indent=2))
    except FileNotFoundError as e:
        import sys

        print(json.dumps({"error": str(e)}, ensure_ascii=False), file=sys.stderr)
        sys.exit(1)
    except Exception as e:
        import sys

        print(json.dumps({"error": f"감사 실행 중 오류: {e}"}, ensure_ascii=False), file=sys.stderr)
        sys.exit(1)
