use std::fs;

use tempfile::tempdir;

use super::test_support::{
    benchmark_gate_result, eval_report, write_contract_gate_workspace_fixture,
};
use super::*;
use crate::models::{DependencyAuditSummary, DependencyInventorySummary, EvalBucket};

const PROMPT_SIGNATURE_POLICY_HEAD_OLD: &str = r#"
fn expected_prompt_contract_signatures(
    contract_version: &str,
    protocol_version: &str,
) -> Option<PromptContractSignatures> {
    match (contract_version.trim(), protocol_version.trim()) {
        ("2.0.0", "om-v2") => Some(PromptContractSignatures {
            observer_single_blake3: "oldhash",
            reflector_blake3: "oldreflect",
        }),
        _ => None,
    }
}
"#;

const PROMPT_SIGNATURE_POLICY_HEAD_CHANGED_WITHOUT_BUMP: &str = r#"
fn expected_prompt_contract_signatures(
    contract_version: &str,
    protocol_version: &str,
) -> Option<PromptContractSignatures> {
    match (contract_version.trim(), protocol_version.trim()) {
        ("2.0.0", "om-v2") => Some(PromptContractSignatures {
            observer_single_blake3: "newhash",
            reflector_blake3: "oldreflect",
        }),
        _ => None,
    }
}
"#;

const PROMPT_SIGNATURE_POLICY_HEAD_CHANGED_WITH_BUMP: &str = r#"
fn expected_prompt_contract_signatures(
    contract_version: &str,
    protocol_version: &str,
) -> Option<PromptContractSignatures> {
    match (contract_version.trim(), protocol_version.trim()) {
        ("2.1.0", "om-v2") => Some(PromptContractSignatures {
            observer_single_blake3: "newhash",
            reflector_blake3: "oldreflect",
        }),
        _ => None,
    }
}
"#;

#[test]
fn eval_quality_gate_decision_respects_threshold_and_case_count() {
    let no_cases = eval_quality_gate_decision(&eval_report(0, 1.0));
    assert!(!no_cases.passed);

    let low_accuracy = eval_quality_gate_decision(&eval_report(10, 0.5));
    assert!(!low_accuracy.passed);

    let passing = eval_quality_gate_decision(&eval_report(10, 0.9));
    assert!(passing.passed);
}

#[test]
fn eval_quality_gate_decision_fails_when_filter_or_relation_buckets_exist() {
    let mut report = eval_report(10, 0.9);
    report.quality.buckets = vec![
        EvalBucket {
            name: "filter_ignored".to_string(),
            count: 1,
        },
        EvalBucket {
            name: "relation_missing".to_string(),
            count: 0,
        },
    ];
    let decision = eval_quality_gate_decision(&report);
    assert!(!decision.passed);
    let details = parse_gate_details(&decision);
    match details {
        ReleaseGateDetails::EvalQuality(value) => {
            assert_eq!(value.filter_ignored, 1);
        }
        other => panic!("expected eval_quality details, got {other:?}"),
    }
}

#[test]
fn session_memory_gate_decision_fails_when_category_missing() {
    let decision = session_memory_gate_decision(true, 2, "probe");
    assert!(!decision.passed);
    let details = parse_gate_details(&decision);
    match details {
        ReleaseGateDetails::SessionMemory(value) => {
            assert_eq!(value.memory_category_miss, 2);
        }
        other => panic!("expected session_memory details, got {other:?}"),
    }
}

#[test]
fn benchmark_gate_prefers_release_check_evidence_uri() {
    let decision = benchmark_release_gate_decision(&benchmark_gate_result(
        Some("axiom://queue/release/checks/1.json"),
        Some("axiom://queue/release/gates/1.json"),
    ));
    assert_eq!(
        decision.evidence_uri.as_deref(),
        Some("axiom://queue/release/checks/1.json")
    );
}

