//! Application state for the MAT REST API.
//!
//! This module provides the shared state that is passed to all API handlers.
//! It contains repositories, services, and real-time event broadcasting.
#![allow(missing_docs)]

use std::collections::HashMap;
use std::sync::Arc;

use parking_lot::RwLock;
use tokio::sync::broadcast;
use uuid::Uuid;

use super::dto::WebSocketMessage;
use crate::detection::{DetectionConfig, DetectionPipeline};
use crate::domain::{
    events::{EventStore, InMemoryEventStore},
    Alert, DisasterEvent,
};

/// Shared application state for the API.
///
/// This is cloned for each request handler and provides thread-safe
/// access to shared resources.
#[derive(Clone)]
pub struct AppState {
    inner: Arc<AppStateInner>,
}

/// Inner state (not cloned, shared via Arc).
struct AppStateInner {
    /// In-memory event repository
    events: RwLock<HashMap<Uuid, DisasterEvent>>,
    /// In-memory alert repository
    alerts: RwLock<HashMap<Uuid, AlertWithEventId>>,
    /// Broadcast channel for real-time updates
    broadcast_tx: broadcast::Sender<WebSocketMessage>,
    /// Configuration
    config: ApiConfig,
    /// Shared detection pipeline for CSI data push
    detection_pipeline: Arc<DetectionPipeline>,
    /// Domain event store
    event_store: Arc<dyn EventStore>,
    /// Scanning state flag
    scanning: std::sync::atomic::AtomicBool,
}

/// Alert with its associated event ID for lookup.
#[derive(Clone)]
pub struct AlertWithEventId {
    pub alert: Alert,
    pub event_id: Uuid,
}

/// API configuration.
#[derive(Clone)]
pub struct ApiConfig {
    /// Maximum number of events to store
    pub max_events: usize,
    /// Maximum survivors per event
    pub max_survivors_per_event: usize,
    /// Broadcast channel capacity
    pub broadcast_capacity: usize,
}

impl Default for ApiConfig {
    fn default() -> Self {
        Self {
            max_events: 1000,
            max_survivors_per_event: 10000,
            broadcast_capacity: 1024,
        }
    }
}

impl AppState {
    /// Create a new application state with default configuration.
    pub fn new() -> Self {
        Self::with_config(ApiConfig::default())
    }

    /// Create a new application state with custom configuration.
    pub fn with_config(config: ApiConfig) -> Self {
        let (broadcast_tx, _) = broadcast::channel(config.broadcast_capacity);
        let detection_pipeline = Arc::new(DetectionPipeline::new(DetectionConfig::default()));
        let event_store: Arc<dyn EventStore> = Arc::new(InMemoryEventStore::new());

        Self {
            inner: Arc::new(AppStateInner {
                events: RwLock::new(HashMap::new()),
                alerts: RwLock::new(HashMap::new()),
                broadcast_tx,
                config,
                detection_pipeline,
                event_store,
                scanning: std::sync::atomic::AtomicBool::new(false),
            }),
        }
    }

    /// Get the detection pipeline for CSI data ingestion.
    pub fn detection_pipeline(&self) -> &DetectionPipeline {
        &self.inner.detection_pipeline
    }

    /// Get the domain event store.
    pub fn event_store(&self) -> &Arc<dyn EventStore> {
        &self.inner.event_store
    }

    /// Get scanning state.
    pub fn is_scanning(&self) -> bool {
        self.inner
            .scanning
            .load(std::sync::atomic::Ordering::SeqCst)
    }

    /// Set scanning state.
    pub fn set_scanning(&self, state: bool) {
        self.inner
            .scanning
            .store(state, std::sync::atomic::Ordering::SeqCst);
    }

    // ========================================================================
    // Event Operations
    // ========================================================================

    /// Store a disaster event.
    pub fn store_event(&self, event: DisasterEvent) -> Uuid {
        let id = *event.id().as_uuid();
        let mut events = self.inner.events.write();

        // Check capacity
        if events.len() >= self.inner.config.max_events {
            // Remove oldest closed event
            let oldest_closed = events
                .iter()
                .filter(|(_, e)| matches!(e.status(), crate::EventStatus::Closed))
                .min_by_key(|(_, e)| e.start_time())
                .map(|(id, _)| *id);

            if let Some(old_id) = oldest_closed {
                events.remove(&old_id);
            }
        }

        events.insert(id, event);
        id
    }

