//! Shared audit sink: emit OpsEvent into klieo_core::EpisodicMemory
//! with tracing::warn on failure. Used by primitive impls
//! (JobQueueEscalation, KvWorkLog, KvHandoff) and the SupervisedAgent
//! runtime wrappers (GovernedLlmClient, GatedToolInvoker).

use crate::emit::emit_ops_event;
use crate::ops_event::OpsEvent;
use klieo_core::{ids::RunId, memory::EpisodicMemory};
use std::sync::Arc;

/// Per-primitive audit sink. Holds an `EpisodicMemory` + `RunId` and
/// emits `OpsEvent`s under that run. Failures surface via
/// `tracing::warn!(target = log_target, …)` rather than panicking or
/// silently swallowing — closes the "audit-emit must not be swallowed"
/// rule from Phase A.
#[derive(Clone)]
pub struct OpsAuditSink {
    episodic: Arc<dyn EpisodicMemory>,
    run_id: RunId,
    log_target: &'static str,
}

impl OpsAuditSink {
    /// Build a sink bound to the supplied EpisodicMemory + RunId. The
    /// `log_target` is the tracing target used for emit-failure warnings
    /// (e.g. "klieo.ops.worklog.audit", "klieo.ops.handoff.audit").
    #[must_use]
    pub fn new(episodic: Arc<dyn EpisodicMemory>, run_id: RunId, log_target: &'static str) -> Self {
        Self {
            episodic,
            run_id,
            log_target,
        }
    }

    /// Emit `event`. On failure, log a warning including the configured
    /// `log_target` label so operators can identify the emitting primitive.
    pub async fn emit(&self, event: OpsEvent) {
        if let Err(err) = emit_ops_event(&*self.episodic, self.run_id, event).await {
            tracing::warn!(
                target: "klieo.ops.audit",
                primitive = self.log_target,
                error = %err,
                "audit emit failed; episode not recorded"
            );
        }
    }
}