#[test]
fn finalize_release_gate_pack_adds_g8_and_counts_blockers() {
    let decisions = vec![
        gate_decision(
            ReleaseGateId::ContractIntegrity,
            true,
            ReleaseGateDetails::BlockerRollup(BlockerRollupGateDetails {
                unresolved_blockers: 0,
            }),
            None,
        ),
        gate_decision(
            ReleaseGateId::BuildQuality,
            false,
            ReleaseGateDetails::BuildQuality(BuildQualityGateDetails {
                cargo_check: false,
                cargo_fmt: false,
                cargo_clippy: false,
                check_output: String::new(),
                fmt_output: String::new(),
                clippy_output: String::new(),
            }),
            None,
        ),
    ];
    let report = finalize_release_gate_pack_report(
        "pack-1".to_string(),
        "/tmp/ws".to_string(),
        decisions,
        "axiom://queue/release/packs/pack-1.json".to_string(),
    );
    assert!(!report.passed);
    assert_eq!(report.status, ReleaseGateStatus::Fail);
    assert_eq!(report.unresolved_blockers, 1);
    let g8 = report.decisions.last().expect("g8");
    assert_eq!(g8.gate_id, ReleaseGateId::BlockerRollup);
    assert!(!g8.passed);
    let details = parse_gate_details(g8);
    match details {
        ReleaseGateDetails::BlockerRollup(value) => {
            assert_eq!(value.unresolved_blockers, 1);
        }
        other => panic!("expected blocker_rollup details, got {other:?}"),
    }
}

#[test]
fn security_audit_gate_decision_contains_expected_summary() {
    let report = SecurityAuditReport {
        report_id: "sec-1".to_string(),
        created_at: "2026-01-01T00:00:00Z".to_string(),
        workspace_dir: "/tmp/ws".to_string(),
        passed: true,
        status: EvidenceStatus::Pass,
        inventory: DependencyInventorySummary {
            lockfile_present: true,
            package_count: 42,
        },
        dependency_audit: DependencyAuditSummary {
            tool: "cargo-audit".to_string(),
            mode: ReleaseSecurityAuditMode::Strict,
            available: true,
            executed: true,
            status: DependencyAuditStatus::Passed,
            advisories_found: 0,
            tool_version: Some("cargo-audit 1.0".to_string()),
            output_excerpt: None,
        },
        checks: Vec::new(),
        report_uri: "axiom://queue/release/security/sec-1.json".to_string(),
    };
    let decision = security_audit_gate_decision(&report);
    let details = parse_gate_details(&decision);
    match details {
        ReleaseGateDetails::SecurityAudit(value) => {
            assert_eq!(value.advisories_found, 0);
        }
        other => panic!("expected security_audit details, got {other:?}"),
    }
    assert!(decision.passed);
    assert_eq!(
        decision.evidence_uri.as_deref(),
        Some("axiom://queue/release/security/sec-1.json")
    );
}

#[test]
fn security_audit_gate_decision_fails_when_mode_is_not_strict() {
    let mut report = SecurityAuditReport {
        report_id: "sec-1".to_string(),
        created_at: "2026-01-01T00:00:00Z".to_string(),
        workspace_dir: "/tmp/ws".to_string(),
        passed: true,
        status: EvidenceStatus::Pass,
        inventory: DependencyInventorySummary {
            lockfile_present: true,
            package_count: 42,
        },
        dependency_audit: DependencyAuditSummary {
            tool: "cargo-audit".to_string(),
            mode: ReleaseSecurityAuditMode::Offline,
            available: true,
            executed: true,
            status: DependencyAuditStatus::Passed,
            advisories_found: 0,
            tool_version: Some("cargo-audit 1.0".to_string()),
            output_excerpt: None,
        },
        checks: Vec::new(),
        report_uri: "axiom://queue/release/security/sec-1.json".to_string(),
    };
    let decision = security_audit_gate_decision(&report);
    assert!(!decision.passed);
    let details = parse_gate_details(&decision);
    match details {
        ReleaseGateDetails::SecurityAudit(value) => {
            assert!(!value.strict_mode);
        }
        other => panic!("expected security_audit details, got {other:?}"),
    }

    report.dependency_audit.mode = ReleaseSecurityAuditMode::Strict;
    let strict_decision = security_audit_gate_decision(&report);
    assert!(strict_decision.passed);
}

#[test]
fn build_quality_gate_reports_failure_for_non_workspace_directory() {
    let temp = tempdir().expect("tempdir");
    let decision = evaluate_build_quality_gate(temp.path());
    assert_eq!(decision.gate_id, ReleaseGateId::BuildQuality);
    assert!(!decision.passed);
    let details = parse_gate_details(&decision);
    match details {
        ReleaseGateDetails::BuildQuality(value) => {
            assert!(!value.cargo_check);
            assert!(!value.cargo_fmt);
            assert!(!value.cargo_clippy);
        }
        other => panic!("expected build_quality details, got {other:?}"),
    }
}

