//! kogral integration bridge — forwards syntaxis lifecycle events to kogral.
//!
//! Enabled with the `kogral` feature. Internally uses `platform-nats::EventStream`
//! so the kogral-core dependency chain is not required.

use bytes::Bytes;
use platform_nats::{EventStream, NatsConfig};
use serde::{Deserialize, Serialize};

use crate::VaporaResult;

/// Connection config for the kogral NATS cluster.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KogralBridgeConfig {
    /// NATS server URL of the kogral cluster
    pub url: String,
    /// JetStream stream that kogral consumes (default: `KOGRAL`)
    pub stream_name: String,
    /// Consumer name (default: `syntaxis-kogral`)
    pub consumer_name: String,
    /// NKey seed for signing (optional)
    pub nkey_seed: Option<String>,
    /// Trusted public NKeys (empty → accept all)
    pub trusted_nkeys: Vec<String>,
}

impl Default for KogralBridgeConfig {
    fn default() -> Self {
        Self {
            url: "nats://localhost:4222".to_string(),
            stream_name: "KOGRAL".to_string(),
            consumer_name: "syntaxis-kogral".to_string(),
            nkey_seed: None,
            trusted_nkeys: vec![],
        }
    }
}

/// Lifecycle event forwarded to kogral for knowledge graph indexing.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KogralEvent {
    /// Dot-separated event type, e.g. `syntaxis.project.created`
    pub event_type: String,
    /// Source project identifier
    pub project_id: String,
    /// Optional phase name relevant to the event
    #[serde(skip_serializing_if = "Option::is_none")]
    pub phase: Option<String>,
    /// RFC 3339 timestamp
    pub timestamp: String,
    /// Arbitrary structured payload
    pub payload: serde_json::Value,
}

/// Bridge that publishes syntaxis lifecycle events to kogral via NATS.
pub struct KogralBridge {
    stream: EventStream,
}

impl std::fmt::Debug for KogralBridge {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("KogralBridge").finish_non_exhaustive()
    }
}

impl KogralBridge {
    /// Connect to the kogral NATS cluster.
    pub async fn connect(cfg: &KogralBridgeConfig) -> VaporaResult<Self> {
        let nats_cfg = NatsConfig {
            url: cfg.url.clone(),
            stream_name: cfg.stream_name.clone(),
            consumer_name: cfg.consumer_name.clone(),
            subjects: vec![format!("kogral.syntaxis.>")],
            nkey_seed: cfg.nkey_seed.clone(),
            trusted_nkeys: cfg.trusted_nkeys.clone(),
            require_signed_messages: false,
        };

        let stream = EventStream::connect(&nats_cfg).await?;
        tracing::info!(url = %cfg.url, stream = %cfg.stream_name, "kogral bridge connected");
        Ok(Self { stream })
    }

    /// Emit a generic lifecycle event to `kogral.syntaxis.<event_type>`.
    pub async fn emit(&self, event: &KogralEvent) -> VaporaResult<()> {
        let subject = format!("kogral.syntaxis.{}", event.event_type.replace('.', "-"));
        let payload = Bytes::from(serde_json::to_vec(event)?);
        self.stream.publish(&subject, payload).await?;
        tracing::debug!(event_type = %event.event_type, project_id = %event.project_id, "kogral event emitted");
        Ok(())
    }

    /// Emit a project-created event.
    pub async fn on_project_created(&self, project_id: &str) -> VaporaResult<()> {
        self.emit(&KogralEvent {
            event_type: "project.created".to_string(),
            project_id: project_id.to_string(),
            phase: None,
            timestamp: chrono::Utc::now().to_rfc3339(),
            payload: serde_json::json!({ "project_id": project_id }),
        })
        .await
    }

    /// Emit a phase-transition event.
    pub async fn on_phase_transition(
        &self,
        project_id: &str,
        from_phase: &str,
        to_phase: &str,
    ) -> VaporaResult<()> {
        self.emit(&KogralEvent {
            event_type: "phase.transition".to_string(),
            project_id: project_id.to_string(),
            phase: Some(to_phase.to_string()),
            timestamp: chrono::Utc::now().to_rfc3339(),
            payload: serde_json::json!({
                "from": from_phase,
                "to": to_phase,
            }),
        })
        .await
    }

    /// Emit a task-completed event.
    pub async fn on_task_completed(
        &self,
        project_id: &str,
        task_id: &str,
        phase: &str,
    ) -> VaporaResult<()> {
        self.emit(&KogralEvent {
            event_type: "task.completed".to_string(),
            project_id: project_id.to_string(),
            phase: Some(phase.to_string()),
            timestamp: chrono::Utc::now().to_rfc3339(),
            payload: serde_json::json!({ "task_id": task_id }),
        })
        .await
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_kogral_bridge_config_default() {
        let cfg = KogralBridgeConfig::default();
        assert_eq!(cfg.stream_name, "KOGRAL");
        assert!(cfg.nkey_seed.is_none());
    }

    #[test]
    fn test_kogral_event_serde() {
        let event = KogralEvent {
            event_type: "project.created".to_string(),
            project_id: "p1".to_string(),
            phase: None,
            timestamp: "2026-01-01T00:00:00Z".to_string(),
            payload: serde_json::json!({ "project_id": "p1" }),
        };
        let json = serde_json::to_string(&event).unwrap();
        let decoded: KogralEvent = serde_json::from_str(&json).unwrap();
        assert_eq!(decoded.event_type, "project.created");
        // `phase` is None → must be absent from the serialized JSON (skip_serializing_if)
        assert!(!json.contains("\"phase\""));
    }

    #[test]
    fn test_kogral_event_with_phase_serde() {
        let event = KogralEvent {
            event_type: "phase.transition".to_string(),
            project_id: "p2".to_string(),
            phase: Some("devel".to_string()),
            timestamp: "2026-01-01T00:00:00Z".to_string(),
            payload: serde_json::json!({ "from": "create", "to": "devel" }),
        };
        let json = serde_json::to_string(&event).unwrap();
        assert!(json.contains("\"phase\""));
        let decoded: KogralEvent = serde_json::from_str(&json).unwrap();
        assert_eq!(decoded.phase.unwrap(), "devel");
    }
}
