//! `Supervisor` trait + supporting types.

use crate::types::{AgentId, AgentMeta, KillReason, KillTrigger, RuntimeState};
use async_trait::async_trait;
use futures_core::stream::BoxStream;
use thiserror::Error;

/// Per-agent health status reported by the supervised agent.
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum Status {
    /// Idle / available.
    Idle,
    /// Busy processing a step.
    Busy,
    /// Drained, will not accept new work.
    Drained,
}

/// Supervisor event stream payload.
#[derive(Clone, Debug)]
#[non_exhaustive]
pub enum SupervisorEvent {
    /// New agent registered.
    Registered(AgentId),
    /// Heartbeat received.
    Heartbeat(AgentId),
    /// Agent missed its stall threshold.
    StallDetected {
        /// Affected agent.
        agent: AgentId,
        /// Heartbeats missed since last beat.
        missed: u32,
    },
    /// Kill-switch tripped.
    KillSwitchTripped {
        /// Reason text.
        reason: String,
    },
    /// Runtime state transition.
    StateChanged(RuntimeState),
}

/// Errors raised by supervisor calls.
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum SupervisorError {
    /// KvStore I/O failed.
    #[error("storage unavailable: {0}")]
    Storage(String),
    /// Caller-supplied id is unknown to the supervisor.
    #[error("unknown agent: {0}")]
    UnknownAgent(AgentId),
    /// Other internal.
    #[error("internal: {0}")]
    Internal(String),
}

/// Supervisor primitive.
#[async_trait]
pub trait Supervisor: Send + Sync {
    /// Register a new agent with the supervisor.
    async fn register(&self, agent: AgentId, meta: AgentMeta) -> Result<(), SupervisorError>;

    /// Send a heartbeat for `agent`. Refreshes the TTL key.
    async fn heartbeat(&self, agent: AgentId) -> Result<(), SupervisorError>;

    /// Report a non-heartbeat status for `agent`.
    async fn report(&self, agent: AgentId, status: Status) -> Result<(), SupervisorError>;

    /// Subscribe to the supervisor event stream.
    async fn watch(&self) -> BoxStream<'static, SupervisorEvent>;

    /// Trip the kill-switch.
    async fn trip_kill_switch(
        &self,
        reason: KillReason,
        trigger: KillTrigger,
    ) -> Result<(), SupervisorError>;

    /// Current runtime state.
    async fn runtime_state(&self) -> RuntimeState;
}