#[test]
fn resolve_workspace_dir_returns_canonical_workspace_path() {
    let temp = tempdir().expect("tempdir");
    fs::write(
        temp.path().join("Cargo.toml"),
        "[workspace]\nmembers = []\nresolver = \"2\"\n",
    )
    .expect("write workspace manifest");

    let workspace = resolve_workspace_dir(Some(
        temp.path()
            .to_str()
            .expect("temporary workspace path must be valid UTF-8"),
    ))
    .expect("resolve workspace");
    let expected = fs::canonicalize(temp.path()).expect("canonical workspace path");
    assert_eq!(workspace, expected);
}

#[test]
fn resolve_workspace_dir_reports_not_found_for_missing_directory() {
    let temp = tempdir().expect("tempdir");
    let missing = temp.path().join("missing-workspace");
    let error = resolve_workspace_dir(Some(
        missing
            .to_str()
            .expect("temporary workspace path must be valid UTF-8"),
    ))
    .expect_err("missing workspace must fail");
    assert!(error.to_string().contains("workspace directory not found"));
}

#[test]
fn resolve_workspace_dir_reports_validation_error_without_manifest() {
    let temp = tempdir().expect("tempdir");
    let error = resolve_workspace_dir(Some(
        temp.path()
            .to_str()
            .expect("temporary workspace path must be valid UTF-8"),
    ))
    .expect_err("workspace without manifest must fail");
    assert!(error.to_string().contains("workspace missing Cargo.toml"));
}

fn parse_gate_details(decision: &ReleaseGateDecision) -> &ReleaseGateDetails {
    &decision.details
}

#[test]
fn release_gate_decision_serializes_details_with_explicit_kind_and_data() {
    let decision = gate_decision(
        ReleaseGateId::BlockerRollup,
        true,
        ReleaseGateDetails::BlockerRollup(BlockerRollupGateDetails {
            unresolved_blockers: 0,
        }),
        None,
    );
    let json = serde_json::to_value(&decision).expect("serialize decision");
    assert_eq!(json["gate_id"], "G8");
    assert_eq!(json["status"], "pass");
    assert_eq!(json["details"]["kind"], "blocker_rollup");
    assert_eq!(json["details"]["data"]["unresolved_blockers"], 0);
}

#[test]
fn security_audit_gate_details_serializes_enum_fields_as_contract_strings() {
    let details = SecurityAuditGateDetails {
        status: EvidenceStatus::Pass,
        mode: ReleaseSecurityAuditMode::Strict,
        strict_mode_required: true,
        strict_mode: true,
        audit_status: DependencyAuditStatus::HostToolsDisabled,
        advisories_found: 0,
        packages: 42,
    };
    let json = serde_json::to_value(&details).expect("serialize security audit gate details");
    assert_eq!(json["status"], "pass");
    assert_eq!(json["mode"], "strict");
    assert_eq!(json["audit_status"], "host_tools_disabled");
}

#[test]
fn security_audit_gate_details_deserializes_contract_strings_into_typed_fields() {
    let payload = serde_json::json!({
        "status": "fail",
        "mode": "offline",
        "strict_mode_required": true,
        "strict_mode": false,
        "audit_status": "tool_missing",
        "advisories_found": 1,
        "packages": 7
    });
    let details: SecurityAuditGateDetails =
        serde_json::from_value(payload).expect("deserialize security audit gate details");
    assert_eq!(details.status, EvidenceStatus::Fail);
    assert_eq!(details.mode, ReleaseSecurityAuditMode::Offline);
    assert_eq!(details.audit_status, DependencyAuditStatus::ToolMissing);
}

#[test]
fn parse_manifest_episodic_dependency_supports_table_form() {
    let manifest = r#"
[package]
name = "axiomsync"
version = "0.1.0"

[dependencies.episodic]
version = "0.2.3"
path = "../episodic"
"#;
    let dependency = parse_manifest_episodic_dependency(manifest).expect("parse manifest");
    assert_eq!(dependency.version_req.as_deref(), Some("0.2.3"));
    assert_eq!(dependency.path.as_deref(), Some("../episodic"));
    assert!(!dependency.has_git);
    assert!(dependency.has_path);
}

