#!/usr/bin/env python3
"""
sync-check.py — 3개 파일 간 모델 정보 불일치 자동 감지 스크립트

체크 대상:
  1. /home/jay/.cokacdir/bot_settings.json       — Source of Truth (읽기 전용)
  2. /home/jay/workspace/memory/organization-structure.json — 조직도
  3. /home/jay/.claude/projects/-home-jay--cokacdir-workspace-autoset/memory/MEMORY.md — 아누 메모리 (읽기 전용)

Usage:
    python3 sync-check.py           # 체크만 (읽기 전용)
    python3 sync-check.py --fix     # 불일치 자동 수정 (org_json + engine_summary만, MEMORY.md 수정 안 함)
"""

import argparse
import json
import re
import sys
from pathlib import Path

# ──────────────────────────────────────────────
# 파일 경로 (하드코딩)
# ──────────────────────────────────────────────
BOT_SETTINGS_PATH = "/home/jay/.cokacdir/bot_settings.json"
ORG_JSON_PATH = "/home/jay/workspace/memory/organization-structure.json"
MEMORY_MD_PATH = "/home/jay/.claude/projects/-home-jay--cokacdir-workspace-autoset/memory/MEMORY.md"

# ──────────────────────────────────────────────
# 봇 키 → 인물 매핑
# ──────────────────────────────────────────────
BOT_KEY_TO_PERSON = {
    "c38fb9955616e24d": "hermes",
    "f3e244a7f4f0d036": "odin",
    "a5dddf38a8c57168": "ra",
    "c119085addb0f8b7": "anu",
}

# 인물 → 한글 표시명
PERSON_LABEL = {
    "anu": "아누",
    "hermes": "헤르메스",
    "odin": "오딘",
    "ra": "라",
}


# 모델명 단축 표시
def short_model(model: str) -> str:
    if "opus" in model:
        return "opus"
    if "sonnet" in model:
        return "sonnet"
    if "haiku" in model:
        return "haiku"
    return model


# ──────────────────────────────────────────────
# 로딩
# ──────────────────────────────────────────────
def load_bot_settings() -> dict:
    with open(BOT_SETTINGS_PATH, encoding="utf-8") as f:
        return json.load(f)


def load_org_json() -> dict:
    with open(ORG_JSON_PATH, encoding="utf-8") as f:
        return json.load(f)


def load_memory_md() -> str:
    with open(MEMORY_MD_PATH, encoding="utf-8") as f:
        return f.read()


# ──────────────────────────────────────────────
# bot_settings에서 인물별 실제 모델 추출
# ──────────────────────────────────────────────
def get_bot_models(bot_settings: dict) -> dict[str, str]:
    """반환: {"anu": "claude-opus-4-6", "hermes": ..., ...}"""
    result = {}
    for bot_key, person in BOT_KEY_TO_PERSON.items():
        entry = bot_settings.get(bot_key, {})
        models = entry.get("models", {})
        if models:
            # 첫 번째 값 사용
            model = next(iter(models.values()))
        else:
            model = None
        result[person] = model
    return result


# ──────────────────────────────────────────────
# organization-structure.json에서 모델 추출 + 경로 반환
# ──────────────────────────────────────────────
def get_org_person_model(org: dict, person: str) -> tuple[str | None, list]:
    """
    반환: (모델명, json_path_list)
    json_path_list: org를 따라가는 키 경로 (수정 시 사용)
    """
    teams = org.get("structure", {}).get("columns", {}).get("teams", [])
    dev_office = None
    for team in teams:
        if team.get("team_id") == "development-office":
            dev_office = team
            break

    if dev_office is None:
        return None, []

    if person == "anu":
        office_lead = dev_office.get("office_lead", {})
        model = office_lead.get("model")
        return model, ["structure", "columns", "teams", "__dev_office__", "office_lead", "model"]

    sub_team_map = {
        "hermes": "dev1-team",
        "odin": "dev2-team",
        "ra": "dev3-team",
    }
    sub_team_id = sub_team_map.get(person)
    if not sub_team_id:
        return None, []

    for sub_team in dev_office.get("sub_teams", []):
        if sub_team.get("sub_team_id") == sub_team_id:
            lead = sub_team.get("lead", {})
            model = lead.get("model")
            return model, ["structure", "columns", "teams", "__dev_office__", "sub_teams", sub_team_id, "lead", "model"]

    return None, []


