//! Event transport abstraction layer
//!
//! This module provides a trait-based abstraction for event sourcing mechanisms,
//! enabling FraiseQL's observer system to work with multiple event transports:
//!
//! - **`PostgresNotify`**: PostgreSQL LISTEN/NOTIFY (low latency, ephemeral)
//! - **`MySQLBridge`**: MySQL polling-based bridge
//! - **`MSSQLBridge`**: SQL Server polling-based bridge
//! - **Nats**: NATS `JetStream` for distributed architectures
//! - **`InMemory`**: Testing and development
//!
//! # Architecture
//!
//! ```text
//! ObserverExecutor
//!     ↓
//! EventTransport trait (Arc<dyn>)
//!     ↓
//! ┌────────────────┬──────────────┬──────────────┬──────────────┬──────────────┐
//! │                │              │              │              │              │
//! PostgresNotify   MySQLBridge    MSSQLBridge    NatsTransport  InMemory
//! (postgres)       (mysql)        (mssql)        (nats)         (testing)
//! ```
//!
//! # Design Decisions
//!
//! - **`Arc<dyn EventTransport>`**: Runtime transport selection without monomorphization bloat
//! - **Stream-based API**: Natural tokio integration with backpressure
//! - **Transport-managed reconnection**: Transports handle retry/backoff internally
//! - **At-least-once delivery**: Transport ACKs after `ObserverExecutor` processes event

use std::pin::Pin;

use async_trait::async_trait;
use futures::Stream;

use crate::{error::Result, event::EntityEvent};

pub mod in_memory;
#[cfg(feature = "postgres")]
pub mod postgres_notify;

#[cfg(feature = "nats")]
pub mod nats;

#[cfg(all(feature = "postgres", feature = "nats"))]
pub mod bridge;

#[cfg(all(feature = "mysql", feature = "nats"))]
pub mod mysql_bridge;

#[cfg(all(feature = "mssql", feature = "nats"))]
pub mod mssql_bridge;

#[cfg(all(feature = "postgres", feature = "nats"))]
pub use bridge::{
    BridgeConfig, ChangeLogEntry, CheckpointStore, PostgresCheckpointStore, PostgresNatsBridge,
};
pub use in_memory::InMemoryTransport;
#[cfg(all(feature = "mssql", feature = "nats"))]
pub use mssql_bridge::{
    MSSQLBridgeConfig, MSSQLChangeLogEntry, MSSQLCheckpointStore, MSSQLNatsBridge, MSSQLPool,
    create_mssql_pool,
};
#[cfg(all(feature = "mysql", feature = "nats"))]
pub use mysql_bridge::{
    MySQLBridgeConfig, MySQLChangeLogEntry, MySQLCheckpointStore, MySQLNatsBridge,
};
#[cfg(feature = "nats")]
pub use nats::{NatsConfig, NatsTransport};
#[cfg(feature = "postgres")]
pub use postgres_notify::PostgresNotifyTransport;

/// Event stream type (async stream of `EntityEvents`)
pub type EventStream = Pin<Box<dyn Stream<Item = Result<EntityEvent>> + Send>>;

/// Core event transport abstraction
///
/// Implementors must:
/// - Handle reconnection/backoff internally
/// - Not crash on transient failures
/// - Emit errors via stream, not panic
/// - ACK messages only after successful processing (at-least-once semantics)
// Reason: designed for use as dyn Trait (Arc<dyn EventTransport>); async_trait ensures Send bounds
// and dyn-compatibility async_trait: dyn-dispatch required; remove when RTN + Send is stable (RFC
// 3425)
#[async_trait]
pub trait EventTransport: Send + Sync {
    /// Subscribe to events matching filter (returns async stream)
    ///
    /// # Guarantees
    /// - Transports must handle reconnection/backoff internally
    /// - Must not crash executor loop on transient failures
    /// - Stream ends on fatal errors (consumers restart loop)
    ///
    /// # ACK Semantics
    /// - `NatsTransport` ACKs only after `ObserverExecutor::process_event()` returns `Ok()`
    /// - At-least-once delivery preserved (redelivery on processing failure)
    /// - If processing fails, message is NOT `ACKed` and will be redelivered
    /// - Idempotent consumers required (duplicates possible on retry)
    async fn subscribe(&self, filter: EventFilter) -> Result<EventStream>;

    /// Publish event (for observers that trigger new events)
    async fn publish(&self, event: EntityEvent) -> Result<()>;

    /// Transport type identifier
    fn transport_type(&self) -> TransportType;

    /// Health check (optional, default implementation)
    async fn health_check(&self) -> Result<TransportHealth> {
        Ok(TransportHealth {
            status:  HealthStatus::Healthy,
            message: None,
        })
    }
}

/// Transport type identifier
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum TransportType {
    /// PostgreSQL LISTEN/NOTIFY
    PostgresNotify,
    /// MySQL polling-based
    #[cfg(feature = "mysql")]
    MySQL,
    /// SQL Server polling-based
    #[cfg(feature = "mssql")]
    MSSQL,
    /// NATS `JetStream`
    #[cfg(feature = "nats")]
    Nats,
    /// In-memory for testing
    InMemory,
}

/// Event filter for subscription
///
/// Future extension: can filter by entity type, operation, tenant, etc.
#[derive(Debug, Clone, Default)]
pub struct EventFilter {
    /// Filter by entity type (None = all types)
    pub entity_type: Option<String>,
    /// Filter by operation (INSERT/UPDATE/DELETE)
    pub operation:   Option<String>,
    /// Filter by tenant ID
    pub tenant_id:   Option<String>,
}

/// Transport health status
#[derive(Debug, Clone)]
pub struct TransportHealth {
    /// Health status
    pub status:  HealthStatus,
    /// Optional message (for degraded/unhealthy states)
    pub message: Option<String>,
}

/// Health status enum
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum HealthStatus {
    /// Transport healthy
    Healthy,
    /// Transport degraded (e.g., retrying connection)
    Degraded,
    /// Transport unhealthy (fatal error)
    Unhealthy,
}

#[cfg(test)]
mod tests;
