//! Cross-impl conformance for the `Escalation` trait + builder dependency
//! validation for gates that may require approval.

use klieo_bus_memory::MemoryBus;
use klieo_ops::escalation::JobQueueEscalation;
use klieo_ops::test_fixtures::run_escalation_conformance;

#[tokio::test]
async fn jobqueue_escalation_passes_conformance() {
    let bus = MemoryBus::new();
    let esc = JobQueueEscalation::new(bus.jobs.clone());
    run_escalation_conformance(esc).await;
}

// ── Builder dependency-validation tests ─────────────────────────────────────

use async_trait::async_trait;
use klieo_core::{Agent, AgentContext, ToolDef};
use klieo_ops::error::BuildError;
use klieo_ops::gates::{Gate, GateDecision, GateRequest};
use klieo_ops::{OpsRuntime, TenantId, TenantResolver};
use std::sync::Arc;

struct ApprovalDemandingGate;

#[async_trait]
impl Gate for ApprovalDemandingGate {
    async fn evaluate(&self, _req: GateRequest) -> GateDecision {
        GateDecision::RequireApproval {
            ticket: "stub".into(),
            quorum: 2,
        }
    }

    fn name(&self) -> &'static str {
        "ApprovalDemandingGate"
    }

    fn may_require_approval(&self) -> bool {
        true
    }
}

struct FixedTenant;

#[async_trait]
impl TenantResolver for FixedTenant {
    async fn resolve(&self) -> Option<TenantId> {
        Some(TenantId("t".into()))
    }
}

struct EchoAgent;

#[async_trait]
impl Agent for EchoAgent {
    type Input = String;
    type Output = String;
    type Error = std::io::Error;

    fn name(&self) -> &str {
        "echo"
    }

    fn system_prompt(&self) -> &str {
        ""
    }

    fn tools(&self) -> &[ToolDef] {
        &[]
    }

    async fn run(&self, _ctx: AgentContext, input: String) -> Result<String, std::io::Error> {
        Ok(input)
    }
}

#[test]
fn builder_without_escalation_but_with_approval_gate_returns_missing_dependency() {
    let result = OpsRuntime::builder()
        .tenant_resolver(Arc::new(FixedTenant))
        .gate(Arc::new(ApprovalDemandingGate))
        .spawn(EchoAgent);
    let err = result.err().expect("must error");
    assert!(
        matches!(err, BuildError::MissingDependency { ref missing, .. } if missing == "escalation"),
        "expected MissingDependency {{ missing: \"escalation\" }}, got: {err:?}",
    );
}

#[test]
fn builder_with_escalation_and_approval_gate_succeeds() {
    let bus = MemoryBus::new();
    let esc = Arc::new(JobQueueEscalation::new(bus.jobs.clone()));
    let result = OpsRuntime::builder()
        .tenant_resolver(Arc::new(FixedTenant))
        .escalation(esc)
        .gate(Arc::new(ApprovalDemandingGate))
        .spawn(EchoAgent);
    assert!(
        result.is_ok(),
        "builder must succeed when escalation + approval-gate both registered",
    );
}

#[test]
fn builder_with_non_approval_gate_and_no_escalation_succeeds() {
    use klieo_ops::gates::AllowlistGate;

    let gate = AllowlistGate::new(&["tool_a"]);
    let result = OpsRuntime::builder()
        .tenant_resolver(Arc::new(FixedTenant))
        .gate(Arc::new(gate))
        .spawn(EchoAgent);
    assert!(
        result.is_ok(),
        "non-approval gate without escalation must not block builder",
    );
}
