//! [`HealthUseCase`] — a trivial, in-memory use case used to validate the
//! end-to-end wiring (composition root → Tauri command → use case → ports →
//! frontend gateway). It carries its ports as `Arc<dyn Port>`, exactly like
//! every real use case will (ARCHITECTURE §6).
//!
//! It depends only on the utility ports [`Clock`] and [`IdGenerator`], so it
//! exercises dependency injection without needing any I/O adapter.

use std::sync::Arc;

use domain::ports::{Clock, EventBus, IdGenerator};
use domain::DomainEvent;
use domain::ProjectId;

use crate::error::AppError;

/// Input for [`HealthUseCase::execute`].
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct HealthInput {
    /// Optional caller-supplied note echoed back in the report (used by tests
    /// and the frontend smoke check).
    pub note: Option<String>,
}

/// Output of [`HealthUseCase::execute`]: a small health report.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HealthReport {
    /// Application/crate version (`CARGO_PKG_VERSION`).
    pub version: String,
    /// Liveness flag — always `true` if the use case ran.
    pub alive: bool,
    /// Server "now" in epoch milliseconds (from the injected [`Clock`]).
    pub time_millis: i64,
    /// A fresh correlation id (from the injected [`IdGenerator`]).
    pub correlation_id: String,
    /// Echoed caller note, if any.
    pub note: Option<String>,
}

/// Trivial health/ping use case validating the DI + IPC pipeline.
pub struct HealthUseCase {
    clock: Arc<dyn Clock>,
    ids: Arc<dyn IdGenerator>,
    events: Arc<dyn EventBus>,
}

impl HealthUseCase {
    /// Builds the use case from its injected ports.
    #[must_use]
    pub fn new(
        clock: Arc<dyn Clock>,
        ids: Arc<dyn IdGenerator>,
        events: Arc<dyn EventBus>,
    ) -> Self {
        Self { clock, ids, events }
    }

    /// Executes the health check.
    ///
    /// As a side effect it publishes a [`DomainEvent`] on the [`EventBus`] so
    /// the event-relay path is exercised end to end (the relay forwards it to a
    /// Tauri event). A `ProjectCreated`-shaped event is reused here purely as a
    /// no-op smoke signal; no project is actually created.
    ///
    /// # Errors
    /// Returns [`AppError`] — never, in this trivial implementation, but the
    /// signature matches the real use-case contract.
    pub fn execute(&self, input: HealthInput) -> Result<HealthReport, AppError> {
        let correlation = self.ids.new_uuid();

        // Exercise the event-bus relay path with a harmless smoke event.
        self.events.publish(DomainEvent::ProjectCreated {
            project_id: ProjectId::from_uuid(correlation),
        });

        Ok(HealthReport {
            version: env!("CARGO_PKG_VERSION").to_owned(),
            alive: true,
            time_millis: self.clock.now_millis(),
            correlation_id: correlation.to_string(),
            note: input.note,
        })
    }
}
