//! Publishable events
pub use crate::{event_type::EventTypeName, partition::PartitionId, FlowId};

use chrono::{DateTime, Utc};
use serde::Serialize;

pub use super::{DataOp, DataType, EventId};

/// A `DataChangeEvent` template for publishing of events
///
/// See also [Nakadi Manual](https://nakadi.io/manual.html#definition_AuthorizationAttribute)
#[derive(Debug, Clone, Serialize)]
pub struct DataChangeEventPub<T> {
    pub data: T,
    pub data_type: DataType,
    pub data_op: DataOp,
    pub metadata: EventMetaDataPub,
}

impl<T> From<super::DataChangeEvent<T>> for DataChangeEventPub<T> {
    fn from(e: super::DataChangeEvent<T>) -> Self {
        Self {
            data: e.data,
            data_type: e.data_type,
            data_op: e.data_op,
            metadata: e.metadata.into(),
        }
    }
}

/// A `BusinessEvent` template for publishing of events
///
/// See also [Nakadi Manual](https://nakadi.io/manual.html#definition_DataChangeEvent)
#[derive(Debug, Clone, Serialize)]
pub struct BusinessEventPub<T> {
    #[serde(flatten)]
    pub data: T,
    pub metadata: EventMetaDataPub,
}

impl<T> From<super::BusinessEvent<T>> for BusinessEventPub<T> {
    fn from(e: super::BusinessEvent<T>) -> Self {
        Self {
            data: e.data,
            metadata: e.metadata.into(),
        }
    }
}

/// Metadata of an event
///
/// See also [Nakadi Manual](https://nakadi.io/manual.html#definition_EventMetadata)
#[derive(Debug, Clone, Serialize)]
pub struct EventMetaDataPub {
    /// Identifier of this Event.
    pub eid: EventId,
    /// The EventType of this Event. This is enriched by Nakadi on reception of the Event
    /// based on the endpoint where the Producer sent the Event to.
    ///
    /// If provided MUST match the endpoint. Failure to do so will cause rejection of the
    /// Event.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub event_type: Option<EventTypeName>,
    /// Timestamp of creation of the Event generated by the producer.
    pub occurred_at: DateTime<Utc>,
    /// Event identifier of the Event that caused the generation of this Event.
    /// Set by the producer.
    #[serde(default)]
    pub parent_eids: Vec<EventId>,
    /// Indicates the partition assigned to this Event.
    ///
    /// Required to be set by the client if partition strategy of the EventType is
    /// ‘user_defined’.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub partition: Option<PartitionId>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub flow_id: Option<FlowId>,
}

impl EventMetaDataPub {
    pub fn new<T: Into<EventId>>(eid: T) -> Self {
        Self {
            eid: eid.into(),
            event_type: None,
            occurred_at: Utc::now(),
            parent_eids: Vec::new(),
            partition: None,
            flow_id: None,
        }
    }

    pub fn random_eid() -> Self {
        Self::new(EventId::random())
    }

    pub fn event_type<T: Into<EventTypeName>>(mut self, v: T) -> Self {
        self.event_type = Some(v.into());
        self
    }

    pub fn occurred_at<T: Into<DateTime<Utc>>>(mut self, v: T) -> Self {
        self.occurred_at = v.into();
        self
    }

    pub fn parent_eid<T: Into<EventId>>(mut self, v: T) -> Self {
        self.parent_eids.push(v.into());
        self
    }

    pub fn partition<T: Into<PartitionId>>(mut self, v: T) -> Self {
        self.partition = Some(v.into());
        self
    }

    pub fn flow_id<T: Into<FlowId>>(mut self, v: T) -> Self {
        self.flow_id = Some(v.into());
        self
    }
}

impl From<super::EventMetaData> for EventMetaDataPub {
    fn from(m: super::EventMetaData) -> Self {
        Self {
            eid: m.eid,
            event_type: Some(m.event_type),
            occurred_at: m.occurred_at,
            parent_eids: m.parent_eids,
            partition: None,
            flow_id: Some(m.flow_id),
        }
    }
}
