use std::sync::Arc;

use futures::StreamExt;
use platform_nats::NatsBridge;
use serde::Serialize;
use tracing::{error, info, warn};

use crate::cache::ExtensionCache;
use crate::models::ExtensionType;

/// Publishes NATS events for extension lifecycle operations.
pub struct EventPublisher {
    bridge: Arc<NatsBridge>,
}

#[derive(Debug, Serialize)]
struct ExtensionEvent<'a> {
    name: &'a str,
    version: &'a str,
    #[serde(rename = "type")]
    extension_type: ExtensionType,
}

impl EventPublisher {
    pub fn new(bridge: Arc<NatsBridge>) -> Self {
        Self { bridge }
    }

    /// Publish `provisioning.extensions.{type}.installed`.
    ///
    /// Fire-and-forget: logs error on failure but never propagates to handler.
    pub async fn publish_installed(
        &self,
        extension_type: ExtensionType,
        name: &str,
        version: &str,
    ) {
        let subject = format!("extensions.{}.installed", extension_type);
        let payload = ExtensionEvent {
            name,
            version,
            extension_type,
        };
        match self.bridge.publish_json(&subject, &payload).await {
            Ok(_) => {
                info!(
                    subject = %subject,
                    extension = %name,
                    version = %version,
                    "Extension installed event published"
                );
            }
            Err(e) => {
                error!(
                    subject = %subject,
                    extension = %name,
                    "Failed to publish extension event: {}", e
                );
            }
        }
    }
}

/// Spawn a background task that subscribes to workspace deploy-done events
/// and invalidates the extension cache on each notification.
///
/// Subject: `provisioning.workspace.*.deploy.done` (filter on WORKSPACE stream)
pub fn spawn_cache_invalidator(bridge: Arc<NatsBridge>, cache: Arc<ExtensionCache>) {
    tokio::spawn(async move {
        run_cache_invalidator(bridge, cache).await;
    });
}

async fn run_cache_invalidator(bridge: Arc<NatsBridge>, cache: Arc<ExtensionCache>) {
    const STREAM: &str = "WORKSPACE";
    const CONSUMER: &str = "ext-registry-cache-invalidator";

    let mut messages = match bridge.subscribe_pull(STREAM, CONSUMER).await {
        Ok(m) => m,
        Err(e) => {
            warn!("Extension registry cache invalidator: subscribe failed — {e}");
            return;
        }
    };

    info!("Extension registry cache invalidator running on stream {STREAM}");

    while let Some(msg_result) = messages.next().await {
        match msg_result {
            Ok(msg) => {
                // Subject pattern: provisioning.workspace.{ws_id}.deploy.done
                // Filter is applied at JetStream level; any message here triggers invalidation.
                let subject = msg.subject.as_str();
                info!(subject = %subject, "Workspace deploy detected — invalidating extension cache");
                cache.invalidate_all();
                if let Err(e) = msg.ack().await {
                    warn!("Cache invalidator: ack failed — {e}");
                }
            }
            Err(e) => {
                error!("Cache invalidator: message error — {e}");
            }
        }
    }
}
