#!/usr/bin/env python3
"""
check-dev3.py - Dev3 Team (봇D) 작업 환경 헬스체크 및 최근 작업 이력 진단 도구

Usage:
    python3 scripts/check-dev3.py          # 전체 점검
    python3 scripts/check-dev3.py --quick  # 환경만 점검
    python3 scripts/check-dev3.py --json   # JSON 출력
"""

import argparse
import json
import os
import subprocess
import sys
from datetime import datetime
from pathlib import Path

# ── 경로 상수 ─────────────────────────────────────────────────────────────────
WORKSPACE = Path(os.environ.get("WORKSPACE_ROOT", str(Path(__file__).resolve().parent.parent)))
DEV3_DIR = WORKSPACE / "teams/dev3"
DEV3_LOGS_DIR = DEV3_DIR / "logs"
EVENTS_DIR = WORKSPACE / "memory/events"
REPORTS_DIR = WORKSPACE / "memory/reports"
AGENTS_MD = Path("/home/jay/.openclaw/workspace/AGENTS.md")
EVENT_QUEUE_JSON = EVENTS_DIR / "event-queue.json"

MAX_HISTORY = 10  # 최근 작업 이력 표시 개수


# ── openclaw CLI 확인 ──────────────────────────────────────────────────────────

def check_openclaw() -> dict:
    """openclaw CLI 동작 가능 여부 확인."""
    result = {
        "available": False,
        "path": None,
        "version": None,
        "error": None,
    }

    # which openclaw
    try:
        proc = subprocess.run(
            ["which", "openclaw"],
            capture_output=True,
            text=True,
            timeout=5,
        )
        if proc.returncode == 0 and proc.stdout.strip():
            result["path"] = proc.stdout.strip()
            result["available"] = True
    except (subprocess.TimeoutExpired, FileNotFoundError, OSError) as e:
        result["error"] = str(e)
        return result

    # openclaw --version
    try:
        proc = subprocess.run(
            ["openclaw", "--version"],
            capture_output=True,
            text=True,
            timeout=5,
        )
        if proc.returncode == 0:
            ver = (proc.stdout.strip() or proc.stderr.strip())
            result["version"] = ver if ver else None
        else:
            ver = (proc.stderr.strip() or proc.stdout.strip())
            result["version"] = ver if ver else None
    except (subprocess.TimeoutExpired, FileNotFoundError, OSError) as e:
        result["error"] = str(e)

    return result


# ── 경로 존재 여부 확인 ────────────────────────────────────────────────────────

def check_paths() -> list[dict]:
    """필수 경로 존재 여부를 점검하여 결과 목록 반환."""
    checks = []

    # dev3 작업 디렉토리
    checks.append({
        "label": "dev3 작업 디렉토리",
        "path": str(DEV3_DIR),
        "exists": DEV3_DIR.is_dir(),
        "suggestion": None,
    })

    # dev3 로그 디렉토리 (없으면 생성 제안)
    logs_exists = DEV3_LOGS_DIR.is_dir()
    checks.append({
        "label": "dev3 로그 디렉토리",
        "path": str(DEV3_LOGS_DIR),
        "exists": logs_exists,
        "suggestion": f"mkdir -p {DEV3_LOGS_DIR}" if not logs_exists else None,
    })

    # events 디렉토리
    checks.append({
        "label": "events 디렉토리",
        "path": str(EVENTS_DIR),
        "exists": EVENTS_DIR.is_dir(),
        "suggestion": None,
    })

    # reports 디렉토리
    checks.append({
        "label": "reports 디렉토리",
        "path": str(REPORTS_DIR),
        "exists": REPORTS_DIR.is_dir(),
        "suggestion": None,
    })

    # AGENTS.md 파일
    checks.append({
        "label": "AGENTS.md",
        "path": str(AGENTS_MD),
        "exists": AGENTS_MD.is_file(),
        "suggestion": None,
    })

    return checks


# ── 이벤트 파일 스캔 ───────────────────────────────────────────────────────────

