//! Walk through all five emergent-time constructions and run the three-clock
//! benchmark. Run with:
//!
//! ```bash
//! cargo run -p emergent-time --example emergent_time
//! ```

use emergent_time::complex::fidelity;
use emergent_time::structural_clock::{
    self, evaluate, Clock, EntropyClock, Scenario, StructuralMetric, StructuralProperTime,
    WallClock,
};
use emergent_time::{
    agentic::CausalTimeline, agentic_time, agentic_time::AgentClock, entropic,
    real_matrix::RealMatrix, thermal, wheeler_dewitt, PageWootters,
};

fn rule(title: &str) {
    println!("\n=== {title} ===");
}

fn sample_hamiltonian(n: usize) -> RealMatrix {
    RealMatrix::from_fn(n, |r, c| {
        if r == c {
            (r as f64) - (n as f64 - 1.0) / 2.0
        } else if (r as i64 - c as i64).abs() == 1 {
            0.3
        } else {
            0.0
        }
    })
}

fn main() {
    println!("emergent-time : time as ordered change measured from inside the system");

    // ---- 1. Wheeler–DeWitt: the timeless universe -------------------------
    rule("1. Wheeler-DeWitt  (H|Psi> = 0)");
    let pw = PageWootters::new(sample_hamiltonian(4));
    let j = wheeler_dewitt::bipartite_constraint(&pw.clock_hamiltonian(), &pw.h_r);
    let psi = pw.global_static_state();
    let residual = wheeler_dewitt::constraint_residual(&j, &psi);
    let phys = wheeler_dewitt::solve_constraint(&j);
    println!("  global state satisfies the constraint:  ||J|Psi>|| = {residual:.2e}");
    println!(
        "  kernel eigenvalue nearest zero:          {:.2e}",
        phys.eigenvalue
    );
    println!("  -> the universe's state carries no external time parameter.");

    // ---- 2. Page–Wootters: evolution from a static state ------------------
    rule("2. Page-Wootters  (relational clock)");
    println!("  conditioning the static |Psi> on clock-reading t recovers e^-iHt|psi0>:");
    for &t in &[0.0, 0.5, 1.0, 2.0] {
        let f = fidelity(&pw.conditional_state(t), &pw.schrodinger_state(t));
        println!("    t = {t:>4}:  fidelity(conditional, Schrodinger) = {f:.10}");
    }
    println!("  -> time is what the rest sector looks like given the clock.");

    // ---- 3. Entropic time: the cold-atom clock ----------------------------
    rule("3. Entropic time  (tau_S = (S - S0)/k)");
    let h = sample_hamiltonian(5);
    let clock = entropic::EntropicClock::new(0.0, 1.0);
    let sweep = entropic::entropic_time_sweep(&h, &clock, 3.0, 0.2, 6);
    println!("  sweeping the barrier (inverse temperature lambda):");
    println!("    {:>8}  {:>10}  {:>10}", "lambda", "S(nats)", "tau_S");
    for (lam, s, tau) in &sweep {
        println!("    {lam:>8.3}  {s:>10.4}  {tau:>10.4}");
    }
    println!("  -> internal time runs fast where entropy changes fast, stalls where it saturates.");

    // ---- 4. Thermal time: flow generated by the state ---------------------
    rule("4. Thermal time  (K = -ln rho,  A(s) = e^isK A e^-isK)");
    let beta = 0.7;
    let rho = entropic::gibbs_density(&h, beta);
    let k = thermal::modular_hamiltonian(&rho);
    // K should equal beta*H + const; report the off-diagonal match.
    let diff = RealMatrix::from_fn(h.n, |r, c| k.get(r, c) - beta * h.get(r, c));
    println!("  modular Hamiltonian K = beta*H + c*I ?");
    println!(
        "    max |off-diagonal(K - beta*H)| = {:.2e}",
        diff.max_offdiag()
    );
    println!("  -> physical time flow is recovered from the thermodynamic state itself.");

    // ---- 5a. Agentic causal time ------------------------------------------
    rule("5a. Agentic causal time  (time = causal structure)");
    let mut tl = CausalTimeline::new();
    let a = tl.add(vec![], 3.0); // start: high uncertainty
    let b = tl.add(vec![a], 3.0); // idle hour: nothing changes
    let c = tl.add(vec![b], 0.6); // contradiction resolved: big drop
    let d = tl.add(vec![c], 0.55); // small refinement
    for id in [a, b, c, d] {
        println!(
            "    event {id}:  causal_depth = {}   internal_time = {:.3}",
            tl.causal_depth(id),
            tl.internal_time(id)
        );
    }
    println!("  -> an idle hour adds ~0 internal time; one contradiction is a large jump.");

    // ---- 5b. Structural Proper Time + the three-clock benchmark -----------
    rule("5b. Structural Proper Time  (a new form of agentic time)");
    let sc = Scenario::default();
    let traj = structural_clock::generate_scenario(&sc);
    let spt = StructuralProperTime::new(StructuralMetric::default());
    let budget = 10;
    // Baseline must be learned over the quiet stretch *before* the first
    // structural precursor, else the precursor contaminates the baseline.
    let (bw, ks) = (sc.baseline_window, 4.0f64);

    let reports = [
        evaluate(&WallClock, &traj, sc.fail_index, bw, ks, budget),
        evaluate(&EntropyClock, &traj, sc.fail_index, bw, ks, budget),
        evaluate(&spt, &traj, sc.fail_index, bw, ks, budget),
    ];

    println!(
        "  scenario: {} steps, structural drift @ {}, entropy rise @ {}, failure @ {}",
        sc.steps, sc.embed_onset, sc.entropy_onset, sc.fail_index
    );
    println!(
        "    {:<12} {:>12} {:>18} {:>14}",
        "clock", "warn-lead", "compress-error", "order-ok"
    );
    for r in &reports {
        println!(
            "    {:<12} {:>12} {:>18.4} {:>14}",
            r.name, r.lead, r.compression_error, r.causal_order_ok
        );
    }

    let wall = &reports[0];
    let entropy = &reports[1];
    let structural = &reports[2];
    let lead_ratio = structural.lead as f64 / entropy.lead.max(1) as f64;

    // History compression: samples each clock needs to preserve the trajectory
    // to a fixed tolerance.
    let tol = 0.3;
    let wall_budget = structural_clock::samples_to_tolerance(&WallClock, &traj, tol);
    let struct_budget = structural_clock::samples_to_tolerance(&spt, &traj, tol);
    let compress_ratio = wall_budget as f64 / struct_budget as f64;

    println!(
        "\n  warn-lead:  wall {} -> structural {}  (structure precedes the visible failure)",
        wall.lead, structural.lead
    );
    println!(
        "  early-warning lead vs entropy clock:  {:.1}x   [acceptance target: >= 2x]",
        lead_ratio
    );
    println!(
        "  history compression to tol {tol}:  wall needs {wall_budget} samples, structural needs {struct_budget}  ({compress_ratio:.1}x)"
    );

    // Agent self-awareness: replan on internal churn without progress, not on
    // step count.
    let spent = spt.cumulative(&traj).last().copied().unwrap_or(0.0);
    let progress_good = 0.9; // hypothetical converging task
    let progress_stuck = 0.05; // hypothetical thrashing task
    println!(
        "\n  agentic replan trigger (progress/structural_time < 0.1):  converging={}, thrashing={}",
        structural_clock::should_replan(spent, progress_good * spent, 0.1),
        structural_clock::should_replan(spent, progress_stuck, 0.1)
    );

    // ---- 5c. Agentic Time: the four-clock workflow demo -------------------
    rule("5c. Agentic Time  (tau_a = f(dB, dM, dR, dG, dE, dP))");
    let tr = agentic_time::generate_failing_trace(0xA9E1);
    let agentic = agentic_time::AgenticTime::new(agentic_time::AgenticWeights::default());
    let abw = tr.baseline_window;
    println!(
        "  failing workflow: {} steps, plan-thrash onset @ {}, failure @ {}",
        tr.states.len(),
        tr.thrash_onset,
        tr.fail_index
    );
    // Fair baselines: windowed z-score change-point detectors (non-strawman).
    let token_base = agentic_time::WindowedDeltaClock::token_delta(abw);
    let belief_base = agentic_time::WindowedDeltaClock::belief_shift(abw);
    println!("    {:<26} {:>12}", "clock", "warn-lead");
    let clocks: [(&str, &dyn agentic_time::AgentClock); 6] = [
        ("wall (constant-rate)", &agentic_time::AgentWallClock),
        ("step-count (constant-rate)", &agentic_time::StepCountClock),
        (
            "token-count (constant-rate)",
            &agentic_time::TokenCountClock,
        ),
        ("windowed-z[token-delta] FAIR", &token_base),
        ("windowed-z[belief] FAIR", &belief_base),
        ("agentic (multi-channel)", &agentic),
    ];
    for (label, cl) in clocks {
        let lead = agentic_time::early_warning_lead(cl, &tr.states, tr.fail_index, abw, 4.0);
        println!("    {label:<26} {lead:>12}");
    }
    println!("  NOTE: wall/step/token are constant-rate clocks (zero baseline variance ->");
    println!("        their alarm CANNOT fire); their 0 lead is a coverage gap, not a");
    println!("        measured loss. The windowed-z detectors ARE fair competitors and");
    println!("        on THIS designed trace they fire at least as early as the agentic");
    println!("        clock: the belief-shift detector catches the planted structural");
    println!("        signal (a single-channel z-score already sees it), and the");
    println!("        token-delta detector trips early on quantization noise (tokens are");
    println!("        a near-constant integer stream) -- reported, not hidden.");
    println!("  HONEST FRAMING: the agentic clock does NOT beat a fair baseline on this");
    println!("        synthetic trace. The 40-step lead is a property of how far the");
    println!("        structural precursor was planted ahead of failure, NOT a measured");
    println!("        competitive win. The agentic clock's real value -- composing many");
    println!("        weak channels when no single scalar carries the signal -- can only");
    println!("        be substantiated on a REAL trace vs this fair baseline (M3 work;");
    println!("        see ADR-251 'Honest limitations').");
    // Live health verdict at a window straddling the thrash onset.
    let th = agentic_time::HealthThresholds::default();
    let w0 = tr.thrash_onset.saturating_sub(2);
    let w1 = tr.thrash_onset + 6;
    let dtau = agentic.cumulative(&tr.states)[w1] - agentic.cumulative(&tr.states)[w0];
    let dprog = tr.progress[w1] - tr.progress[w0];
    let contra = tr.states[w1].contradiction;
    let verdict = agentic_time::classify(dtau, dprog, contra, &th);
    println!(
        "  agentic-time index at onset window: dtau={dtau:.2}, dprogress={dprog:.2} -> {verdict:?}"
    );
    println!(
        "  -> agents should measure time by meaningful change, not seconds, tokens, or steps."
    );

    println!("\n  thesis:  physics state -> vector geometry -> internal clock -> prediction");
}
