# OID_analysis/analysis/run_full_analysis.py
"""
Run the full OID analysis pipeline for a scene.
Checks data availability first, runs all phases that have prerequisites.

Usage:
    # Run all available scenes
    python3 src/OID_analysis/analysis/run_full_analysis.py

    # Run specific scene
    python3 src/OID_analysis/analysis/run_full_analysis.py --scene karman_re100

    # Force re-run
    python3 src/OID_analysis/analysis/run_full_analysis.py --scene steady_cloak --force
"""
from __future__ import annotations

import argparse
import json
import os
import subprocess
import sys
import time

_REPO = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", ".."))
if _REPO not in sys.path:
    sys.path.insert(0, _REPO)
_SRC = os.path.join(_REPO, "src")
if _SRC not in sys.path:
    sys.path.insert(0, _SRC)

from OID_analysis.configs import data_dir_for_scene, SCENES  # noqa: E402

ANALYSIS_DIR = os.path.dirname(os.path.abspath(__file__))


def scene_prerequisites_met(scene_key: str, verbose: bool = True) -> bool:
    """Check if all prerequisite data exists for a scene."""
    # Determine which fields are needed
    if scene_key == "steady_cloak":
        needed = {
            "q_in (empty_channel)": lambda: os.path.isfile(os.path.join(data_dir_for_scene("empty_channel"), "fields.npz")),
            "q_blk (pinball_baseline)": lambda: os.path.isfile(os.path.join(data_dir_for_scene("pinball_baseline"), "fields.npz")),
            "q_ctl (steady_cloak)": lambda: os.path.isfile(os.path.join(data_dir_for_scene("steady_cloak"), "fields.npz")),
            "forces": lambda: os.path.isfile(os.path.join(data_dir_for_scene("pinball_baseline"), "forces.npz")),
        }
    elif scene_key.startswith("karman"):
        needed = {
            "q_in (disturbance_only)": lambda: os.path.isfile(os.path.join(data_dir_for_scene("disturbance_only"), "fields.npz")),
            "q_blk (karman_blk)": lambda: os.path.isfile(os.path.join(data_dir_for_scene("karman_blk"), "fields.npz")),
            "q_ctl (karman_re100)": lambda: os.path.isfile(os.path.join(data_dir_for_scene("karman_re100"), "fields.npz")),
            "controlled": lambda: os.path.isfile(os.path.join(data_dir_for_scene("karman_re100"), "controlled.npz")),
        }
    elif scene_key.startswith("illusion"):
        needed = {
            "q_ctl": lambda: os.path.isfile(os.path.join(data_dir_for_scene(scene_key), "fields.npz")),
            "controlled": lambda: os.path.isfile(os.path.join(data_dir_for_scene(scene_key), "controlled.npz")),
        }
    else:
        needed = {}

    all_met = True
    for name, check_fn in needed.items():
        met = check_fn()
        if not met:
            if verbose:
                print(f"  MISSING: {name}")
            all_met = False
        elif verbose:
            print(f"  OK: {name}")

    return all_met


def run_phase(script_name: str, scene_key: str, force: bool = False):
    """Run one phase script for a scene."""
    script_path = os.path.join(ANALYSIS_DIR, script_name)
    if not os.path.isfile(script_path):
        print(f"  SKIP: {script_name} not found")
        return 0

    env = os.environ.copy()
    env["PYTHONPATH"] = f"{_SRC}:{env.get('PYTHONPATH', '')}"

    cmd = [sys.executable, script_path]
    if scene_key:
        cmd.extend(["--scene", scene_key])

    print(f"\n--- Running: {' '.join(cmd)} ---")
    t0 = time.time()
    result = subprocess.run(cmd, capture_output=True, text=True, env=env)
    elapsed = time.time() - t0

    # Print output
    for line in result.stdout.split("\n"):
        if line.strip():
            print(f"  {line}")
    if result.stderr.strip():
        for line in result.stderr.split("\n"):
            if line.strip() and "WARNING" in line:
                print(f"  ! {line.strip()}")

    if result.returncode != 0:
        print(f"  FAILED ({elapsed:.0f}s), code={result.returncode}")
    else:
        print(f"  OK ({elapsed:.0f}s)")

    return result.returncode


def run_full(scene_key: str, force: bool = False):
    print(f"\n{'='*70}")
    print(f"Full OID Analysis Pipeline: {scene_key}")
    print(f"{'='*70}")

    if not scene_prerequisites_met(scene_key):
        print(f"  PREREQUISITES NOT MET. Skipping.")
        return False

    # Phase order
    phases = [
        ("phase1_correction_pod.py", "Phase 1: Correction-field POD"),
        ("phase2_build_observables.py", "Phase 2: Observable construction"),
        ("phase3_force_oid.py", "Phase 3: Force-OID"),
    ]

    # Phase 4-5 (conditional)
    if scene_key.startswith("karman") or scene_key.startswith("illusion"):
        phases.append(("phase4a_signature_oid.py", "Phase 4a: Signature-OID"))
        phases.append(("phase4b_signature_pcd.py", "Phase 4b: Signature-PCD"))
    elif scene_key == "steady_cloak":
        phases.append(("phase5_steady_oid.py", "Phase 5: Steady suppression-OID"))

    # Phase 6-7
    phases.append(("phase6_comparison.py", "Phase 6: Comparison"))
    phases.append(("phase7_whitebox.py", "Phase 7: White-box chain"))

    errors = 0
    for script, desc in phases:
        print(f"\n>>> {desc}")
        errors += run_phase(script, scene_key, force)

    print(f"\n{'='*70}")
    print(f"Pipeline complete for {scene_key}. Errors: {errors}")
    print(f"{'='*70}")
    return errors == 0


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--scene", type=str, default=None,
                    help="Scene key or 'all'")
    ap.add_argument("--force", action="store_true",
                    help="Force re-run")
    ap.add_argument("--check", action="store_true",
                    help="Only check prerequisites")
    ap.add_argument("--list", action="store_true",
                    help="List available scenes")
    args = ap.parse_args()

    all_scenes = ["steady_cloak", "karman_re100",
                  "illusion_0.75L", "illusion_1.0L", "illusion_1.5L"]

    if args.list:
        for sn in all_scenes:
            met = scene_prerequisites_met(sn, verbose=False)
            status = "READY" if met else "MISSING DATA"
            print(f"  {sn:30s} {status}")
        return

    if args.check:
        for sn in all_scenes:
            print(f"\n--- {sn} ---")
            scene_prerequisites_met(sn)
        return

    targets = all_scenes if (args.scene == "all" or args.scene is None) else [args.scene]
    for sn in targets:
        if sn not in all_scenes:
            print(f"Unknown scene: {sn}")
            continue
        run_full(sn, args.force)

    print("\nAll pipelines complete.")


if __name__ == "__main__":
    main()
