"""CLI to run ReaScript refinement on a .rpp file.

Usage: python scripts/run_in_reaper.py <rpp_path> [--output <wav_path>] [--timeout <seconds>]
       [--plugins-config <song_json_path>] [--action <action1 action2 ...>]
"""

from __future__ import annotations

import argparse
import json
import sys
import time
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parents[1]))

from src.reaper_scripting import ReaScriptGenerator
from src.reaper_scripting.commands import (
    ReaScriptCommand,
    ReaScriptResult,
    read_result,
    write_command,
)
from src.reaper_builder import get_builtin_plugins
from src.core.schema import (
    SongDefinition,
    SongMeta,
    TrackDef,
    PluginDef,
)


def _load_song_config(path: Path) -> SongDefinition:
    """Reconstruct a SongDefinition from a JSON file (produced by SongDefinition.to_json)."""
    data: dict = json.loads(path.read_text(encoding="utf-8"))

    meta_data: dict = data["meta"]
    meta = SongMeta(
        bpm=float(meta_data["bpm"]),
        key=str(meta_data["key"]),
        title=str(meta_data.get("title", "")),
        ppq=int(meta_data.get("ppq", 960)),
        time_sig_num=int(meta_data.get("time_sig_num", 4)),
        time_sig_den=int(meta_data.get("time_sig_den", 4)),
        calibrate=bool(meta_data.get("calibrate", True)),
    )

    tracks: list[TrackDef] = []
    for t in data.get("tracks", []):
        plugins: list[PluginDef] = []
        for p in t.get("plugins", []):
            params: dict[int, float] = {}
            for k, v in p.get("params", {}).items():
                params[int(k)] = float(v)
            plugins.append(PluginDef(
                name=str(p["name"]),
                path=str(p.get("path", "")),
                index=int(p.get("index", 0)),
                params=params,
                preset_data=p.get("preset_data"),
                role=str(p.get("role", "")),
                builtin=bool(p.get("builtin", False)),
            ))
        tracks.append(TrackDef(
            name=str(t["name"]),
            volume=float(t.get("volume", 0.85)),
            pan=float(t.get("pan", 0.0)),
            color=int(t.get("color", 0)),
            plugins=plugins,
            send_reverb=float(t.get("send_reverb", 0.0)),
            send_delay=float(t.get("send_delay", 0.0)),
            send_level={int(k): float(v) for k, v in t.get("send_level", {}).items()},
        ))

    return SongDefinition(meta=meta, tracks=tracks)


def _normalize_action(action_arg: str | None) -> list[str]:
    """Parse space-separated action names into a list. Defaults to ['calibrate']."""
    if not action_arg:
        return ["calibrate"]
    return [a.strip() for a in action_arg.split() if a.strip()]