def scan_event_files() -> list[dict]:
    """
    events/ 디렉토리에서 .done / .failed 파일을 수집하고
    event-queue.json의 dev3-team 이벤트와 교차 참조하여 이력 반환.
    """
    records = []

    if not EVENTS_DIR.is_dir():
        return records

    # event-queue.json 에서 dev3 이벤트 로드
    dev3_events: dict[str, dict] = {}  # task_id -> event
    if EVENT_QUEUE_JSON.is_file():
        try:
            with open(EVENT_QUEUE_JSON, encoding="utf-8") as f:
                queue_data = json.load(f)
            processed = queue_data.get("processed", [])
            for evt in processed:
                if "dev3" in str(evt.get("team", "")):
                    tid = evt.get("task_id", "")
                    # 동일 task_id가 여러 개일 경우 가장 최신 것 유지
                    if tid not in dev3_events or (
                        evt.get("processed_at", "") > dev3_events[tid].get("processed_at", "")
                    ):
                        dev3_events[tid] = evt
        except (OSError, json.JSONDecodeError):
            pass

    # events/ 디렉토리 파일 스캔
    try:
        entries = list(EVENTS_DIR.iterdir())
    except OSError:
        return records

    for entry in entries:
        if not entry.is_file():
            continue

        name = entry.name
        parts = name.split(".")
        # 패턴: task-NNN.M.done.clear  또는  task-NNN.M.failed.clear
        # 최소 3개 파트 필요: [task-NNN, M, done|failed, ...]
        if len(parts) < 3:
            continue
        if parts[2] not in ("done", "failed"):
            continue

        task_id = f"{parts[0]}.{parts[1]}"   # e.g. "task-148.1"
        status = parts[2]                      # "done" | "failed"

        try:
            mtime = entry.stat().st_mtime
            mtime_str = datetime.fromtimestamp(mtime).strftime("%Y-%m-%d %H:%M")
        except OSError:
            mtime = 0
            mtime_str = "unknown"

        # 보고서 존재 여부 확인 (event-queue 우선, fallback: reports/ 직접)
        evt = dev3_events.get(task_id)
        if evt:
            report_path = evt.get("report", "")
            has_report = bool(report_path) and Path(report_path).is_file()
        else:
            # dev3 events에 없으면 기록하지 않음 (다른 팀 파일)
            continue

        records.append({
            "task_id": task_id,
            "status": status,
            "mtime": mtime,
            "mtime_str": mtime_str,
            "has_report": has_report,
        })

    # mtime 내림차순 정렬 후 중복 task_id 제거 (가장 최신 유지)
    records.sort(key=lambda r: r["mtime"], reverse=True)
    seen_ids: set[str] = set()
    deduped = []
    for r in records:
        if r["task_id"] not in seen_ids:
            seen_ids.add(r["task_id"])
            deduped.append(r)

    return deduped


def scan_report_files() -> list[dict]:
    """
    reports/ 디렉토리에서 dev3 관련 보고서를 검색하여 반환.
    event-queue.json 의 dev3-team 이벤트에서 참조된 파일 기준.
    """
    records = []

    if not REPORTS_DIR.is_dir():
        return records

    # dev3 이벤트 task_id 목록 수집
    dev3_task_ids: set[str] = set()
    if EVENT_QUEUE_JSON.is_file():
        try:
            with open(EVENT_QUEUE_JSON, encoding="utf-8") as f:
                queue_data = json.load(f)
            for evt in queue_data.get("processed", []):
                if "dev3" in str(evt.get("team", "")):
                    dev3_task_ids.add(evt.get("task_id", ""))
        except (OSError, json.JSONDecodeError):
            pass

    try:
        entries = list(REPORTS_DIR.iterdir())
    except OSError:
        return records

    for entry in entries:
        if not entry.is_file() or not entry.suffix == ".md":
            continue
        # 파일명이 dev3 task_id 와 매칭되는지 확인
        # 예: task-148.1.md  →  task_id = "task-148.1"
        stem = entry.stem  # e.g. "task-148.1"
        if stem not in dev3_task_ids:
            continue

        try:
            mtime = entry.stat().st_mtime
            mtime_str = datetime.fromtimestamp(mtime).strftime("%Y-%m-%d %H:%M")
        except OSError:
            mtime = 0
            mtime_str = "unknown"

        records.append({
            "task_id": stem,
            "path": str(entry),
            "mtime": mtime,
            "mtime_str": mtime_str,
        })

    records.sort(key=lambda r: r["mtime"], reverse=True)
    return records


def scan_log_files() -> list[dict]:
    """dev3 logs/ 디렉토리에서 최근 로그 파일 목록 반환."""
    records = []

    if not DEV3_LOGS_DIR.is_dir():
        return records

    try:
        entries = list(DEV3_LOGS_DIR.iterdir())
    except OSError:
        return records

    for entry in entries:
        if not entry.is_file():
            continue
        try:
            mtime = entry.stat().st_mtime
            mtime_str = datetime.fromtimestamp(mtime).strftime("%Y-%m-%d %H:%M")
        except OSError:
            mtime = 0
            mtime_str = "unknown"

        records.append({
            "name": entry.name,
            "path": str(entry),
            "mtime": mtime,
            "mtime_str": mtime_str,
        })

    records.sort(key=lambda r: r["mtime"], reverse=True)
    return records


# ── 작업 이력 통합 ─────────────────────────────────────────────────────────────

def build_history(event_records: list[dict], limit: int = MAX_HISTORY) -> list[dict]:
    """이벤트 레코드를 기반으로 최근 작업 이력 구성."""
    return event_records[:limit]


def calc_stats(history: list[dict]) -> dict:
    """성공/실패 통계 계산."""
    total = len(history)
    success = sum(1 for r in history if r["status"] == "done")
    failed = total - success
    rate = int(success / total * 100) if total > 0 else 0
    return {
        "total": total,
        "success": success,
        "failed": failed,
        "rate": rate,
    }


