use decapod::core::capsule_policy::CapsulePolicyBinding;
use decapod::core::context_capsule::{
    ContextCapsuleSnippet, ContextCapsuleSource, DeterministicContextCapsule, write_context_capsule,
};
use decapod::core::{workspace, workunit};
use tempfile::tempdir;

fn write_manifest(
    root: &std::path::Path,
    task_id: &str,
    status: workunit::WorkUnitStatus,
    state_refs: Vec<&str>,
    proof_plan: Vec<&str>,
    proof_results: Vec<(&str, &str)>,
) {
    let manifest = workunit::WorkUnitManifest {
        task_id: task_id.to_string(),
        intent_ref: "intent://demo".to_string(),
        spec_refs: vec![],
        state_refs: state_refs.into_iter().map(|s| s.to_string()).collect(),
        proof_plan: proof_plan.into_iter().map(|s| s.to_string()).collect(),
        proof_results: proof_results
            .into_iter()
            .map(|(gate, status)| workunit::WorkUnitProofResult {
                gate: gate.to_string(),
                status: status.to_string(),
                artifact_ref: None,
            })
            .collect(),
        status,
    };

    workunit::write_workunit(root, &manifest).expect("write workunit manifest");
}

fn write_capsule(root: &std::path::Path, task_id: &str) {
    let capsule = DeterministicContextCapsule {
        schema_version: "1.1.0".to_string(),
        topic: "publish".to_string(),
        scope: "interfaces".to_string(),
        task_id: Some(task_id.to_string()),
        workunit_id: None,
        sources: vec![ContextCapsuleSource {
            path: "interfaces/PLAN_GOVERNED_EXECUTION".to_string(),
            section: "Contract".to_string(),
        }],
        snippets: vec![ContextCapsuleSnippet {
            source_path: "interfaces/PLAN_GOVERNED_EXECUTION".to_string(),
            text: "promotion path is proof-gated".to_string(),
        }],
        policy: CapsulePolicyBinding {
            risk_tier: "medium".to_string(),
            policy_hash: "abc123".to_string(),
            policy_version: "jit-capsule-policy-v1".to_string(),
            policy_path: ".decapod/generated/policy/context_capsule_policy.json".to_string(),
            repo_revision: "UNBORN:master".to_string(),
        },
        capsule_hash: String::new(),
    };
    write_context_capsule(root, &capsule).expect("write capsule");
}

#[test]
fn publish_gate_skips_when_branch_has_no_task_ids() {
    let dir = tempdir().expect("tempdir");
    let result = workspace::verify_workunit_gate_for_publish(dir.path(), "feature/no-task-id");
    assert!(result.is_ok(), "expected no-op pass for non-task branch");
}

#[test]
fn publish_gate_fails_when_branch_task_manifest_missing() {
    let dir = tempdir().expect("tempdir");
    let err = workspace::verify_workunit_gate_for_publish(dir.path(), "agent/unknown/r_01ABCXYZ")
        .expect_err("expected missing workunit manifest failure");
    let msg = err.to_string();
    assert!(
        msg.contains("missing required workunit manifest"),
        "unexpected error message: {msg}"
    );
}

#[test]
fn publish_gate_fails_when_branch_task_not_verified() {
    let dir = tempdir().expect("tempdir");
    write_manifest(
        dir.path(),
        "test_000001",
        workunit::WorkUnitStatus::Claimed,
        vec![],
        vec!["validate_passes"],
        vec![("validate_passes", "pass")],
    );

    let err = workspace::verify_workunit_gate_for_publish(dir.path(), "agent/codex/test_000001")
        .expect_err("expected status gate failure");
    let msg = err.to_string();
    assert!(
        msg.contains("is not VERIFIED"),
        "unexpected error message: {msg}"
    );
}

#[test]
fn publish_gate_passes_when_branch_task_verified() {
    let dir = tempdir().expect("tempdir");
    write_capsule(dir.path(), "test_000002");
    write_manifest(
        dir.path(),
        "test_000002",
        workunit::WorkUnitStatus::Verified,
        vec![".decapod/generated/context/test_000002.json"],
        vec!["validate_passes", "test:cargo test --all"],
        vec![
            ("validate_passes", "pass"),
            ("test:cargo test --all", "pass"),
        ],
    );

    let result = workspace::verify_workunit_gate_for_publish(dir.path(), "agent/codex/test_000002");
    assert!(result.is_ok(), "expected verified branch task to pass");
}

#[test]
fn publish_gate_fails_when_verified_task_missing_capsule_lineage() {
    let dir = tempdir().expect("tempdir");
    write_manifest(
        dir.path(),
        "test_000003",
        workunit::WorkUnitStatus::Verified,
        vec![".decapod/generated/context/test_000003.json"],
        vec!["validate_passes"],
        vec![("validate_passes", "pass")],
    );

    let err = workspace::verify_workunit_gate_for_publish(dir.path(), "agent/codex/test_000003")
        .expect_err("expected missing capsule lineage failure");
    let msg = err.to_string();
    assert!(
        msg.contains("WORKUNIT_CAPSULE_POLICY_LINEAGE_MISSING"),
        "unexpected error message: {msg}"
    );
}

#[test]
fn publish_gate_fails_when_verified_task_capsule_state_ref_missing() {
    let dir = tempdir().expect("tempdir");
    write_capsule(dir.path(), "test_000004");
    write_manifest(
        dir.path(),
        "test_000004",
        workunit::WorkUnitStatus::Verified,
        vec![],
        vec!["validate_passes"],
        vec![("validate_passes", "pass")],
    );

    let err = workspace::verify_workunit_gate_for_publish(dir.path(), "agent/codex/test_000004")
        .expect_err("expected missing capsule state_ref failure");
    let msg = err.to_string();
    assert!(
        msg.contains("WORKUNIT_CAPSULE_POLICY_LINEAGE_STATE_REF_MISSING"),
        "unexpected error message: {msg}"
    );
}
