//! Shared test helpers: deterministic fakes for the `Clock` / `IdGenerator`
//! ports, plus small id constructors. Kept in `tests/` so they never ship in
//! the crate proper.
#![allow(dead_code)]

use std::cell::Cell;

use domain::ports::{Clock, IdGenerator};
use uuid::Uuid;

/// A clock that always returns the same fixed millisecond value.
pub struct FixedClock(pub i64);

impl Clock for FixedClock {
    fn now_millis(&self) -> i64 {
        self.0
    }
}

/// An id generator producing a deterministic, monotonically increasing sequence
/// of UUIDs (`0000...0001`, `0000...0002`, ...).
pub struct SeqIdGenerator {
    next: Cell<u128>,
}

impl SeqIdGenerator {
    #[must_use]
    pub fn new() -> Self {
        Self { next: Cell::new(1) }
    }
}

impl Default for SeqIdGenerator {
    fn default() -> Self {
        Self::new()
    }
}

// `IdGenerator` only requires `&self`, so we use a `Cell` for interior
// mutability. The trait demands `Send + Sync`; `Cell` is `!Sync`, so for the
// test fake we wrap calls behind `&self` single-threaded usage only.
// To satisfy the bound we instead expose a plain method used directly in tests.
impl SeqIdGenerator {
    /// Returns the next UUID in the deterministic sequence.
    pub fn next_uuid(&self) -> Uuid {
        let n = self.next.get();
        self.next.set(n + 1);
        Uuid::from_u128(n)
    }
}

/// A `Send + Sync` deterministic id generator suitable for the `IdGenerator`
/// port (uses an atomic counter).
pub struct AtomicSeqIdGenerator {
    next: std::sync::atomic::AtomicU64,
}

impl AtomicSeqIdGenerator {
    #[must_use]
    pub fn new() -> Self {
        Self {
            next: std::sync::atomic::AtomicU64::new(1),
        }
    }
}

impl Default for AtomicSeqIdGenerator {
    fn default() -> Self {
        Self::new()
    }
}

impl IdGenerator for AtomicSeqIdGenerator {
    fn new_uuid(&self) -> Uuid {
        let n = self.next.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
        Uuid::from_u128(u128::from(n))
    }
}

/// Builds a `NodeId` from a small integer (handy, readable test ids).
#[must_use]
pub fn node(n: u128) -> domain::NodeId {
    domain::NodeId::from_uuid(Uuid::from_u128(n))
}

/// Builds a `SessionId` from a small integer.
#[must_use]
pub fn session(n: u128) -> domain::SessionId {
    domain::SessionId::from_uuid(Uuid::from_u128(n))
}