    /// Get an event by ID.
    pub fn get_event(&self, id: Uuid) -> Option<DisasterEvent> {
        self.inner.events.read().get(&id).cloned()
    }

    /// Get mutable access to an event (for updates).
    pub fn update_event<F, R>(&self, id: Uuid, f: F) -> Option<R>
    where
        F: FnOnce(&mut DisasterEvent) -> R,
    {
        let mut events = self.inner.events.write();
        events.get_mut(&id).map(f)
    }

    /// List all events.
    pub fn list_events(&self) -> Vec<DisasterEvent> {
        self.inner.events.read().values().cloned().collect()
    }

    /// Get event count.
    pub fn event_count(&self) -> usize {
        self.inner.events.read().len()
    }

    // ========================================================================
    // Alert Operations
    // ========================================================================

    /// Store an alert.
    pub fn store_alert(&self, alert: Alert, event_id: Uuid) -> Uuid {
        let id = *alert.id().as_uuid();
        let mut alerts = self.inner.alerts.write();
        alerts.insert(id, AlertWithEventId { alert, event_id });
        id
    }

    /// Get an alert by ID.
    pub fn get_alert(&self, id: Uuid) -> Option<AlertWithEventId> {
        self.inner.alerts.read().get(&id).cloned()
    }

    /// Update an alert.
    pub fn update_alert<F, R>(&self, id: Uuid, f: F) -> Option<R>
    where
        F: FnOnce(&mut Alert) -> R,
    {
        let mut alerts = self.inner.alerts.write();
        alerts.get_mut(&id).map(|a| f(&mut a.alert))
    }

    /// List alerts for an event.
    pub fn list_alerts_for_event(&self, event_id: Uuid) -> Vec<Alert> {
        self.inner
            .alerts
            .read()
            .values()
            .filter(|a| a.event_id == event_id)
            .map(|a| a.alert.clone())
            .collect()
    }

    // ========================================================================
    // Broadcasting
    // ========================================================================

    /// Get a receiver for real-time updates.
    pub fn subscribe(&self) -> broadcast::Receiver<WebSocketMessage> {
        self.inner.broadcast_tx.subscribe()
    }

    /// Broadcast a message to all subscribers.
    pub fn broadcast(&self, message: WebSocketMessage) {
        // Ignore send errors (no subscribers)
        let _ = self.inner.broadcast_tx.send(message);
    }

    /// Get the number of active subscribers.
    pub fn subscriber_count(&self) -> usize {
        self.inner.broadcast_tx.receiver_count()
    }
}

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

#[cfg(test)]
mod tests {
    use super::*;
    use crate::domain::{DisasterEvent, DisasterType};
    use geo::Point;

    #[test]
    fn test_store_and_get_event() {
        let state = AppState::new();
        let event = DisasterEvent::new(
            DisasterType::Earthquake,
            Point::new(-122.4194, 37.7749),
            "Test earthquake",
        );
        let id = *event.id().as_uuid();

        state.store_event(event);

        let retrieved = state.get_event(id);
        assert!(retrieved.is_some());
        assert_eq!(retrieved.unwrap().id().as_uuid(), &id);
    }

    #[test]
    fn test_update_event() {
        let state = AppState::new();
        let event = DisasterEvent::new(DisasterType::Earthquake, Point::new(0.0, 0.0), "Test");
        let id = *event.id().as_uuid();
        state.store_event(event);

        let result = state.update_event(id, |e| {
            e.set_status(crate::EventStatus::Suspended);
            true
        });

        assert!(result.unwrap());
        let updated = state.get_event(id).unwrap();
        assert!(matches!(updated.status(), crate::EventStatus::Suspended));
    }

    #[test]
    fn test_broadcast_subscribe() {
        let state = AppState::new();
        let _rx = state.subscribe();

        state.broadcast(WebSocketMessage::Heartbeat {
            timestamp: chrono::Utc::now(),
        });

        // Try to receive (in async context this would work)
        assert_eq!(state.subscriber_count(), 1);
    }
}