#[test]
fn parse_manifest_episodic_dependency_allows_absent_dependency() {
    let manifest = r#"
[package]
name = "axiomsync"
version = "0.1.0"
"#;
    let dependency = parse_manifest_episodic_dependency(manifest).expect("parse manifest");
    assert_eq!(dependency.version_req, None);
    assert_eq!(dependency.path, None);
    assert!(!dependency.has_git);
    assert!(!dependency.has_path);
}

#[test]
fn parse_manifest_episodic_dependency_requires_rev_when_git_is_present() {
    let manifest = r#"
[package]
name = "axiomsync"
version = "0.1.0"

[dependencies.episodic]
git = "https://example.com/episodic.git"
"#;
    let error = parse_manifest_episodic_dependency(manifest).expect_err("missing rev");
    assert_eq!(error, "episodic_dependency_missing_rev");
}

#[test]
fn episodic_manifest_req_contract_matches_accepts_workspace_semver() {
    assert!(episodic_manifest_req_contract_matches("", ""));
}

#[test]
fn episodic_manifest_req_contract_matches_rejects_non_matching_values() {
    for value in ["0.1.0", "deadbeef", "0.2.3"] {
        assert!(
            !episodic_manifest_req_contract_matches(value, ""),
            "expected unsupported manifest req: {value}"
        );
    }
}

#[test]
fn episodic_lock_version_contract_matches_checks_absence() {
    assert!(episodic_lock_version_contract_matches(""));
    assert!(!episodic_lock_version_contract_matches("0.2.0"));
    assert!(!episodic_lock_version_contract_matches("invalid"));
}

#[test]
fn contract_integrity_gate_fails_when_core_crate_missing() {
    let temp = tempdir().expect("tempdir");
    let decision = evaluate_contract_integrity_gate(temp.path());
    assert!(!decision.passed);
    let details = parse_gate_details(&decision);
    match details {
        ReleaseGateDetails::ContractIntegrity(value) => {
            assert_eq!(
                value.episodic_semver_probe.error.as_deref(),
                Some("missing_axiomsync_crate")
            );
        }
        other => panic!("expected contract_integrity details, got {other:?}"),
    }
}

#[test]
fn contract_integrity_gate_passes_when_contract_probe_succeeds() {
    let temp = tempdir().expect("tempdir");
    write_contract_gate_workspace_fixture(temp.path(), "", None);

    let output = format!("running 1 test\ntest {CONTRACT_EXECUTION_TEST_NAME} ... ok\n");
    let episodic_output = format!("running 1 test\ntest {EPISODIC_API_PROBE_TEST_NAME} ... ok\n");
    let ontology_output =
        format!("running 1 test\ntest {ONTOLOGY_CONTRACT_PROBE_TEST_NAME} ... ok\n");
    let decision = with_workspace_command_mocks(
        &[
            (
                "cargo",
                &[
                    "test",
                    "-p",
                    "axiomsync",
                    CONTRACT_EXECUTION_TEST_NAME,
                    "--",
                    "--exact",
                ],
                true,
                &output,
            ),
            (
                "cargo",
                &[
                    "test",
                    "-p",
                    "axiomsync",
                    EPISODIC_API_PROBE_TEST_NAME,
                    "--",
                    "--exact",
                ],
                true,
                &episodic_output,
            ),
            (
                "git",
                &["rev-parse", "--verify", "HEAD~1"],
                true,
                "abc123\n",
            ),
            (
                "git",
                &[
                    "show",
                    "HEAD~1:crates/axiomsync/src/client/tests/relation_trace_logs.rs",
                ],
                true,
                PROMPT_SIGNATURE_POLICY_HEAD_OLD,
            ),
            (
                "git",
                &[
                    "show",
                    "HEAD:crates/axiomsync/src/client/tests/relation_trace_logs.rs",
                ],
                true,
                PROMPT_SIGNATURE_POLICY_HEAD_OLD,
            ),
            (
                "cargo",
                &[
                    "test",
                    "-p",
                    "axiomsync",
                    ONTOLOGY_CONTRACT_PROBE_TEST_NAME,
                    "--",
                    "--exact",
                ],
                true,
                &ontology_output,
            ),
        ],
        || evaluate_contract_integrity_gate(temp.path()),
    );
    assert!(decision.passed, "{:?}", decision.details);
    let details = parse_gate_details(&decision);
    match details {
        ReleaseGateDetails::ContractIntegrity(value) => {
            assert_eq!(value.contract_probe.test_name, CONTRACT_EXECUTION_TEST_NAME);
            assert_eq!(
                value.episodic_api_probe.test_name,
                EPISODIC_API_PROBE_TEST_NAME
            );
            assert!(value.episodic_semver_probe.passed);
            assert_eq!(value.policy.required_minor, EPISODIC_REQUIRED_MINOR);
            assert_eq!(
                value.policy.required_manifest_path,
                EPISODIC_REQUIRED_MANIFEST_PATH
            );
            assert_eq!(
                value.policy.required_workspace_member,
                EPISODIC_REQUIRED_WORKSPACE_MEMBER
            );
            assert_eq!(
                value.policy.required_lock_source,
                EPISODIC_REQUIRED_LOCK_SOURCE
            );
            assert!(
                value
                    .ontology_policy
                    .as_ref()
                    .is_some_and(|policy| policy.required_schema_version == 1)
            );
            assert!(
                value
                    .ontology_probe
                    .as_ref()
                    .is_some_and(|probe| probe.passed)
            );
        }
        other => panic!("expected contract_integrity details, got {other:?}"),
    }
}