def main() -> None:
    parser = argparse.ArgumentParser(
        description="Run ReaScript refinement on a .rpp file"
    )
    parser.add_argument("rpp_path", help="Path to .rpp file")
    parser.add_argument(
        "--output", "-o", help="Path for rendered WAV output", default=None
    )
    parser.add_argument(
        "--timeout",
        type=int,
        default=120,
        help="Timeout in seconds for polling result (default: 120)",
    )
    parser.add_argument(
        "--plugins-config",
        help="Path to JSON serialized SongDefinition for deriving plugins_to_add",
        default=None,
    )
    parser.add_argument(
        "--action",
        help="Space-separated action pipeline (e.g. 'add_plugins calibrate render'). Default: calibrate",
        default=None,
    )
    args = parser.parse_args()

    rpp_path = Path(args.rpp_path)
    if not rpp_path.exists():
        print(f"ERROR: .rpp file not found: {rpp_path}", file=sys.stderr)
        sys.exit(1)

    # Resolve render_path: default to .rpp folder with _rendered.wav
    if args.output:
        render_path = Path(args.output)
    else:
        render_path = rpp_path.parent / f"{rpp_path.stem}_rendered.wav"

    # Normalize action to list
    actions = _normalize_action(args.action)

    # Derive plugins_to_add from song if --plugins-config is provided
    plugins_to_add: list[dict] = []
    if args.plugins_config:
        config_path = Path(args.plugins_config)
        if not config_path.exists():
            print(f"ERROR: plugins config file not found: {config_path}", file=sys.stderr)
            sys.exit(1)
        song = _load_song_config(config_path)
        plugins_to_add = get_builtin_plugins(song)
        print(f"Derived {len(plugins_to_add)} builtin plugins from song config")

    # 1. Build command
    command = ReaScriptCommand(
        version=1,
        action=actions,
        rpp_path=str(rpp_path.resolve()),
        render_path=str(render_path.resolve()),
        timeout=args.timeout,
        track_calibration=[],
        plugins_to_add=plugins_to_add,
    )

    # 2. Generate ReaScript
    generator = ReaScriptGenerator()
    # Write to a fixed path - in real usage this would be REAPER ResourcePath()/scripts/
    reaper_scripts_dir = Path(sys.path[1]) / "scripts" if len(sys.path) > 1 else Path("scripts")
    script_path = reaper_scripts_dir / "fl_control_phase2.py"
    generator.generate(script_path, command)
    print(f"Generated ReaScript: {script_path}")

    # 3. Write command JSON (same directory as script)
    cmd_json_path = reaper_scripts_dir / "fl_control_command.json"
    write_command(cmd_json_path, command)
    print(f"Wrote command JSON: {cmd_json_path}")

    # 4. Poll for result JSON
    res_json_path = reaper_scripts_dir / "fl_control_result.json"
    timeout = args.timeout
    poll_interval = 2.0
    elapsed = 0.0

    print(f"Polling for result at {res_json_path} (timeout={timeout}s)...")
    while elapsed < timeout:
        if res_json_path.exists():
            break
        time.sleep(poll_interval)
        elapsed += poll_interval

    if not res_json_path.exists():
        print("TIMEOUT: result JSON not found within timeout", file=sys.stderr)
        sys.exit(2)

    # 5. Read and print results
    try:
        result = read_result(res_json_path)
    except Exception as e:
        print(f"ERROR: failed to read result: {e}", file=sys.stderr)
        sys.exit(1)

    if result.status == "error":
        print(f"REAPER error: {result.message}", file=sys.stderr)
        sys.exit(1)

    # Print LUFS metrics
    print("\n=== Phase 2 Result ===")
    print(f"Status: {result.status}")
    if result.lufs is not None:
        print(f"LUFS: {result.lufs}")
    if result.integrated_lufs is not None:
        print(f"Integrated LUFS: {result.integrated_lufs}")
    if result.short_term_lufs is not None:
        print(f"Short-term LUFS: {result.short_term_lufs}")
    print(f"Tracks verified: {result.tracks_verified}")
    if result.fx_errors:
        print(f"FX errors: {json.dumps(result.fx_errors, indent=2)}")
    if result.added_plugins:
        print(f"Added plugins: {json.dumps(result.added_plugins, indent=2)}")
    print(f"Message: {result.message}")

    # 6. Write output files
    output_dir = rpp_path.parent / "output"
    output_dir.mkdir(exist_ok=True)

    # LUFS metrics
    lufs_data = {
        "lufs": result.lufs,
        "integrated_lufs": result.integrated_lufs,
        "short_term_lufs": result.short_term_lufs,
        "render_path": str(render_path.resolve()),
    }
    lufs_path = output_dir / f"{rpp_path.stem}_lufs.json"
    with open(lufs_path, "w", encoding="utf-8") as f:
        json.dump(lufs_data, f, indent=2)
    print(f"Wrote LUFS data: {lufs_path}")

    # FX errors
    if result.fx_errors:
        fx_path = output_dir / f"{rpp_path.stem}_fx_errors.json"
        with open(fx_path, "w", encoding="utf-8") as f:
            json.dump(result.fx_errors, f, indent=2)
        print(f"Wrote FX errors: {fx_path}")

    print("\nPhase 2 complete.")
    sys.exit(0)


if __name__ == "__main__":
    main()