# ──────────────────────────────────────────────
# organization-structure.json에서 전체 멤버 모델 카운트
# ──────────────────────────────────────────────
def collect_all_members(org: dict) -> list[dict]:
    """조직도에서 모든 멤버(lead 포함) 리스트 반환"""
    members = []

    columns = org.get("structure", {}).get("columns", {})
    rows = org.get("structure", {}).get("rows", {})

    # 수직 조직 (columns.teams)
    for team in columns.get("teams", []):
        # office_lead
        office_lead = team.get("office_lead")
        if office_lead:
            members.append(office_lead)

        # 팀장 (lead)
        lead = team.get("lead")
        if lead:
            members.append(lead)

        # sub_teams
        for sub_team in team.get("sub_teams", []):
            sub_lead = sub_team.get("lead")
            if sub_lead:
                members.append(sub_lead)
            for m in sub_team.get("members", []):
                members.append(m)

        # 일반 members
        for m in team.get("members", []):
            members.append(m)

    # 횡단 조직 (rows.centers)
    for center in rows.get("centers", []):
        lead = center.get("lead")
        if lead:
            members.append(lead)
        for m in center.get("members", []):
            members.append(m)

    return members


def count_models_from_members(members: list[dict]) -> dict[str, dict]:
    """
    실제 멤버 목록을 순회하여 모델별 카운트와 멤버 이름 목록 계산.
    반환: {"claude-opus-4-6": {"count": N, "members": [...]}, ...}
    """
    result: dict[str, dict] = {}
    for m in members:
        model = m.get("model")
        if not model:
            continue
        name = m.get("name", m.get("id", "?"))
        # 한글 이름 추출 (예: "아누 (Anu)" → "아누")
        korean_name = name.split("(")[0].strip() if "(" in name else name.strip()
        if model not in result:
            result[model] = {"count": 0, "members": []}
        result[model]["count"] += 1
        result[model]["members"].append(korean_name)
    return result


# ──────────────────────────────────────────────
# MEMORY.md에서 인물별 모델 파싱
# ──────────────────────────────────────────────

# MEMORY.md 모델 표기 → 실제 모델명 매핑
MEMORY_MODEL_MAP = {
    "opus": "claude-opus-4-6",
    "sonnet": "claude-sonnet-4-6",
    "haiku": "claude-haiku-4-5",
}


def parse_memory_model(memory_md: str, person: str) -> str | None:
    """
    MEMORY.md에서 인물 라인을 찾아 Opus/Sonnet/Haiku 추출.
    역할/모델이 "—" (em dash) 다음에 오는 라인을 우선 매칭.
    반환: "claude-opus-4-6" / "claude-sonnet-4-6" / "claude-haiku-4-5" / None
    """
    # 첫 번째 시도: "인물명 — 모델명" 패턴 (직접 역할 할당)
    patterns_primary = {
        "anu": r"아누[^—\n]*—[^\n]*(Opus|Sonnet|Haiku)",
        "hermes": r"헤르메스[^—\n]*—[^\n]*(Opus|Sonnet|Haiku)",
        "odin": r"오딘[^—\n]*—[^\n]*(Opus|Sonnet|Haiku)",
        "ra": r"라[^—\n]*—[^\n]*(Opus|Sonnet|Haiku)",
    }
    pat = patterns_primary.get(person)
    if pat:
        m = re.search(pat, memory_md, re.IGNORECASE)
        if m:
            keyword = m.group(1).lower()
            return MEMORY_MODEL_MAP.get(keyword)

    # 두 번째 시도: 폴백 (역할 할당 명시가 없으면 먼저 매치한 라인 사용)
    patterns_fallback = {
        "anu": r"아누[^\n]*?(Opus|Sonnet|Haiku)",
        "hermes": r"헤르메스[^\n]*?(Opus|Sonnet|Haiku)",
        "odin": r"오딘[^\n]*?(Opus|Sonnet|Haiku)",
        "ra": r"라[^\n]*?(Opus|Sonnet|Haiku)",
    }
    pat = patterns_fallback.get(person)
    if pat:
        m = re.search(pat, memory_md, re.IGNORECASE)
        if m:
            keyword = m.group(1).lower()
            return MEMORY_MODEL_MAP.get(keyword)

    return None