#[test]
fn contract_integrity_gate_fails_when_contract_probe_output_does_not_match() {
    let temp = tempdir().expect("tempdir");
    write_contract_gate_workspace_fixture(temp.path(), "", None);

    let decision = with_workspace_command_mocks(
        &[
            (
                "cargo",
                &[
                    "test",
                    "-p",
                    "axiomsync",
                    CONTRACT_EXECUTION_TEST_NAME,
                    "--",
                    "--exact",
                ],
                true,
                "running 1 test\ntest some_other_test ... ok\n",
            ),
            (
                "cargo",
                &[
                    "test",
                    "-p",
                    "axiomsync",
                    EPISODIC_API_PROBE_TEST_NAME,
                    "--",
                    "--exact",
                ],
                true,
                "running 1 test\ntest client::tests::relation_trace_logs::episodic_api_probe_validates_om_contract ... ok\n",
            ),
            (
                "git",
                &["rev-parse", "--verify", "HEAD~1"],
                true,
                "abc123\n",
            ),
            (
                "git",
                &[
                    "show",
                    "HEAD~1:crates/axiomsync/src/client/tests/relation_trace_logs.rs",
                ],
                true,
                PROMPT_SIGNATURE_POLICY_HEAD_OLD,
            ),
            (
                "git",
                &[
                    "show",
                    "HEAD:crates/axiomsync/src/client/tests/relation_trace_logs.rs",
                ],
                true,
                PROMPT_SIGNATURE_POLICY_HEAD_OLD,
            ),
            (
                "cargo",
                &[
                    "test",
                    "-p",
                    "axiomsync",
                    ONTOLOGY_CONTRACT_PROBE_TEST_NAME,
                    "--",
                    "--exact",
                ],
                true,
                "running 1 test\ntest ontology::validate::tests::ontology_contract_probe_default_schema_is_compilable ... ok\n",
            ),
        ],
        || evaluate_contract_integrity_gate(temp.path()),
    );
    assert!(!decision.passed);
    let details = parse_gate_details(&decision);
    match details {
        ReleaseGateDetails::ContractIntegrity(value) => {
            assert!(!value.contract_probe.matched);
        }
        other => panic!("expected contract_integrity details, got {other:?}"),
    }
}