# ── 출력 포맷 ──────────────────────────────────────────────────────────────────

def print_env_check(openclaw_result: dict, path_checks: list[dict]) -> None:
    """환경 점검 결과 출력."""
    print("\n[환경 점검]")

    # openclaw
    if openclaw_result["available"]:
        ver_str = f" (버전: {openclaw_result['version']})" if openclaw_result["version"] else ""
        print(f"  v openclaw CLI: 사용 가능 (경로: {openclaw_result['path']}){ver_str}")
    else:
        err = f" — {openclaw_result['error']}" if openclaw_result["error"] else ""
        print(f"  x openclaw CLI: 사용 불가{err}")

    # 경로들
    for check in path_checks:
        if check["exists"]:
            print(f"  v {check['label']}: {check['path']}")
        else:
            suggestion = f" → {check['suggestion']}" if check["suggestion"] else ""
            print(f"  x {check['label']}: 없음{suggestion}")


def print_history(history: list[dict], log_files: list[dict]) -> None:
    """최근 작업 이력 출력."""
    print(f"\n[최근 작업 이력] (최근 {MAX_HISTORY}건)")

    if not history:
        print("  (기록 없음)")
    else:
        for r in history:
            status_label = "성공" if r["status"] == "done" else "실패"
            report_label = "있음" if r["has_report"] else "없음"
            print(
                f"  {r['task_id']:<14}"
                f"  {status_label}"
                f"  {r['mtime_str']}"
                f"  보고서: {report_label}"
            )

    # dev3 로그 파일 (있는 경우)
    if log_files:
        print(f"\n[dev3 로그 파일] (최근 {min(5, len(log_files))}건)")
        for lf in log_files[:5]:
            print(f"  {lf['name']:<30}  {lf['mtime_str']}")


def print_stats(stats: dict) -> None:
    """통계 출력."""
    print(
        f"\n[통계]\n"
        f"  총 작업: {stats['total']}건"
        f" | 성공: {stats['success']}건"
        f" | 실패: {stats['failed']}건"
        f" | 성공률: {stats['rate']}%"
    )


def print_full_report(
    openclaw_result: dict,
    path_checks: list[dict],
    history: list[dict],
    log_files: list[dict],
    stats: dict,
) -> None:
    """전체 헬스체크 보고서 출력."""
    print("=== Dev3 Team Health Check ===")
    print_env_check(openclaw_result, path_checks)
    print_history(history, log_files)
    print_stats(stats)
    print()


def print_quick_report(
    openclaw_result: dict,
    path_checks: list[dict],
) -> None:
    """환경 점검만 출력 (--quick 모드)."""
    print("=== Dev3 Team Health Check (Quick) ===")
    print_env_check(openclaw_result, path_checks)
    print()


def print_json_report(
    openclaw_result: dict,
    path_checks: list[dict],
    history: list[dict],
    log_files: list[dict],
    stats: dict,
) -> None:
    """JSON 형식으로 전체 결과 출력 (--json 모드)."""
    output = {
        "generated_at": datetime.now().isoformat(),
        "openclaw": openclaw_result,
        "paths": path_checks,
        "history": history,
        "logs": log_files[:5],
        "stats": stats,
    }
    print(json.dumps(output, ensure_ascii=False, indent=2))


# ── CLI 진입점 ─────────────────────────────────────────────────────────────────

def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="Dev3 Team 작업 환경 헬스체크 및 최근 작업 이력 진단",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog=(
            "Examples:\n"
            "  python3 scripts/check-dev3.py          # 전체 점검\n"
            "  python3 scripts/check-dev3.py --quick  # 환경만 점검\n"
            "  python3 scripts/check-dev3.py --json   # JSON 출력\n"
        ),
    )
    parser.add_argument(
        "--quick",
        action="store_true",
        default=False,
        help="환경 점검만 수행 (작업 이력 제외)",
    )
    parser.add_argument(
        "--json",
        action="store_true",
        default=False,
        help="결과를 JSON 형식으로 출력",
    )
    return parser.parse_args()


def main() -> None:
    args = parse_args()

    # 1. openclaw CLI 확인
    openclaw_result = check_openclaw()

    # 2. 경로 존재 여부 확인
    path_checks = check_paths()

    # --quick: 환경 점검만
    if args.quick and not args.json:
        print_quick_report(openclaw_result, path_checks)
        sys.exit(0)

    # 3. 최근 작업 이력 수집
    event_records = scan_event_files()
    log_files = scan_log_files()
    history = build_history(event_records, limit=MAX_HISTORY)
    stats = calc_stats(history)

    # 4. 출력
    if args.json:
        print_json_report(openclaw_result, path_checks, history, log_files, stats)
    else:
        print_full_report(openclaw_result, path_checks, history, log_files, stats)


if __name__ == "__main__":
    main()
