//! Regression pin for primitive Eq/Ord mono-symbol body hashes.
//!
//! Locks the six primitive mono symbols (`eq__Int`, `eq__Bool`, `eq__Str`,
//! `compare__Int`, `compare__Bool`, `compare__Str`) to their pre-iter-23.4
//! body hashes. The unified mono pass MUST produce bit-identical bodies.

use ailang_core::ast::Def;
use ailang_core::hash::def_hash;
use ailang_surface::load_workspace;
use std::path::PathBuf;

fn fixture_path() -> PathBuf {
    let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    d.pop();
    d.pop();
    d.join("examples").join("mono_hash_pin_smoke.ail")
}

fn show_fixture_path() -> PathBuf {
    let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    d.pop();
    d.pop();
    d.join("examples").join("show_mono_pin_smoke.ail")
}

#[test]
fn primitive_eq_ord_mono_symbol_hashes_stay_bit_identical() {
    let ws = load_workspace(&fixture_path()).expect("workspace loads");
    let diags = ailang_check::check_workspace(&ws);
    assert!(diags.is_empty(), "typecheck diagnostics: {diags:?}");
    let post_mono = ailang_check::monomorphise_workspace(&ws).expect("mono green");

    // Mono symbols for class methods are appended to the instance's
    // `defining_module` — i.e. the prelude module, since all six
    // primitive Eq/Ord instances live there.
    let prelude_mod = post_mono
        .modules
        .get("prelude")
        .expect("prelude module present");

    // 2026-05-14 bugfix-print-leak-show-ret-mode: hashes re-pinned
    // after `substitute_rigids` started preserving `param_modes` and
    // `ret_mode` through rigid substitution (was: hard-reset to
    // Implicit / `[]`). Eq/Ord class methods declare
    // `param_modes: ["borrow", "borrow"]` in `prelude.ail.json`, so
    // the mono-synthesised `eq__T` / `compare__T` symbols now carry
    // those modes in their `Type::Fn` — canonical-JSON bytes drift,
    // bodies are semantically identical.
    // 2026-05-21 operator-routing-eq-ord re-pinned the three `eq__*`
    // hashes after the prelude reshape: the Eq Int/Bool/Str instance
    // bodies became placeholder `false` lambdas (codegen intercept
    // emits the actual `icmp` / `@ail_str_eq` call). The three
    // `compare__*` hashes were unchanged — those bodies were already
    // placeholder Ordering ctors.
    // 2026-05-29 intrinsic-bodies.2 re-pinned all six: the prelude
    // Eq/Ord instance-method bodies migrated from placeholder terms
    // ((body false)/(body true)/(term-ctor Ordering EQ)) to
    // Term::Intrinsic, so the mono-synthesised eq__T/compare__T body
    // bytes flip. Behaviour is unchanged — codegen intercepts these
    // by name; the body is signature-only. The four show__* pins
    // below do NOT move (Show instance bodies are real, unchanged).
    let pins: &[(&str, &str)] = &[
        ("eq__Int",       "85be5c2de568b58d"),
        ("eq__Bool",      "851b24fb1b513bf9"),
        ("eq__Str",       "ead456ddde7c0cbd"),
        ("compare__Int",  "f99b792409ee8417"),
        ("compare__Bool", "bdf90cb462351564"),
        ("compare__Str",  "752b08fdd12c6a67"),
    ];

    for (sym, pin) in pins {
        let def = prelude_mod
            .defs
            .iter()
            .find(|d| matches!(d, Def::Fn(f) if f.name == *sym))
            .unwrap_or_else(|| panic!("mono symbol {sym} not found in prelude module"));
        let h = def_hash(def);
        assert_eq!(&h, pin, "{sym} body hash drifted; captured: {h}");
    }
}

#[test]
fn primitive_show_mono_symbol_hashes_stay_bit_identical() {
    // 24.2 hash-stability pin for the four primitive `show__T` mono
    // symbols synthesised from `prelude.Show {Int|Bool|Str|Float}`
    // instance bodies. Loaded from a separate fixture
    // (`show_mono_pin_smoke.ail.json`) so the Eq/Ord pin above stays
    // structurally isolated.
    let ws = load_workspace(&show_fixture_path()).expect("workspace loads");
    let diags = ailang_check::check_workspace(&ws);
    assert!(diags.is_empty(), "typecheck diagnostics: {diags:?}");
    let post_mono = ailang_check::monomorphise_workspace(&ws).expect("mono green");

    let prelude_mod = post_mono
        .modules
        .get("prelude")
        .expect("prelude module present");

    // 2026-05-14 bugfix-print-leak-show-ret-mode: hashes re-pinned
    // after the Show class method declaration in `prelude.ail.json`
    // added `ret_mode: "own"` AND `substitute_rigids` started
    // preserving `param_modes`/`ret_mode`. The mono-synthesised
    // `show__T` symbols now carry `param_modes: ["borrow"]` and
    // `ret_mode: Own` in their `Type::Fn` — bodies are semantically
    // identical.
    let pins: &[(&str, &str)] = &[
        ("show__Int",   "9229ce65fdf11f61"),
        ("show__Bool",  "ade621119184351f"),
        ("show__Str",   "93fb52834427598c"),
        ("show__Float", "49874c7ed31c9d15"),
    ];

    for (sym, pin) in pins {
        let def = prelude_mod
            .defs
            .iter()
            .find(|d| matches!(d, Def::Fn(f) if f.name == *sym))
            .unwrap_or_else(|| panic!("mono symbol {sym} not found in prelude module"));
        let h = def_hash(def);
        assert_eq!(&h, pin, "{sym} body hash drifted; captured: {h}");
    }
}
