"""Interface adapter exposing the ``simulate_q1`` API over the
canonical Bayesian Monte Carlo engine.

The EVPPI, tornado, and graph-topology analyses (bayesian_evppi.py,
bayesian_tornado.py, graph_topology_sensitivity.py) consume a
``simulate_q1`` interface. This module provides that interface and
forwards every call to the canonical simulator in
bayesian_mc_canonical.py, so those analyses and the headline
distributions are produced by one and the same model.

Interface:

    simulate_q1(arch, R, seed, per_rep=None, graph_variant='6node')
        Returns dict with keys 'q1' (length R), 'Q10' (length R),
        'param_samples' (dict of length-R arrays).

The ``pr`` / ``per_rep`` dict accepts the parameter names gamma,
gamma_X_ratio, d_EA, d_tg, d_co, P3P4, sigma_lam and forwards them to
bayesian_mc_canonical.simulate as per_rep_overrides.
"""
import numpy as np
import bayesian_mc_canonical as bmc

# Number of attack-graph channels.
N_CHANNELS = 4

# Channel weights, normalised.
CHANNEL_WEIGHTS = np.array([1.5, 1.5, 1.5, 1.0]) / np.sum([1.5, 1.5, 1.5, 1.0])

# DEFAULT_PARAMS dict. The parameter names match the keys read by the
# EVPPI, tornado, and topology scripts; values are taken from
# bayesian_mc_canonical.PARAMS so the two stay consistent.
DEFAULT_PARAMS = {
    "lam_total":      bmc.PARAMS["lam_total"],
    "sigma_lam":      bmc.PARAMS["sigma_lam"],
    "log_d_EA_mu":    bmc.PARAMS["log_d_EA"][0],
    "log_d_EA_sd":    bmc.PARAMS["log_d_EA"][1],
    "log_d_tg_mu":    bmc.PARAMS["log_d_target"][0],
    "log_d_tg_sd":    bmc.PARAMS["log_d_target"][1],
    "log_d_co_mu":    bmc.PARAMS["log_d_concen"][0],
    "log_d_co_sd":    bmc.PARAMS["log_d_concen"][1],
    "alpha_Z":        bmc.PARAMS["alpha_Z"],
    "beta_Z":         bmc.PARAMS["beta_Z"],
    "gamma":          bmc.PARAMS["gamma"],
    "gamma_X_ratio":  bmc.PARAMS["gamma_X_ratio_median"],
    "P_3":            bmc.PARAMS["P_3"],
    "P_4":            bmc.PARAMS["P_4"],
}


def simulate_q1(arch, R, seed, per_rep=None, graph_variant="6node"):
    """Delegate to bayesian_mc_canonical.simulate.

    Parameters
    ----------
    arch : 'TEA' or 'OTT'
    R : int, number of Monte Carlo replicates
    seed : int, RNG seed
    per_rep : dict, optional, per-replicate parameter overrides.
        Keys accepted: 'gamma', 'gamma_X_ratio', 'd_EA', 'd_tg', 'd_co',
        'P3P4', 'sigma_lam'. Values must be length-R arrays.
    graph_variant : '6node' (default) or '7node'.

    Returns
    -------
    dict with keys:
        q1 : length-R array, annual compromise probability
        Q10 : length-R array, 10-year cumulative compromise probability
        param_samples : dict of length-R arrays for each sampled parameter
    """
    sim = bmc.simulate(arch=arch, R=R, seed=seed, dependence=True,
                       n_years=10, per_rep_overrides=per_rep,
                       graph_variant=graph_variant)
    return {
        "q1": sim["q_1"],
        "Q10": sim["Q_T"],
        "param_samples": sim["param_samples"],
    }


def calibration_check(R=2000, seed=2024):
    """Light sanity-check on the canonical simulator's central medians."""
    sim_TEA = bmc.simulate("TEA", R, seed)
    sim_OTT = bmc.simulate("OTT", R, seed)
    return {
        "TEA_q1_median":  float(np.median(sim_TEA["q_1"])),
        "TEA_Q10_median": float(np.median(sim_TEA["Q_T"])),
        "OTT_q1_median":  float(np.median(sim_OTT["q_1"])),
        "OTT_Q10_median": float(np.median(sim_OTT["Q_T"])),
    }


if __name__ == "__main__":
    print("bayesian_engine -- simulate_q1 interface over canonical BMC")
    cal = calibration_check()
    print(f"TEA  q1 median: {cal['TEA_q1_median']*100:.2f}%")
    print(f"TEA Q10 median: {cal['TEA_Q10_median']*100:.2f}%")
    print(f"OTT  q1 median: {cal['OTT_q1_median']*100:.2f}%")
    print(f"OTT Q10 median: {cal['OTT_Q10_median']*100:.2f}%")