#[test]
fn contract_integrity_gate_fails_when_episodic_dependency_uses_wrong_path() {
    let temp = tempdir().expect("tempdir");
    write_contract_gate_workspace_fixture(temp.path(), "episodic = \"0.2.3\"", None);

    let decision = with_workspace_command_mocks(
        &[
            (
                "cargo",
                &[
                    "test",
                    "-p",
                    "axiomsync",
                    CONTRACT_EXECUTION_TEST_NAME,
                    "--",
                    "--exact",
                ],
                true,
                "running 1 test\ntest client::tests::relation_trace_logs::contract_execution_probe_validates_core_algorithms ... ok\n",
            ),
            (
                "cargo",
                &[
                    "test",
                    "-p",
                    "axiomsync",
                    EPISODIC_API_PROBE_TEST_NAME,
                    "--",
                    "--exact",
                ],
                true,
                "running 1 test\ntest client::tests::relation_trace_logs::episodic_api_probe_validates_om_contract ... ok\n",
            ),
            (
                "git",
                &["rev-parse", "--verify", "HEAD~1"],
                true,
                "abc123\n",
            ),
            (
                "git",
                &[
                    "show",
                    "HEAD~1:crates/axiomsync/src/client/tests/relation_trace_logs.rs",
                ],
                true,
                PROMPT_SIGNATURE_POLICY_HEAD_OLD,
            ),
            (
                "git",
                &[
                    "show",
                    "HEAD:crates/axiomsync/src/client/tests/relation_trace_logs.rs",
                ],
                true,
                PROMPT_SIGNATURE_POLICY_HEAD_OLD,
            ),
            (
                "cargo",
                &[
                    "test",
                    "-p",
                    "axiomsync",
                    ONTOLOGY_CONTRACT_PROBE_TEST_NAME,
                    "--",
                    "--exact",
                ],
                true,
                "running 1 test\ntest ontology::validate::tests::ontology_contract_probe_default_schema_is_compilable ... ok\n",
            ),
        ],
        || evaluate_contract_integrity_gate(temp.path()),
    );
    assert!(!decision.passed);
    let details = parse_gate_details(&decision);
    match details {
        ReleaseGateDetails::ContractIntegrity(value) => {
            assert_eq!(
                value.episodic_semver_probe.manifest_req,
                Some("0.2.3".to_string())
            );
            assert_eq!(value.episodic_semver_probe.manifest_req_ok, Some(false));
            assert_eq!(value.episodic_semver_probe.manifest_source_ok, Some(false));
        }
        other => panic!("expected contract_integrity details, got {other:?}"),
    }
}

#[test]
fn contract_integrity_gate_fails_when_prompt_contract_signature_changes_without_tuple_bump() {
    let temp = tempdir().expect("tempdir");
    write_contract_gate_workspace_fixture(temp.path(), "", None);

    let decision = with_workspace_command_mocks(
        &[
            (
                "cargo",
                &[
                    "test",
                    "-p",
                    "axiomsync",
                    CONTRACT_EXECUTION_TEST_NAME,
                    "--",
                    "--exact",
                ],
                true,
                "running 1 test\ntest client::tests::relation_trace_logs::contract_execution_probe_validates_core_algorithms ... ok\n",
            ),
            (
                "cargo",
                &[
                    "test",
                    "-p",
                    "axiomsync",
                    EPISODIC_API_PROBE_TEST_NAME,
                    "--",
                    "--exact",
                ],
                true,
                "running 1 test\ntest client::tests::relation_trace_logs::episodic_api_probe_validates_om_contract ... ok\n",
            ),
            (
                "git",
                &["rev-parse", "--verify", "HEAD~1"],
                true,
                "abc123\n",
            ),
            (
                "git",
                &[
                    "show",
                    "HEAD~1:crates/axiomsync/src/client/tests/relation_trace_logs.rs",
                ],
                true,
                PROMPT_SIGNATURE_POLICY_HEAD_OLD,
            ),
            (
                "git",
                &[
                    "show",
                    "HEAD:crates/axiomsync/src/client/tests/relation_trace_logs.rs",
                ],
                true,
                PROMPT_SIGNATURE_POLICY_HEAD_CHANGED_WITHOUT_BUMP,
            ),
            (
                "cargo",
                &[
                    "test",
                    "-p",
                    "axiomsync",
                    ONTOLOGY_CONTRACT_PROBE_TEST_NAME,
                    "--",
                    "--exact",
                ],
                true,
                "running 1 test\ntest ontology::validate::tests::ontology_contract_probe_default_schema_is_compilable ... ok\n",
            ),
        ],
        || evaluate_contract_integrity_gate(temp.path()),
    );
    assert!(!decision.passed);
    let details = parse_gate_details(&decision);
    match details {
        ReleaseGateDetails::ContractIntegrity(value) => {
            assert!(!value.episodic_api_probe.passed);
            assert!(value.episodic_api_probe.output_excerpt.contains(
                "prompt_contract_signature_changed_without_contract_or_protocol_version_bump"
            ));
        }
        other => panic!("expected contract_integrity details, got {other:?}"),
    }
}

