//! Boundary traits and data types for the signal ledger.
//!
//! Contains the `WalWriter` trait (the boundary between the signals subsystem
//! and the WAL), its no-op test implementation, and the `EntitySignalEntry`
//! combined hot + warm state container.

use std::fmt;

use crate::schema::{EntityId, Timestamp};

use super::super::SignalTypeId;
use super::super::hot::HotSignalState;
use super::super::warm::BucketedCounter;

// ── WAL boundary ─────────────────────────────────────────────────────────────

/// Trait boundary for WAL integration.
///
/// `m1p2` provides the real implementation. `m1p4` tests use `NoopWalWriter`.
/// The `SignalLedger` calls `append_signal()` before updating in-memory state,
/// ensuring WAL-first durability semantics.
pub trait WalWriter: Send + Sync {
    /// Append a signal event to the WAL.
    ///
    /// Returns `Ok(())` when the event is durably committed. After this
    /// returns, in-memory state is updated.
    ///
    /// # Errors
    ///
    /// Returns `TidalError::Durability` if the WAL write fails.
    fn append_signal(
        &self,
        signal_type_id: SignalTypeId,
        entity_id: EntityId,
        weight: f64,
        timestamp: Timestamp,
    ) -> crate::Result<()>;
}

/// No-op WAL writer for testing. Always succeeds without writing anything.
pub struct NoopWalWriter;

impl WalWriter for NoopWalWriter {
    fn append_signal(
        &self,
        _signal_type_id: SignalTypeId,
        _entity_id: EntityId,
        _weight: f64,
        _timestamp: Timestamp,
    ) -> crate::Result<()> {
        Ok(())
    }
}

impl fmt::Debug for NoopWalWriter {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("NoopWalWriter")
    }
}

// ── Entry ─────────────────────────────────────────────────────────────────────

/// Combined hot-tier and warm-tier state for one entity-signal pair.
pub struct EntitySignalEntry {
    /// Running exponentially-decayed score (hot tier).
    pub hot: HotSignalState,
    /// Bucketed windowed event counters (warm tier).
    pub warm: BucketedCounter,
}

impl fmt::Debug for EntitySignalEntry {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("EntitySignalEntry")
            .field("hot", &self.hot)
            .field("warm", &self.warm)
            .finish()
    }
}
