//! Storage for escalation events triggered by high-conflict assertions.
//!
//! Escalations are stored at `ESC:{timestamp_nanos}:{id_hex}` to enable efficient
//! time-range queries. External systems can poll for pending escalations and
//! resolve them after review.

use crate::key_codec;
use crate::{KVStore, Result, StorageError};
use async_trait::async_trait;
use std::sync::Arc;
use stemedb_core::types::EscalationEvent;
use tracing::{debug, instrument};

/// Storage trait for escalation events.
///
/// Provides operations for writing, reading, and resolving escalations triggered
/// by high-conflict materialized views.
#[async_trait]
pub trait EscalationStore: Send + Sync {
    /// Write an escalation event to storage.
    ///
    /// Key format: `ESC:{timestamp_nanos}:{id_hex}`
    async fn write_escalation(&self, event: &EscalationEvent) -> Result<()>;

    /// Get all escalation events since the given timestamp (nanoseconds).
    ///
    /// Returns events ordered by timestamp (oldest first).
    async fn get_escalations_since(&self, since: u64) -> Result<Vec<EscalationEvent>>;

    /// Mark an escalation event as resolved.
    ///
    /// Returns `true` if the event was found and updated, `false` if not found.
    async fn resolve_escalation(&self, id: &[u8; 32]) -> Result<bool>;

    /// Get all pending (unresolved) escalation events.
    ///
    /// Returns events ordered by timestamp (oldest first).
    async fn get_pending_escalations(&self) -> Result<Vec<EscalationEvent>>;

    /// Get a specific escalation event by ID.
    ///
    /// Returns `None` if the event does not exist.
    async fn get_escalation(&self, id: &[u8; 32]) -> Result<Option<EscalationEvent>>;
}

/// Generic implementation of `EscalationStore` backed by any `KVStore`.
pub struct GenericEscalationStore<S> {
    store: Arc<S>,
}

impl<S: KVStore> GenericEscalationStore<S> {
    /// Create a new escalation store backed by the given KV store.
    pub fn new(store: Arc<S>) -> Self {
        Self { store }
    }

    /// Parse a key into (timestamp, id).
    ///
    /// Key format: `\x00ESC:{timestamp}:{id_hex}`
    fn parse_key(key: &[u8]) -> Option<(u64, [u8; 32])> {
        let key_str = std::str::from_utf8(key).ok()?;
        // Remove the leading \x00 if present
        let key_str = key_str.strip_prefix('\x00').unwrap_or(key_str);
        let parts: Vec<&str> = key_str.split(':').collect();
        if parts.len() != 3 || parts[0] != "ESC" {
            return None;
        }

        let timestamp = parts[1].parse::<u64>().ok()?;
        let id_hex = parts[2];
        let id_bytes = hex::decode(id_hex).ok()?;
        if id_bytes.len() != 32 {
            return None;
        }

        let mut id = [0u8; 32];
        id.copy_from_slice(&id_bytes);
        Some((timestamp, id))
    }
}