#[test]
fn contract_integrity_gate_allows_prompt_contract_signature_change_with_version_bump() {
    let temp = tempdir().expect("tempdir");
    write_contract_gate_workspace_fixture(temp.path(), "", None);

    let decision = with_workspace_command_mocks(
        &[
            (
                "cargo",
                &[
                    "test",
                    "-p",
                    "axiomsync",
                    CONTRACT_EXECUTION_TEST_NAME,
                    "--",
                    "--exact",
                ],
                true,
                "running 1 test\ntest client::tests::relation_trace_logs::contract_execution_probe_validates_core_algorithms ... ok\n",
            ),
            (
                "cargo",
                &[
                    "test",
                    "-p",
                    "axiomsync",
                    EPISODIC_API_PROBE_TEST_NAME,
                    "--",
                    "--exact",
                ],
                true,
                "running 1 test\ntest client::tests::relation_trace_logs::episodic_api_probe_validates_om_contract ... ok\n",
            ),
            (
                "git",
                &["rev-parse", "--verify", "HEAD~1"],
                true,
                "abc123\n",
            ),
            (
                "git",
                &[
                    "show",
                    "HEAD~1:crates/axiomsync/src/client/tests/relation_trace_logs.rs",
                ],
                true,
                PROMPT_SIGNATURE_POLICY_HEAD_OLD,
            ),
            (
                "git",
                &[
                    "show",
                    "HEAD:crates/axiomsync/src/client/tests/relation_trace_logs.rs",
                ],
                true,
                PROMPT_SIGNATURE_POLICY_HEAD_CHANGED_WITH_BUMP,
            ),
            (
                "cargo",
                &[
                    "test",
                    "-p",
                    "axiomsync",
                    ONTOLOGY_CONTRACT_PROBE_TEST_NAME,
                    "--",
                    "--exact",
                ],
                true,
                "running 1 test\ntest ontology::validate::tests::ontology_contract_probe_default_schema_is_compilable ... ok\n",
            ),
        ],
        || evaluate_contract_integrity_gate(temp.path()),
    );
    assert!(decision.passed, "{:?}", decision.details);
}

#[test]
fn contract_integrity_gate_allows_prompt_signature_policy_when_head_parent_is_unavailable() {
    let temp = tempdir().expect("tempdir");
    write_contract_gate_workspace_fixture(temp.path(), "", None);

    let decision = with_workspace_command_mocks(
        &[
            (
                "cargo",
                &[
                    "test",
                    "-p",
                    "axiomsync",
                    CONTRACT_EXECUTION_TEST_NAME,
                    "--",
                    "--exact",
                ],
                true,
                "running 1 test\ntest client::tests::relation_trace_logs::contract_execution_probe_validates_core_algorithms ... ok\n",
            ),
            (
                "cargo",
                &[
                    "test",
                    "-p",
                    "axiomsync",
                    EPISODIC_API_PROBE_TEST_NAME,
                    "--",
                    "--exact",
                ],
                true,
                "running 1 test\ntest client::tests::relation_trace_logs::episodic_api_probe_validates_om_contract ... ok\n",
            ),
            (
                "git",
                &["rev-parse", "--verify", "HEAD~1"],
                false,
                "fatal: ambiguous argument 'HEAD~1'\n",
            ),
            (
                "git",
                &[
                    "show",
                    "HEAD:crates/axiomsync/src/client/tests/relation_trace_logs.rs",
                ],
                true,
                PROMPT_SIGNATURE_POLICY_HEAD_OLD,
            ),
            (
                "cargo",
                &[
                    "test",
                    "-p",
                    "axiomsync",
                    ONTOLOGY_CONTRACT_PROBE_TEST_NAME,
                    "--",
                    "--exact",
                ],
                true,
                "running 1 test\ntest ontology::validate::tests::ontology_contract_probe_default_schema_is_compilable ... ok\n",
            ),
        ],
        || evaluate_contract_integrity_gate(temp.path()),
    );
    assert!(decision.passed, "{:?}", decision.details);
}