def parse_memory_model_keyword(memory_md: str, person: str) -> str | None:
    """MEMORY.md에서 파싱한 원본 키워드 (Opus/Sonnet/Haiku) 반환"""
    # 첫 번째 시도: "인물명 — 모델명" 패턴 (직접 역할 할당)
    patterns_primary = {
        "anu": r"아누[^—\n]*—[^\n]*(Opus|Sonnet|Haiku)",
        "hermes": r"헤르메스[^—\n]*—[^\n]*(Opus|Sonnet|Haiku)",
        "odin": r"오딘[^—\n]*—[^\n]*(Opus|Sonnet|Haiku)",
        "ra": r"라[^—\n]*—[^\n]*(Opus|Sonnet|Haiku)",
    }
    pat = patterns_primary.get(person)
    if pat:
        m = re.search(pat, memory_md, re.IGNORECASE)
        if m:
            return m.group(1).capitalize()

    # 두 번째 시도: 폴백
    patterns_fallback = {
        "anu": r"아누[^\n]*?(Opus|Sonnet|Haiku)",
        "hermes": r"헤르메스[^\n]*?(Opus|Sonnet|Haiku)",
        "odin": r"오딘[^\n]*?(Opus|Sonnet|Haiku)",
        "ra": r"라[^\n]*?(Opus|Sonnet|Haiku)",
    }
    pat = patterns_fallback.get(person)
    if pat:
        m = re.search(pat, memory_md, re.IGNORECASE)
        if m:
            return m.group(1).capitalize()

    return None


# ──────────────────────────────────────────────
# 인물 모델 불일치 체크
# ──────────────────────────────────────────────
def check_person_models(
    bot_models: dict,
    org: dict,
    memory_md: str,
) -> tuple[list[dict], bool]:
    """
    각 인물(anu, hermes, odin, ra)에 대해 3-way 비교.
    반환: (결과_리스트, 불일치_있음)
    결과 항목: {
        "person": str,
        "label": str,
        "bot": str,
        "org": str,
        "memory": str | None,
        "memory_keyword": str | None,
        "mismatch": bool,
        "fixes": list[str],   # 수정 필요 설명
    }
    """
    results = []
    has_mismatch = False

    persons = ["anu", "hermes", "odin", "ra"]
    for person in persons:
        bot_model = bot_models.get(person)
        org_model, _ = get_org_person_model(org, person)
        mem_model = parse_memory_model(memory_md, person)
        mem_keyword = parse_memory_model_keyword(memory_md, person)

        fixes = []
        mismatch = False

        # bot_settings ↔ org_json 비교
        if bot_model and org_model and bot_model != org_model:
            mismatch = True
            fixes.append(f"org_json 수정 필요: {org_model} → {bot_model}")

        # bot_settings ↔ memory 비교 (memory가 파싱됐을 때만)
        if bot_model and mem_model and bot_model != mem_model:
            mismatch = True
            # MEMORY.md는 수정 안 하므로 경고 형태
            expected_keyword = None
            for kw, mv in MEMORY_MODEL_MAP.items():
                if mv == bot_model:
                    expected_keyword = kw.capitalize()
                    break
            fixes.append(f"MEMORY.md 수정 필요: {mem_keyword} → {expected_keyword}")

        if mismatch:
            has_mismatch = True

        results.append(
            {
                "person": person,
                "label": PERSON_LABEL[person],
                "bot": short_model(bot_model) if bot_model else "N/A",
                "org": short_model(org_model) if org_model else "N/A",
                "memory": mem_keyword if mem_keyword else "N/A",
                "bot_full": bot_model,
                "org_full": org_model,
                "mismatch": mismatch,
                "fixes": fixes,
            }
        )

    return results, has_mismatch