#[async_trait]
impl<S: KVStore + 'static> EscalationStore for GenericEscalationStore<S> {
    #[instrument(skip(self, event), fields(id = %hex::encode(event.id), subject = %event.subject, predicate = %event.predicate))]
    async fn write_escalation(&self, event: &EscalationEvent) -> Result<()> {
        let key = key_codec::escalation_key(event.timestamp, &hex::encode(event.id));
        let serialized = stemedb_core::serde::serialize(event)
            .map_err(|e| StorageError::Serialization(e.to_string()))?;

        self.store.put(&key, &serialized).await?;

        debug!(
            id = %hex::encode(event.id),
            subject = %event.subject,
            predicate = %event.predicate,
            conflict_score = event.conflict_score,
            level = ?event.level,
            "Wrote escalation event"
        );

        Ok(())
    }

    #[instrument(skip(self))]
    async fn get_escalations_since(&self, since: u64) -> Result<Vec<EscalationEvent>> {
        // Scan all escalation keys and filter by timestamp
        let entries = self.store.scan_prefix(&key_codec::escalation_scan_prefix()).await?;

        let mut events = Vec::new();
        for (key, data) in entries {
            // Only include entries with timestamp >= since
            if let Some((timestamp, _)) = Self::parse_key(&key) {
                if timestamp >= since {
                    match stemedb_core::serde::deserialize::<EscalationEvent>(&data) {
                        Ok(event) => events.push(event),
                        Err(e) => {
                            debug!(
                                key = %String::from_utf8_lossy(&key),
                                error = %e,
                                "Skipping malformed escalation event"
                            );
                        }
                    }
                }
            }
        }

        debug!(since = since, count = events.len(), "Retrieved escalations since timestamp");

        Ok(events)
    }

    #[instrument(skip(self), fields(id = %hex::encode(id)))]
    async fn resolve_escalation(&self, id: &[u8; 32]) -> Result<bool> {
        // Scan for the event with this ID
        let entries = self.store.scan_prefix(&key_codec::escalation_scan_prefix()).await?;

        for (key, data) in entries {
            if let Some((_timestamp, found_id)) = Self::parse_key(&key) {
                if &found_id == id {
                    // Found the event, deserialize, mark resolved, re-serialize
                    let mut event: EscalationEvent = stemedb_core::serde::deserialize(&data)
                        .map_err(|e| StorageError::Serialization(e.to_string()))?;

                    if event.resolved {
                        // Already resolved
                        return Ok(true);
                    }

                    event.resolved = true;
                    let serialized = stemedb_core::serde::serialize(&event)
                        .map_err(|e| StorageError::Serialization(e.to_string()))?;

                    self.store.put(&key, &serialized).await?;

                    debug!(
                        id = %hex::encode(id),
                        subject = %event.subject,
                        predicate = %event.predicate,
                        "Resolved escalation event"
                    );

                    return Ok(true);
                }
            }
        }

        debug!(id = %hex::encode(id), "Escalation event not found");
        Ok(false)
    }

    #[instrument(skip(self))]
    async fn get_pending_escalations(&self) -> Result<Vec<EscalationEvent>> {
        let entries = self.store.scan_prefix(&key_codec::escalation_scan_prefix()).await?;

        let mut events = Vec::new();
        for (_key, data) in entries {
            match stemedb_core::serde::deserialize::<EscalationEvent>(&data) {
                Ok(event) if !event.resolved => events.push(event),
                Ok(_) => {} // Skip resolved events
                Err(e) => {
                    debug!(error = %e, "Skipping malformed escalation event");
                }
            }
        }

        // Sort by timestamp (oldest first)
        events.sort_by_key(|e| e.timestamp);

        debug!(count = events.len(), "Retrieved pending escalations");

        Ok(events)
    }

    #[instrument(skip(self), fields(id = %hex::encode(id)))]
    async fn get_escalation(&self, id: &[u8; 32]) -> Result<Option<EscalationEvent>> {
        let entries = self.store.scan_prefix(&key_codec::escalation_scan_prefix()).await?;

        for (key, data) in entries {
            if let Some((_timestamp, found_id)) = Self::parse_key(&key) {
                if &found_id == id {
                    let event: EscalationEvent = stemedb_core::serde::deserialize(&data)
                        .map_err(|e| StorageError::Serialization(e.to_string()))?;
                    return Ok(Some(event));
                }
            }
        }

        Ok(None)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::HybridStore;
    use stemedb_core::types::{EscalationEvent, EscalationLevel};

    fn create_event(
        subject: &str,
        predicate: &str,
        conflict_score: f32,
        timestamp: u64,
        level: EscalationLevel,
    ) -> EscalationEvent {
        let id = EscalationEvent::compute_id(subject, predicate, conflict_score, timestamp);
        EscalationEvent {
            id,
            subject: subject.to_string(),
            predicate: predicate.to_string(),
            conflict_score,
            level,
            reason: format!("Conflict score {} exceeds threshold", conflict_score),
            timestamp,
            resolved: false,
        }
    }

    #[tokio::test]
    async fn test_write_and_get_escalation() {
        let store = Arc::new(HybridStore::open_temp().expect("store"));
        let esc_store = GenericEscalationStore::new(store);

        let event = create_event("Tesla", "revenue", 0.85, 1000, EscalationLevel::High);
        esc_store.write_escalation(&event).await.expect("write");

        let retrieved =
            esc_store.get_escalation(&event.id).await.expect("get").expect("should exist");
        assert_eq!(retrieved, event);
    }

    #[tokio::test]
    async fn test_get_escalations_since() {
        let store = Arc::new(HybridStore::open_temp().expect("store"));
        let esc_store = GenericEscalationStore::new(store);

        let e1 = create_event("Tesla", "revenue", 0.85, 1000, EscalationLevel::High);
        let e2 = create_event("Apple", "profit", 0.90, 2000, EscalationLevel::Critical);
        let e3 = create_event("Google", "growth", 0.75, 3000, EscalationLevel::Medium);

        esc_store.write_escalation(&e1).await.expect("write e1");
        esc_store.write_escalation(&e2).await.expect("write e2");
        esc_store.write_escalation(&e3).await.expect("write e3");

        // Get all since timestamp 1500
        let results = esc_store.get_escalations_since(1500).await.expect("get_since");
        assert_eq!(results.len(), 2);
        assert!(results.iter().any(|e| e.subject == "Apple"));
        assert!(results.iter().any(|e| e.subject == "Google"));
        assert!(!results.iter().any(|e| e.subject == "Tesla"));
    }

    #[tokio::test]
    async fn test_resolve_escalation() {
        let store = Arc::new(HybridStore::open_temp().expect("store"));
        let esc_store = GenericEscalationStore::new(store);

        let event = create_event("Tesla", "revenue", 0.85, 1000, EscalationLevel::High);
        esc_store.write_escalation(&event).await.expect("write");

        // Initially pending
        let pending = esc_store.get_pending_escalations().await.expect("get_pending");
        assert_eq!(pending.len(), 1);

        // Resolve it
        let resolved = esc_store.resolve_escalation(&event.id).await.expect("resolve");
        assert!(resolved);

        // No longer pending
        let pending_after = esc_store.get_pending_escalations().await.expect("get_pending");
        assert_eq!(pending_after.len(), 0);

        // But still exists in storage
        let retrieved =
            esc_store.get_escalation(&event.id).await.expect("get").expect("should exist");
        assert!(retrieved.resolved);
    }

    #[tokio::test]
    async fn test_get_pending_escalations() {
        let store = Arc::new(HybridStore::open_temp().expect("store"));
        let esc_store = GenericEscalationStore::new(store);

        let e1 = create_event("Tesla", "revenue", 0.85, 1000, EscalationLevel::High);
        let e2 = create_event("Apple", "profit", 0.90, 2000, EscalationLevel::Critical);

        esc_store.write_escalation(&e1).await.expect("write e1");
        esc_store.write_escalation(&e2).await.expect("write e2");

        // Both pending
        let pending = esc_store.get_pending_escalations().await.expect("get_pending");
        assert_eq!(pending.len(), 2);

        // Resolve one
        esc_store.resolve_escalation(&e1.id).await.expect("resolve");

        // Only one pending
        let pending_after = esc_store.get_pending_escalations().await.expect("get_pending");
        assert_eq!(pending_after.len(), 1);
        assert_eq!(pending_after[0].subject, "Apple");
    }

    #[tokio::test]
    async fn test_resolve_nonexistent_escalation() {
        let store = Arc::new(HybridStore::open_temp().expect("store"));
        let esc_store = GenericEscalationStore::new(store);

        let nonexistent_id = [42u8; 32];
        let resolved = esc_store.resolve_escalation(&nonexistent_id).await.expect("resolve");
        assert!(!resolved);
    }

    #[tokio::test]
    async fn test_parse_key() {
        let event = create_event("Tesla", "revenue", 0.85, 1000, EscalationLevel::High);
        let key = key_codec::escalation_key(event.timestamp, &hex::encode(event.id));

        let (timestamp, id) =
            GenericEscalationStore::<HybridStore>::parse_key(&key).expect("parse should succeed");

        assert_eq!(timestamp, 1000);
        assert_eq!(id, event.id);
    }
}