#[test]
fn contract_integrity_gate_allows_prompt_signature_policy_when_head_parent_path_was_renamed() {
    let temp = tempdir().expect("tempdir");
    write_contract_gate_workspace_fixture(temp.path(), "", None);

    let decision = with_workspace_command_mocks(
        &[
            (
                "cargo",
                &[
                    "test",
                    "-p",
                    "axiomsync",
                    CONTRACT_EXECUTION_TEST_NAME,
                    "--",
                    "--exact",
                ],
                true,
                "running 1 test\ntest client::tests::relation_trace_logs::contract_execution_probe_validates_core_algorithms ... ok\n",
            ),
            (
                "cargo",
                &[
                    "test",
                    "-p",
                    "axiomsync",
                    EPISODIC_API_PROBE_TEST_NAME,
                    "--",
                    "--exact",
                ],
                true,
                "running 1 test\ntest client::tests::relation_trace_logs::episodic_api_probe_validates_om_contract ... ok\n",
            ),
            (
                "git",
                &["rev-parse", "--verify", "HEAD~1"],
                true,
                "abc123\n",
            ),
            (
                "git",
                &[
                    "show",
                    "HEAD~1:crates/axiomsync/src/client/tests/relation_trace_logs.rs",
                ],
                false,
                "fatal: path 'crates/axiomsync/src/client/tests/relation_trace_logs.rs' exists on disk, but not in 'HEAD~1'\n",
            ),
            (
                "git",
                &[
                    "show",
                    "HEAD:crates/axiomsync/src/client/tests/relation_trace_logs.rs",
                ],
                true,
                PROMPT_SIGNATURE_POLICY_HEAD_OLD,
            ),
            (
                "cargo",
                &[
                    "test",
                    "-p",
                    "axiomsync",
                    ONTOLOGY_CONTRACT_PROBE_TEST_NAME,
                    "--",
                    "--exact",
                ],
                true,
                "running 1 test\ntest ontology::validate::tests::ontology_contract_probe_default_schema_is_compilable ... ok\n",
            ),
        ],
        || evaluate_contract_integrity_gate(temp.path()),
    );
    assert!(decision.passed, "{:?}", decision.details);
}

#[test]
fn contract_integrity_gate_reads_current_prompt_signature_policy_from_workspace_when_head_lags() {
    let temp = tempdir().expect("tempdir");
    write_contract_gate_workspace_fixture(temp.path(), "", None);
    let relation_trace_logs = temp
        .path()
        .join("crates")
        .join("axiomsync")
        .join("src")
        .join("client")
        .join("tests")
        .join("relation_trace_logs.rs");
    fs::create_dir_all(relation_trace_logs.parent().expect("parent")).expect("mkdir tests");
    fs::write(&relation_trace_logs, PROMPT_SIGNATURE_POLICY_HEAD_OLD).expect("write relation logs");

    let decision = with_workspace_command_mocks(
        &[
            (
                "cargo",
                &[
                    "test",
                    "-p",
                    "axiomsync",
                    CONTRACT_EXECUTION_TEST_NAME,
                    "--",
                    "--exact",
                ],
                true,
                "running 1 test\ntest client::tests::relation_trace_logs::contract_execution_probe_validates_core_algorithms ... ok\n",
            ),
            (
                "cargo",
                &[
                    "test",
                    "-p",
                    "axiomsync",
                    EPISODIC_API_PROBE_TEST_NAME,
                    "--",
                    "--exact",
                ],
                true,
                "running 1 test\ntest client::tests::relation_trace_logs::episodic_api_probe_validates_om_contract ... ok\n",
            ),
            (
                "git",
                &["rev-parse", "--verify", "HEAD~1"],
                true,
                "abc123\n",
            ),
            (
                "git",
                &[
                    "show",
                    "HEAD~1:crates/axiomsync/src/client/tests/relation_trace_logs.rs",
                ],
                false,
                "fatal: path 'crates/axiomsync/src/client/tests/relation_trace_logs.rs' exists on disk, but not in 'HEAD~1'\n",
            ),
            (
                "git",
                &[
                    "show",
                    "HEAD:crates/axiomsync/src/client/tests/relation_trace_logs.rs",
                ],
                false,
                "fatal: path 'crates/axiomsync/src/client/tests/relation_trace_logs.rs' exists on disk, but not in 'HEAD'\n",
            ),
            (
                "cargo",
                &[
                    "test",
                    "-p",
                    "axiomsync",
                    ONTOLOGY_CONTRACT_PROBE_TEST_NAME,
                    "--",
                    "--exact",
                ],
                true,
                "running 1 test\ntest ontology::validate::tests::ontology_contract_probe_default_schema_is_compilable ... ok\n",
            ),
        ],
        || evaluate_contract_integrity_gate(temp.path()),
    );
    assert!(decision.passed, "{:?}", decision.details);
}