# ──────────────────────────────────────────────
# engine_summary 검증
# ──────────────────────────────────────────────
def check_engine_summary(org: dict) -> tuple[list[dict], bool]:
    """
    engine_summary의 count/members ↔ 실제 멤버 카운트 비교.
    반환: (결과_리스트, 불일치_있음)
    """
    engine_summary = org.get("engine_summary", {})
    members = collect_all_members(org)
    actual_counts = count_models_from_members(members)

    results = []
    has_mismatch = False

    for model_key, summary_val in engine_summary.items():
        if model_key == "total":
            continue
        if not isinstance(summary_val, dict):
            continue

        summary_count = summary_val.get("count", 0)
        actual_info = actual_counts.get(model_key, {"count": 0, "members": []})
        actual_count = actual_info["count"]

        mismatch = summary_count != actual_count

        if mismatch:
            has_mismatch = True

        results.append(
            {
                "model": model_key,
                "summary_count": summary_count,
                "actual_count": actual_count,
                "actual_members": actual_info["members"],
                "mismatch": mismatch,
            }
        )

    # actual_counts에만 있는 모델 (engine_summary에 누락된 경우)
    for model_key, actual_info in actual_counts.items():
        if model_key not in engine_summary:
            has_mismatch = True
            results.append(
                {
                    "model": model_key,
                    "summary_count": 0,
                    "actual_count": actual_info["count"],
                    "actual_members": actual_info["members"],
                    "mismatch": True,
                }
            )

    return results, has_mismatch


# ──────────────────────────────────────────────
# 출력
# ──────────────────────────────────────────────
def print_results(person_results: list[dict], engine_results: list[dict]):
    print("=== 설정 동기화 체크 ===\n")

    for r in person_results:
        label = r["label"]
        bot_s = r["bot"]
        org_s = r["org"]
        mem_s = r["memory"]

        if not r["mismatch"]:
            print(f"[OK] {label}: bot_settings={bot_s}, org_json={org_s}, memory={mem_s} ✓")
        else:
            print(f"[MISMATCH] {label}: bot_settings={bot_s}, org_json={org_s}, memory={mem_s}")
            for fix in r["fixes"]:
                print(f"  → {fix}")

    print("\n--- engine_summary 검증 ---")
    for r in engine_results:
        model = r["model"]
        s_count = r["summary_count"]
        a_count = r["actual_count"]
        if not r["mismatch"]:
            print(f"[OK] {model}: summary={s_count}, actual={a_count} ✓")
        else:
            print(f"[MISMATCH] {model}: summary={s_count}, actual={a_count}")
            print(f"  → engine_summary 수정 필요")

    # 총 불일치 수 집계
    total_mismatch = sum(1 for r in person_results if r["mismatch"])
    total_mismatch += sum(1 for r in engine_results if r["mismatch"])
    print(f"\n=== 결과: {total_mismatch}개 불일치 발견 ===")
    return total_mismatch


