# reproduce/core/open_loop_validator.py
"""Open-loop comparison: target-recording phase on new CelerisLab vs legacy target.

For each scene, runs the target-recording phase on the new CelerisLab with
the legacy-compatible config, then compares directly against SR_analysis
reference target signals.

This isolates CFD differences before DRL is involved.
"""

from __future__ import annotations

import json
import os
import sys
from typing import Dict, Optional

import numpy as np

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

from legacy_test.core.dtw_metrics import calc_lag, calc_dtw_sim  # noqa: E402
from legacy_test.core.io_helpers import load_reference_target  # noqa: E402


def compare_target_signals(
    ref_dir: str,
    new_target: np.ndarray,
    label: str = "",
    conv_len: int = 30,
    sensor_slice: slice = slice(0, 6),
) -> Dict:
    """Compare new CFD target signals against SR_analysis legacy target.

    Args:
        ref_dir: Path to SR_analysis scene directory.
        new_target: (FIFO_LEN, N) new CFD target signals.
        label: Scene label for printing.
        conv_len: DTW convergence window.
        sensor_slice: Which columns of new_target are sensor channels.

    Returns:
        dict with dtw_sim, per_channel_corr, rms_err, passed.
    """
    ref = load_reference_target(ref_dir)

    # Apply sensor slice if reference has more columns than new
    if ref.shape[1] > new_target.shape[1]:
        ref = ref[:, sensor_slice]
    new = new_target[:, sensor_slice] if sensor_slice.stop <= new_target.shape[1] else new_target

    n = min(ref.shape[0], new.shape[0])
    ref = ref[:n]
    new = new[:n]

    n_ch = min(ref.shape[1], new.shape[1])
    ch_corr = []
    for i in range(n_ch):
        r = ref[:, i]
        g = new[:, i]
        denom = np.sqrt(((r - r.mean())**2).sum() * ((g - g.mean())**2).sum())
        ch_corr.append(float(((r - r.mean()) * (g - g.mean())).sum() / max(denom, 1e-12)))

    # RMS error
    rms = float(np.sqrt(np.mean((ref - new)**2)))

    # DTW similarity (all channels)
    sim_sum = 0.0
    for i in range(n_ch):
        ref_seq = ref[conv_len:2 * conv_len, i]
        new_seq = new[-conv_len:, i]
        sim_sum += calc_dtw_sim(ref_seq, new_seq)
    dtw_sim = float(sim_sum / max(n_ch, 1))

    # FFT peak comparison on first channel
    ref_fft = np.abs(np.fft.rfft(ref[:, 0]))
    new_fft = np.abs(np.fft.rfft(new[:, 0]))
    freqs = np.fft.rfftfreq(n, d=1)
    ref_peak = freqs[1:][np.argmax(ref_fft[1:])] if len(freqs) > 1 else 0
    new_peak = freqs[1:][np.argmax(new_fft[1:])] if len(freqs) > 1 else 0
    fft_ok = abs(ref_peak - new_peak) / max(abs(ref_peak), 1e-12) < 0.10 if abs(ref_peak) > 1e-12 else True

    passed = dtw_sim > 0.90 and float(np.min(ch_corr if ch_corr else [1.0])) > 0.85

    prefix = f"[{label}] " if label else ""
    print(f"{prefix}Channel corr: {ch_corr}")
    print(f"{prefix}DTW sim: {dtw_sim:.4f}, RMS err: {rms:.6f}")
    print(f"{prefix}FFT peak: ref={ref_peak:.6f}, new={new_peak:.6f}, ok={fft_ok}")
    print(f"{prefix}{'PASS' if passed else 'FAIL'}")

    return {
        "channel_corr": ch_corr,
        "dtw_sim": float(dtw_sim),
        "rms_err": float(rms),
        "ref_fft_peak": float(ref_peak),
        "new_fft_peak": float(new_peak),
        "fft_ok": bool(fft_ok),
        "passed": bool(passed),
    }


def load_legacy_target(ref_dir: str) -> np.ndarray:
    """Load legacy target from SR_analysis data."""
    return load_reference_target(ref_dir)
