// Copyright (c) Subzero Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

use std::{fmt, hash};

use crate::{error::MatcherError, event::Event, predicate::ElementaryPredicate};

/// Trait defining a subscription key.
pub trait SubscriptionKey: fmt::Debug + Clone + PartialEq + Eq + hash::Hash {}

impl<T> SubscriptionKey for T where T: fmt::Debug + Clone + PartialEq + Eq + hash::Hash {}

/// A subscription is a conjunction of elementary predicates
#[derive(Debug, Clone, PartialEq)]
pub struct Subscription<T: SubscriptionKey> {
    /// Unique identifier for the subscription
    pub id: T,

    /// The predicates that make up this subscription (combined with AND logic)
    pub predicates: Vec<ElementaryPredicate>,
}

impl<T: SubscriptionKey> Subscription<T> {
    /// Creates a new subscription with the given ID
    pub fn new(id: T) -> Self {
        Self {
            id,
            predicates: Vec::new(),
        }
    }

    /// Adds a predicate to the subscription
    pub fn with_predicate(mut self, predicate: ElementaryPredicate) -> Self {
        self.predicates.push(predicate);
        self
    }

    /// Adds multiple predicates to the subscription
    pub fn with_predicates(mut self, predicates: Vec<ElementaryPredicate>) -> Self {
        self.predicates.extend(predicates);
        self
    }

    /// Evaluates whether an event matches this subscription
    pub fn matches(&self, event: &impl Event) -> Result<bool, MatcherError> {
        // A subscription matches if all of its predicates match (AND logic)
        for predicate in &self.predicates {
            if !predicate.evaluate(event)? {
                return Ok(false);
            }
        }
        Ok(true)
    }
}

impl<T: SubscriptionKey> fmt::Display for Subscription<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "Subscription(id={:?}, predicates=[", self.id)?;
        for (i, predicate) in self.predicates.iter().enumerate() {
            if i > 0 {
                write!(f, " AND ")?;
            }
            write!(f, "{predicate}")?;
        }
        write!(f, "])")
    }
}