# ──────────────────────────────────────────────
# --fix: organization-structure.json 수정
# ──────────────────────────────────────────────
def fix_org_json(org: dict, person_results: list[dict], engine_results: list[dict]) -> dict:
    """
    org_json 내 lead.model 수정 + engine_summary 재계산.
    bot_settings.json, MEMORY.md 는 절대 수정하지 않음.
    """
    # 1) 인물별 lead.model 수정
    teams = org["structure"]["columns"]["teams"]
    dev_office = None
    for team in teams:
        if team.get("team_id") == "development-office":
            dev_office = team
            break

    if dev_office is None:
        print("[ERROR] development-office 팀을 찾을 수 없습니다.")
        return org

    for r in person_results:
        if not r["mismatch"]:
            continue
        person = r["person"]
        bot_full = r["bot_full"]
        if bot_full is None:
            continue

        # org_json 수정이 필요한지 확인 (MEMORY.md 수정 관련 fix는 건너뜀)
        needs_org_fix = any("org_json 수정 필요" in fix for fix in r["fixes"])
        if not needs_org_fix:
            continue

        if person == "anu":
            old = dev_office["office_lead"]["model"]
            dev_office["office_lead"]["model"] = bot_full
            print(f"  [FIX] 아누 office_lead.model: {old} → {bot_full}")
        else:
            sub_team_map = {"hermes": "dev1-team", "odin": "dev2-team", "ra": "dev3-team"}
            sub_team_id = sub_team_map.get(person)
            for sub_team in dev_office.get("sub_teams", []):
                if sub_team.get("sub_team_id") == sub_team_id:
                    old = sub_team["lead"]["model"]
                    sub_team["lead"]["model"] = bot_full
                    print(f"  [FIX] {PERSON_LABEL[person]} lead.model: {old} → {bot_full}")
                    break

    # 2) engine_summary 재계산
    needs_engine_fix = any(r["mismatch"] for r in engine_results)
    if needs_engine_fix:
        # 수정 후 멤버 목록 재수집
        members = collect_all_members(org)
        actual_counts = count_models_from_members(members)

        # 현재 engine_summary 보존하되 count/members 업데이트
        current_summary = org.get("engine_summary", {})
        new_summary = {}

        # 기존 모델들 업데이트
        for model_key, val in current_summary.items():
            if model_key == "total":
                continue
            if not isinstance(val, dict):
                continue
            actual = actual_counts.get(model_key, {"count": 0, "members": []})
            new_summary[model_key] = {
                "count": actual["count"],
                "members": actual["members"],
                "role": val.get("role", ""),
            }

        # actual_counts에만 있는 모델 추가
        for model_key, actual in actual_counts.items():
            if model_key not in new_summary:
                new_summary[model_key] = {
                    "count": actual["count"],
                    "members": actual["members"],
                    "role": "",
                }

        # total 재계산
        total = sum(v["count"] for v in new_summary.values())
        new_summary["total"] = total

        print(f"  [FIX] engine_summary 재계산 완료 (total={total})")
        org["engine_summary"] = new_summary

    return org


def save_org_json(org: dict):
    with open(ORG_JSON_PATH, "w", encoding="utf-8") as f:
        json.dump(org, f, ensure_ascii=False, indent=2)
    print(f"  [SAVED] {ORG_JSON_PATH}")


# ──────────────────────────────────────────────
# 메인
# ──────────────────────────────────────────────
def main():
    parser = argparse.ArgumentParser(description="bot_settings / org_json / MEMORY.md 모델 정보 동기화 체크")
    parser.add_argument(
        "--fix",
        action="store_true",
        help="불일치 자동 수정 (organization-structure.json + engine_summary만 수정. MEMORY.md는 경고만)",
    )
    args = parser.parse_args()

    # 파일 로딩
    bot_settings = load_bot_settings()
    org = load_org_json()
    memory_md = load_memory_md()

    # 모델 추출
    bot_models = get_bot_models(bot_settings)

    # 체크
    person_results, person_mismatch = check_person_models(bot_models, org, memory_md)
    engine_results, engine_mismatch = check_engine_summary(org)

    # 출력
    total_mismatch = print_results(person_results, engine_results)

    # --fix 처리
    if args.fix:
        if total_mismatch == 0:
            print("\n[INFO] 불일치 없음. 수정 불필요.")
            return

        print("\n--- 자동 수정 시작 ---")

        # MEMORY.md 경고 출력
        memory_fixes = []
        for r in person_results:
            for fix in r["fixes"]:
                if "MEMORY.md 수정 필요" in fix:
                    memory_fixes.append(f"  [WARNING] {r['label']}: {fix}")
        if memory_fixes:
            print("[WARNING] MEMORY.md는 자동 수정되지 않습니다. 수동으로 확인하세요:")
            for w in memory_fixes:
                print(w)

        # org_json 수정
        org = fix_org_json(org, person_results, engine_results)
        save_org_json(org)
        print("--- 자동 수정 완료 ---")
    else:
        # 체크 전용: MEMORY.md 수동 수정 필요 항목 안내
        memory_fixes = []
        for r in person_results:
            for fix in r["fixes"]:
                if "MEMORY.md 수정 필요" in fix:
                    memory_fixes.append(f"  {r['label']}: {fix}")
        if memory_fixes:
            print("\n[NOTE] MEMORY.md는 자동 수정 대상이 아닙니다. 수동 확인 필요:")
            for w in memory_fixes:
                print(w)


if __name__ == "__main__":
    main()
