// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
//
// Integration tests for iseriser.
// Tests the full pipeline: manifest -> validation -> scaffold -> output.

use iseriser::codegen::parser::validate_language_description;
use iseriser::codegen::scaffold::scaffold_repo;
use iseriser::manifest::{parse_manifest, validate};
use tempfile::tempdir;

// ---------------------------------------------------------------------------
// Helper: build a manifest TOML string from parameters
// ---------------------------------------------------------------------------

fn make_toml(
    lang_name: &str,
    paradigm: &str,
    type_sys: &str,
    target: &str,
    primitives: &[&str],
) -> String {
    let iser_name = format!("{}iser", lang_name.to_lowercase());
    let prims: Vec<String> = primitives.iter().map(|p| format!("\"{}\"", p)).collect();
    format!(
        r#"
[project]
name = "{iser_name}"
version = "0.1.0"

[language]
name = "{lang_name}"
paradigm = "{paradigm}"
type-system = "{type_sys}"
compilation-target = "{target}"
key-primitives = [{primitives}]

[output]
repo-name = "{iser_name}"
github-org = "hyperpolymath"
description = "{lang_name} interop -iser — generated by iseriser"
"#,
        iser_name = iser_name,
        lang_name = lang_name,
        paradigm = paradigm,
        type_sys = type_sys,
        target = target,
        primitives = prims.join(", "),
    )
}

// ---------------------------------------------------------------------------
// Test 1: Full pipeline for an imperative/native language (Chapel)
// ---------------------------------------------------------------------------

#[test]
fn test_full_pipeline_chapel() {
    let toml = make_toml(
        "Chapel",
        "imperative",
        "simple",
        "native",
        &["task", "locale", "domain", "forall", "sync"],
    );
    let manifest = parse_manifest(&toml).expect("parse failed");
    validate(&manifest).expect("structural validation failed");

    let report = validate_language_description(&manifest);
    assert!(
        report.is_valid(),
        "semantic validation failed: {:?}",
        report.errors
    );

    let tmp = tempdir().unwrap();
    let result = scaffold_repo(&manifest, tmp.path());
    assert!(
        result.is_success(),
        "scaffold failed: {:?}",
        result.error_message()
    );

    let repo = result.repo().unwrap();
    assert_eq!(repo.name, "chapeliser");
    assert!(
        repo.is_complete(),
        "generated repo missing mandatory categories"
    );
    assert!(
        repo.file_count() >= 20,
        "expected 20+ files, got {}",
        repo.file_count()
    );

    // Verify key files exist on disk
    let root = tmp.path().join("chapeliser");
    assert!(root.join("Cargo.toml").exists(), "Cargo.toml missing");
    assert!(root.join("src/main.rs").exists(), "src/main.rs missing");
    assert!(
        root.join("src/interface/abi/Types.idr").exists(),
        "Types.idr missing"
    );
    assert!(
        root.join("ffi/zig/src/main.zig").exists(),
        "main.zig missing"
    );
    assert!(root.join("README.adoc").exists(), "README.adoc missing");
    assert!(root.join("TOPOLOGY.md").exists(), "TOPOLOGY.md missing");

    // Verify Cargo.toml content
    let cargo = std::fs::read_to_string(root.join("Cargo.toml")).unwrap();
    assert!(cargo.contains("chapeliser"), "Cargo.toml missing repo name");
    assert!(
        cargo.contains("PMPL-1.0-or-later"),
        "Cargo.toml missing license"
    );
}

// ---------------------------------------------------------------------------
// Test 2: Functional / BEAM language (Gleam)
// ---------------------------------------------------------------------------

#[test]
fn test_full_pipeline_gleam_functional_beam() {
    let toml = make_toml(
        "Gleam",
        "functional",
        "algebraic",
        "beam",
        &["result", "option", "dynamic"],
    );
    let manifest = parse_manifest(&toml).unwrap();
    validate(&manifest).unwrap();

    let tmp = tempdir().unwrap();
    let result = scaffold_repo(&manifest, tmp.path());
    assert!(result.is_success());

    let repo = result.repo().unwrap();
    assert_eq!(repo.name, "gleamiser");
    assert!(repo.is_complete());

    // Functional paradigm should generate ADT mapper
    assert!(
        repo.files
            .iter()
            .any(|f| f.path.to_str().unwrap().contains("adt_mapper")),
        "functional language should have adt_mapper"
    );

    // BEAM target should generate NIF boilerplate
    assert!(
        repo.files
            .iter()
            .any(|f| f.path.to_str().unwrap().contains("beam_nif")),
        "BEAM target should have beam_nif"
    );
}

// ---------------------------------------------------------------------------
// Test 3: Array language with deep type system (Julia)
// ---------------------------------------------------------------------------

