#!/usr/bin/env python3
"""Batch-regenerate ICD offline-vs-online compare artifacts for existing runs."""

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


def parse_args():
    parser = argparse.ArgumentParser(
        description=(
            "Batch rerun icd_offline_online_compare.py for existing run directories "
            "and emit version-tagged HTML/CSV artifacts."
        )
    )
    parser.add_argument("--root", required=True, help="Case root that contains log*/runs/*/run_manifest.json.")
    parser.add_argument("--case-glob", default="log*", help="Case directory glob under root. Default: log*")
    parser.add_argument(
        "--compare-script",
        help="Override compare script path. Default: scripts/icd_offline_online_compare.py",
    )
    parser.add_argument("--all-runs", action="store_true", help="Process all discovered runs instead of only the latest run of each case.")
    parser.add_argument("--overwrite", action="store_true", help="Regenerate artifacts even if the version-tagged HTML already exists.")
    parser.add_argument("--dry-run", action="store_true", help="Only print what would run.")
    return parser.parse_args()


def default_compare_script():
    return Path(__file__).resolve().with_name("icd_offline_online_compare.py")


def resolve_online_dir(online_root: Path) -> Path:
    auto_analysis = online_root / "auto_analysis"
    backup_root = auto_analysis / "backup"
    if backup_root.is_dir():
        backups = sorted((p for p in backup_root.iterdir() if p.is_dir()), key=lambda p: p.stat().st_mtime)
        if backups:
            return backups[-1]
    if auto_analysis.is_dir() and any(auto_analysis.glob("*.csv")):
        return auto_analysis
    temp_dirs = sorted((p for p in online_root.glob("temp_*") if p.is_dir()), key=lambda p: p.stat().st_mtime)
    for temp_dir in reversed(temp_dirs):
        if any(temp_dir.glob("*.csv")):
            return temp_dir
    raise FileNotFoundError(f"No online compare csv directory found under {online_root}")


def discover_run_manifests(root: Path, case_glob: str, all_runs: bool):
    manifests = []
    for case_dir in sorted(p for p in root.glob(case_glob) if p.is_dir()):
        case_manifests = sorted(case_dir.glob("runs/*/run_manifest.json"), key=lambda p: p.stat().st_mtime)
        if not case_manifests:
            continue
        manifests.extend(case_manifests if all_runs else [case_manifests[-1]])
    return manifests


def build_file_tag(skill_version) -> str:
    if skill_version is None:
        return ""
    version = str(skill_version).strip()
    if not version:
        return ""
    return version if version.startswith("v") else f"v{version}"


def process_manifest(manifest_path: Path, compare_script: Path, overwrite: bool, dry_run: bool):
    data = json.loads(manifest_path.read_text(encoding="utf-8"))
    run_dir = Path(data["run_dir"])
    case_name = run_dir.parent.parent.name
    version_tag = build_file_tag(data.get("skill_version"))
    output_dir = run_dir / "output" / "html_compare"
    output_dir.mkdir(parents=True, exist_ok=True)

    html_name = "icd_offline_online_compare.html"
    if version_tag:
        html_name = f"icd_offline_online_compare_{version_tag}.html"
    html_path = output_dir / html_name
    if html_path.exists() and not overwrite:
        return {"case": case_name, "run_dir": str(run_dir), "status": "skipped", "html": str(html_path)}

    offline_dir = run_dir / "tmp" / "mdlog_auto_analysis_modem"
    if not offline_dir.is_dir():
        raise FileNotFoundError(f"Missing offline dir: {offline_dir}")
    online_dir = resolve_online_dir(run_dir / "tmp" / "online_required_input")

    title = case_name if not version_tag else f"{case_name} {version_tag}"
    cmd = [
        sys.executable,
        str(compare_script),
        "--offline-dir",
        str(offline_dir),
        "--online-dir",
        str(online_dir),
        "--output-dir",
        str(output_dir),
        "--title",
        title,
    ]
    if version_tag:
        cmd.extend(["--file-tag", version_tag])

    if dry_run:
        return {"case": case_name, "run_dir": str(run_dir), "status": "dry_run", "html": str(html_path), "cmd": cmd}

    subprocess.run(cmd, check=True)
    return {"case": case_name, "run_dir": str(run_dir), "status": "generated", "html": str(html_path)}


def main():
    args = parse_args()
    root = Path(args.root).expanduser().resolve()
    compare_script = Path(args.compare_script).expanduser().resolve() if args.compare_script else default_compare_script()
    if not compare_script.is_file():
        raise FileNotFoundError(f"Compare script not found: {compare_script}")

    manifests = discover_run_manifests(root, args.case_glob, args.all_runs)
    if not manifests:
        raise FileNotFoundError(f"No run manifests found under {root} with case glob {args.case_glob}")

    failures = []
    results = []
    for manifest in manifests:
        try:
            results.append(process_manifest(manifest, compare_script, args.overwrite, args.dry_run))
        except Exception as exc:
            run_dir = str(manifest.parent)
            failures.append((run_dir, str(exc)))

    for item in results:
        print(f"[{item['status'].upper()}] {item['case']} -> {item['html']}")
        if item["status"] == "dry_run":
            print("  " + " ".join(item["cmd"]))

    if failures:
        for run_dir, error in failures:
            print(f"[FAILED] {run_dir}: {error}", file=sys.stderr)
        raise SystemExit(1)


if __name__ == "__main__":
    main()
