//! L1 tests for [`HealthUseCase`] driven entirely through **mocked/fake ports**
//! (`FixedClock`, `SeqIdGenerator`, a spy `EventBus`), exercising DI without any
//! real I/O (README "Domaine/application testés sans I/O").

use std::sync::{Arc, Mutex};

use application::{HealthInput, HealthUseCase};
use domain::events::DomainEvent;
use domain::ports::{Clock, EventBus, EventStream, IdGenerator};
use uuid::Uuid;

/// A clock that always returns the same configured instant.
struct FixedClock(i64);
impl Clock for FixedClock {
    fn now_millis(&self) -> i64 {
        self.0
    }
}

/// An id generator yielding a deterministic, predefined UUID.
struct SeqIdGenerator(Uuid);
impl IdGenerator for SeqIdGenerator {
    fn new_uuid(&self) -> Uuid {
        self.0
    }
}

/// A spy event bus capturing every published event for assertions.
#[derive(Default)]
struct SpyEventBus {
    published: Mutex<Vec<DomainEvent>>,
}
impl EventBus for SpyEventBus {
    fn publish(&self, event: DomainEvent) {
        self.published.lock().unwrap().push(event);
    }
    fn subscribe(&self) -> EventStream {
        Box::new(std::iter::empty())
    }
}

fn fixed_uuid() -> Uuid {
    Uuid::parse_str("11111111-2222-3333-4444-555555555555").unwrap()
}

#[test]
fn health_report_reflects_injected_ports_and_input() {
    let clock = Arc::new(FixedClock(1_700_000_000_123));
    let ids = Arc::new(SeqIdGenerator(fixed_uuid()));
    let bus = Arc::new(SpyEventBus::default());

    let uc = HealthUseCase::new(clock, ids, Arc::clone(&bus) as Arc<dyn EventBus>);

    let report = uc
        .execute(HealthInput {
            note: Some("ping".to_owned()),
        })
        .expect("health never errs");

    assert!(report.alive);
    assert_eq!(report.time_millis, 1_700_000_000_123);
    assert_eq!(report.correlation_id, fixed_uuid().to_string());
    assert_eq!(report.note.as_deref(), Some("ping"));
    assert_eq!(report.version, env!("CARGO_PKG_VERSION"));
}

#[test]
fn health_publishes_exactly_one_domain_event() {
    let clock = Arc::new(FixedClock(0));
    let ids = Arc::new(SeqIdGenerator(fixed_uuid()));
    let bus = Arc::new(SpyEventBus::default());

    let uc = HealthUseCase::new(clock, ids, Arc::clone(&bus) as Arc<dyn EventBus>);
    uc.execute(HealthInput::default()).unwrap();

    let published = bus.published.lock().unwrap();
    assert_eq!(published.len(), 1, "exactly one smoke event is published");
    match &published[0] {
        DomainEvent::ProjectCreated { project_id } => {
            // The smoke event reuses the correlation id as the (fake) project id.
            assert_eq!(project_id.as_uuid(), fixed_uuid());
        }
        other => panic!("expected ProjectCreated smoke event, got {other:?}"),
    }
}

#[test]
fn health_note_defaults_to_none() {
    let uc = HealthUseCase::new(
        Arc::new(FixedClock(42)),
        Arc::new(SeqIdGenerator(fixed_uuid())),
        Arc::new(SpyEventBus::default()),
    );
    let report = uc.execute(HealthInput::default()).unwrap();
    assert_eq!(report.note, None);
}