#[test]
fn test_full_pipeline_julia_array() {
    let toml = make_toml(
        "Julia",
        "array",
        "gradual",
        "native",
        &["array", "broadcast", "multiple-dispatch"],
    );
    let manifest = parse_manifest(&toml).unwrap();
    validate(&manifest).unwrap();

    let tmp = tempdir().unwrap();
    let result = scaffold_repo(&manifest, tmp.path());
    assert!(result.is_success());

    let repo = result.repo().unwrap();
    assert_eq!(repo.name, "juliaiser");

    // Array paradigm should generate array bridge
    assert!(
        repo.files
            .iter()
            .any(|f| f.path.to_str().unwrap().contains("array_bridge")),
        "array language should have array_bridge"
    );
}

// ---------------------------------------------------------------------------
// Test 4: Dependent types language (Idris2-to-Idris2, meta!)
// ---------------------------------------------------------------------------

#[test]
fn test_full_pipeline_dependent_types() {
    let toml = make_toml(
        "Idris2",
        "functional",
        "dependent",
        "native",
        &["Nat", "Vect", "DPair"],
    );
    let manifest = parse_manifest(&toml).unwrap();
    validate(&manifest).unwrap();

    let tmp = tempdir().unwrap();
    let result = scaffold_repo(&manifest, tmp.path());
    assert!(result.is_success());

    let repo = result.repo().unwrap();

    // Dependent types should generate deep ABI proof docs
    assert!(
        repo.files
            .iter()
            .any(|f| f.path.to_str().unwrap().contains("deep-abi-proofs")),
        "dependent type language should have deep ABI proof docs"
    );

    // Verify the Idris2 Types.idr mentions deep proofs
    let types_idr = repo
        .files
        .iter()
        .find(|f| f.path.to_str().unwrap().contains("Types.idr"))
        .unwrap();
    assert!(
        types_idr.content.contains("DeepProof"),
        "Types.idr should contain DeepProof for dependent types"
    );
}

// ---------------------------------------------------------------------------
// Test 5: Validation rejects invalid primitives
// ---------------------------------------------------------------------------

#[test]
fn test_validation_rejects_empty_primitive() {
    let toml = make_toml("BadLang", "functional", "algebraic", "native", &["ok", ""]);
    let manifest = parse_manifest(&toml).unwrap();
    let report = validate_language_description(&manifest);
    assert!(!report.is_valid(), "should reject empty primitive");
}

// ---------------------------------------------------------------------------
// Test 6: Validation warns on unusual paradigm/target combos
// ---------------------------------------------------------------------------

#[test]
fn test_validation_warns_logic_gpu() {
    let toml = make_toml("WeirdProlog", "logic", "none", "gpu", &["clause"]);
    let manifest = parse_manifest(&toml).unwrap();
    let report = validate_language_description(&manifest);
    assert!(
        report.is_valid(),
        "unusual combo should be a warning, not error"
    );
    assert!(
        !report.warnings.is_empty(),
        "should have warnings for logic+gpu"
    );
}

// ---------------------------------------------------------------------------
// Test 7: JS target generates JS bridge
// ---------------------------------------------------------------------------

#[test]
fn test_js_target_generates_bridge() {
    let toml = make_toml(
        "ReScript",
        "functional",
        "algebraic",
        "js",
        &["variant", "record", "promise"],
    );
    let manifest = parse_manifest(&toml).unwrap();
    validate(&manifest).unwrap();

    let tmp = tempdir().unwrap();
    let result = scaffold_repo(&manifest, tmp.path());
    assert!(result.is_success());

    let repo = result.repo().unwrap();
    assert!(
        repo.files
            .iter()
            .any(|f| f.path.to_str().unwrap().contains("js_bridge")),
        "JS target should generate js_bridge"
    );
}

// ---------------------------------------------------------------------------
// Test 8: Manifest init and reload round-trip
// ---------------------------------------------------------------------------

#[test]
fn test_manifest_init_round_trip() {
    let tmp = tempdir().unwrap();
    let dir = tmp.path().to_str().unwrap();

    // Init creates a file
    iseriser::manifest::init_manifest(dir).expect("init failed");

    let manifest_path = tmp.path().join("iseriser.toml");
    assert!(
        manifest_path.exists(),
        "iseriser.toml should exist after init"
    );

    // The generated file should be parseable
    let content = std::fs::read_to_string(&manifest_path).unwrap();
    let m = parse_manifest(&content).expect("generated manifest should parse");
    validate(&m).expect("generated manifest should validate");
}

// ---------------------------------------------------------------------------
// Test 9: Structural validation catches missing repo-name suffix
// ---------------------------------------------------------------------------

#[test]
fn test_structural_validation_repo_name_suffix() {
    let toml = r#"
[project]
name = "bad"
[language]
name = "Bad"
paradigm = "imperative"
type-system = "simple"
compilation-target = "native"
key-primitives = ["x"]
[output]
repo-name = "bad-tool"
github-org = "hyperpolymath"
description = "test"
"#;
    let m = parse_manifest(toml).unwrap();
    let err = validate(&m).unwrap_err();
    assert!(
        err.to_string().contains("iser"),
        "should require repo-name ending in 'iser'"
    );
}
