//! RED-pin for the series-step-1 enabling fix (#61, base-tier): a
//! polymorphic function may allocate a `RawBuf a` over its OWN forall
//! type variable via `new` with NO explicit type-arg — the element
//! type is solved purely from the expected ctor-field context
//! `(con RawBuf a)` — and under monomorphisation the buffer must
//! specialise to the CALLER's concrete element type, NOT default to
//! Unit.
//!
//! Property protected: a check-time-solved element var on
//! `(new RawBuf n)` survives monomorphisation. When the only carrier
//! of the buffer's element type is the enclosing ADT ctor-field type
//! `(con RawBuf a)` (the builder `mk` takes only the size, never a
//! value of type `a`), mono must propagate the check-phase solution
//! for `a` to the caller's concrete instantiation. Today it drops
//! that solution, defaults `a` to Unit, and codegen mints
//! `RawBuf_new__Unit`, which has no registered intercept
//! (crates/ailang-codegen/src/intercepts.rs registers only
//! Int/Float/Bool). The fix lives in mono substitution propagation to
//! Term::New lowering (crates/ailang-check/src/lower_to_mir.rs:501,
//! `elem: None`). Precedent: the #51 explicit-`(con Int)`-type-arg
//! fix carried a WRITTEN annotation through mono; the
//! variable-via-expected-type case here is the untouched gap.
//!
//! This is a CLI-boundary E2E test because the defect spans the
//! check -> mono -> codegen pipeline and the symptom is the exit
//! behaviour of `ail build` itself. The element is read back via
//! `first` (RawBuf.get) so the specialisation is OBSERVABLE: a wrong
//! specialisation could not print the stored Float.

use std::path::{Path, PathBuf};
use std::process::Command;

fn repo_root() -> PathBuf {
    let manifest_dir = env!("CARGO_MANIFEST_DIR");
    Path::new(manifest_dir)
        .parent()
        .unwrap()
        .parent()
        .unwrap()
        .to_path_buf()
}

/// A polymorphic `Box a` whose builder `mk` allocates `(new RawBuf n)`
/// over its own forall var `a` (no type-arg, element solved from the
/// `(con RawBuf a)` ctor-field context). Instantiated by the caller at
/// `Float`, it must build under `--alloc=rc` and print the stored
/// `1.0` — proving the inner buffer specialised to the caller's
/// element type rather than defaulting to Unit.
#[test]
fn rawbuf_new_poly_elem_specialises_to_caller_type() {
    let fixture = repo_root().join("examples/raw_buf_new_poly_elem.ail");
    let tmp = std::env::temp_dir().join(format!(
        "ailang_rawbuf_new_poly_elem_{}",
        std::process::id()
    ));
    std::fs::create_dir_all(&tmp).unwrap();
    let out = tmp.join("bin");

    let build = Command::new(env!("CARGO_BIN_EXE_ail"))
        .args(["build", fixture.to_str().unwrap(), "--alloc=rc", "-o"])
        .arg(&out)
        .output()
        .expect("ail build failed to spawn");
    assert!(
        build.status.success(),
        "ail build --alloc=rc failed for polymorphic RawBuf<a> over a \
         forall var solved from the ctor-field context: stderr={}",
        String::from_utf8_lossy(&build.stderr)
    );

    let run = Command::new(&out).output().expect("execute binary");
    assert!(
        run.status.success(),
        "binary exited non-zero: {:?}",
        run.status
    );
    let stdout = String::from_utf8_lossy(&run.stdout);
    assert!(
        stdout.trim() == "1.0",
        "expected the program to print the stored Float `1.0` (buffer \
         specialised to the caller's element type Float), got: {stdout:?}"
    );
}
