//! Watch event types for notifications

use crate::kv::KvEntry;
use crate::Revision;
use serde::{Deserialize, Serialize};

/// Event type for watch notifications
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum WatchEventType {
    /// Key was created or updated
    Put,
    /// Key was deleted
    Delete,
}

/// A single watch event
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct WatchEvent {
    /// Type of event (Put or Delete)
    pub event_type: WatchEventType,
    /// Current key-value (for Put, contains new value; for Delete, contains deleted value)
    pub kv: KvEntry,
    /// Previous key-value (if requested and existed)
    pub prev_kv: Option<KvEntry>,
}

impl WatchEvent {
    /// Create a Put event
    pub fn put(kv: KvEntry, prev_kv: Option<KvEntry>) -> Self {
        Self {
            event_type: WatchEventType::Put,
            kv,
            prev_kv,
        }
    }

    /// Create a Delete event
    pub fn delete(kv: KvEntry, prev_kv: Option<KvEntry>) -> Self {
        Self {
            event_type: WatchEventType::Delete,
            kv,
            prev_kv,
        }
    }

    /// Check if this is a Put event
    pub fn is_put(&self) -> bool {
        self.event_type == WatchEventType::Put
    }

    /// Check if this is a Delete event
    pub fn is_delete(&self) -> bool {
        self.event_type == WatchEventType::Delete
    }
}

/// Watch subscription request
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct WatchRequest {
    /// Unique identifier for this watch
    pub watch_id: i64,
    /// Key to watch
    pub key: Vec<u8>,
    /// Range end for prefix/range watches. None = single key
    pub range_end: Option<Vec<u8>>,
    /// Start watching from this revision. None = from current
    pub start_revision: Option<Revision>,
    /// Include previous value in events
    pub prev_kv: bool,
    /// Send periodic progress notifications
    pub progress_notify: bool,
}

impl WatchRequest {
    /// Create a watch for a single key
    pub fn key(watch_id: i64, key: impl Into<Vec<u8>>) -> Self {
        Self {
            watch_id,
            key: key.into(),
            range_end: None,
            start_revision: None,
            prev_kv: false,
            progress_notify: false,
        }
    }

    /// Create a watch for all keys with a prefix
    pub fn prefix(watch_id: i64, prefix: impl Into<Vec<u8>>) -> Self {
        let prefix = prefix.into();
        let range_end = crate::kv::KeyRange::prefix(prefix.clone())
            .end
            .unwrap_or_default();

        Self {
            watch_id,
            key: prefix,
            range_end: Some(range_end),
            start_revision: None,
            prev_kv: false,
            progress_notify: false,
        }
    }

    /// Create a watch for a range of keys
    pub fn range(watch_id: i64, start: impl Into<Vec<u8>>, end: impl Into<Vec<u8>>) -> Self {
        Self {
            watch_id,
            key: start.into(),
            range_end: Some(end.into()),
            start_revision: None,
            prev_kv: false,
            progress_notify: false,
        }
    }

    /// Set start revision
    pub fn from_revision(mut self, revision: Revision) -> Self {
        self.start_revision = Some(revision);
        self
    }

    /// Request previous values in events
    pub fn with_prev_kv(mut self) -> Self {
        self.prev_kv = true;
        self
    }

    /// Request progress notifications
    pub fn with_progress_notify(mut self) -> Self {
        self.progress_notify = true;
        self
    }

    /// Check if this watch matches a key
    pub fn matches(&self, key: &[u8]) -> bool {
        match &self.range_end {
            None => self.key == key,
            Some(end) => {
                if end.is_empty() {
                    // Empty end means all keys >= start
                    key >= self.key.as_slice()
                } else {
                    key >= self.key.as_slice() && key < end.as_slice()
                }
            }
        }
    }
}

/// Response for a watch stream
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct WatchResponse {
    /// Watch ID this response is for
    pub watch_id: i64,
    /// True if this is a watch creation confirmation
    pub created: bool,
    /// True if the watch was canceled
    pub canceled: bool,
    /// Current revision (for progress notifications)
    pub compact_revision: Revision,
    /// Events in this response
    pub events: Vec<WatchEvent>,
}

impl WatchResponse {
    /// Create a creation confirmation response
    pub fn created(watch_id: i64) -> Self {
        Self {
            watch_id,
            created: true,
            canceled: false,
            compact_revision: 0,
            events: Vec::new(),
        }
    }

    /// Create a cancellation response
    pub fn canceled(watch_id: i64) -> Self {
        Self {
            watch_id,
            created: false,
            canceled: true,
            compact_revision: 0,
            events: Vec::new(),
        }
    }

    /// Create an events response
    pub fn events(watch_id: i64, events: Vec<WatchEvent>) -> Self {
        Self {
            watch_id,
            created: false,
            canceled: false,
            compact_revision: 0,
            events,
        }
    }

    /// Create a progress notification
    pub fn progress(watch_id: i64, revision: Revision) -> Self {
        Self {
            watch_id,
            created: false,
            canceled: false,
            compact_revision: revision,
            events: Vec::new(),
        }
    }
}

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

    #[test]
    fn test_watch_event_put() {
        let kv = KvEntry::new(b"key".to_vec(), b"value".to_vec(), 1);
        let event = WatchEvent::put(kv.clone(), None);

        assert!(event.is_put());
        assert!(!event.is_delete());
        assert_eq!(event.kv, kv);
    }

    #[test]
    fn test_watch_request_single_key() {
        let req = WatchRequest::key(1, "/test/key");

        assert!(req.matches(b"/test/key"));
        assert!(!req.matches(b"/test/key2"));
        assert!(!req.matches(b"/test"));
    }

    #[test]
    fn test_watch_request_prefix() {
        let req = WatchRequest::prefix(1, "/nodes/");

        assert!(req.matches(b"/nodes/node1"));
        assert!(req.matches(b"/nodes/node2/tasks"));
        assert!(!req.matches(b"/nodes")); // No trailing slash
        assert!(!req.matches(b"/other/path"));
    }

    #[test]
    fn test_watch_request_range() {
        let req = WatchRequest::range(1, "a", "d");

        assert!(req.matches(b"a"));
        assert!(req.matches(b"b"));
        assert!(req.matches(b"c"));
        assert!(!req.matches(b"d")); // End is exclusive
        assert!(!req.matches(b"e"));
    }

    #[test]
    fn test_watch_serialization() {
        let req = WatchRequest::prefix(42, "/test/")
            .from_revision(100)
            .with_prev_kv();

        let serialized = bincode::serialize(&req).unwrap();
        let deserialized: WatchRequest = bincode::deserialize(&serialized).unwrap();

        assert_eq!(req, deserialized);
    }
}
