# OID_analysis/analysis/phase4a_signature_oid.py
"""
Phase 4a: Signature-OID minimal version.
Cross-covariance SVD between correction-field POD coefs and delayed sensor error.

Only for periodic scenes: karman_re100, illusion_0.75L, 1.0L, 1.5L.

Usage:
    python3 src/OID_analysis/analysis/phase4a_signature_oid.py
"""
from __future__ import annotations

import argparse
import json
import os
import sys

import numpy as np

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

from OID_analysis.configs import DATA_DIR  # noqa: E402
from OID_analysis.utils.analysis import (  # noqa: E402
    compute_force_oid as compute_oid,
    standardize, reconstruct_oid_modes,
)


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


def run_signature_oid(scene_key: str, version: str = "delayed"):
    print(f"\n--- Phase 4a: Signature-OID ({version}) for {scene_key} ---")

    # Load POD coefs
    pod_dir = os.path.join(DATA_DIR, "derived", "pod", scene_key)
    pod_fp = os.path.join(pod_dir, "pod_coefs_r10.npy.npz")
    if not os.path.isfile(pod_fp):
        print(f"  WARNING: POD not found")
        return
    pod_npz = np.load(pod_fp, allow_pickle=True)
    coefs = pod_npz["coefs"]

    # Load observable
    obs_dir = os.path.join(DATA_DIR, "derived", "observables", scene_key)
    if version == "delayed":
        obs_name = "sensor_error_delayed"
    else:
        obs_name = "sensor_error"

    obs_fp = os.path.join(obs_dir, f"{obs_name}.npz")
    if not os.path.isfile(obs_fp):
        print(f"  WARNING: {obs_name} not found at {obs_fp}")
        return
    obs_data = np.load(obs_fp)
    Y = obs_data["standardized"]

    N = min(coefs.shape[0], Y.shape[0])
    A = coefs[:N]
    Y_cut = Y[:N]
    A_std, _, _ = standardize(A)

    oid = compute_oid(A_std, Y_cut)

    # Load POD modes for reconstruction
    pod_modes = None
    modes_fp = os.path.join(pod_dir, "pod_modes_r10.npz")
    if os.path.isfile(modes_fp):
        md = np.load(modes_fp)
        pod_modes = md["modes"]
        oid_modes = reconstruct_oid_modes(pod_modes[:, :A.shape[1]], oid["U"])
    else:
        oid_modes = None

    # Save
    out_dir = os.path.join(DATA_DIR, "derived", "oid", "signature", scene_key)
    os.makedirs(out_dir, exist_ok=True)
    np.savez(os.path.join(out_dir, f"sig_oid_{version}.npz"),
             z=oid["z"], S=oid["S"], U=oid["U"], cum_corr=oid["cum_corr"])
    if oid_modes is not None:
        np.savez_compressed(os.path.join(out_dir, f"sig_oid_{version}_modes.npz"),
                            modes=oid_modes[:, :6])

    print(f"  Sig vals: {oid['S'][:6]}")
    print(f"  Cum corr (3): {oid['cum_corr'][:3]}")


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--scene", type=str, default=None)
    args = ap.parse_args()

    targets = [args.scene] if args.scene and args.scene in SCENES else SCENES

    for sn in targets:
        run_signature_oid(sn, "current")  # e_s(t)
        run_signature_oid(sn, "delayed")  # e_s(t+tau_c)

    print("Phase 4a complete.")


if __name__ == "__main__":
    main()
