use serde::Serialize;
use serde::de::DeserializeOwned;
use std::fmt;

use crate::value_object::Version;

/// Capability bound that every domain event payload must satisfy.
///
/// A `DomainEvent` is a fact — an immutable record of something that has
/// happened inside an aggregate. Implementations are typically `enum`s with
/// one variant per concrete event, generated by the `#[domain_event]`
/// procedural macro. The auto-generated implementation handles identity,
/// type discrimination and version tagging so application code can focus on
/// the business payload of each variant.
///
/// # Trait bounds
///
/// The supertrait set is intentionally broad and covers the practical
/// requirements of an event-sourced system:
///
/// - `Clone` + `PartialEq` + `Debug` make events convenient to assert against
///   in tests and to fan out to multiple consumers.
/// - `Serialize` + `DeserializeOwned` enable round-tripping through the
///   persistence layer (see [`crate::persist::SerializedEvent`]) and any
///   transport boundary (message bus, HTTP, etc.).
/// - `Send` + `Sync` + `'static` allow events to flow freely across async
///   tasks and thread boundaries.
///
/// # Contract
///
/// Implementations must guarantee:
///
/// - [`event_id`](Self::event_id) is globally unique for the lifetime of the
///   system. It is used for idempotency, deduplication and as the
///   `causation_id` of any downstream event.
/// - [`event_type`](Self::event_type) is a stable string identifier. Renaming
///   a variant is a breaking change for stored data and must be handled via
///   an [`EventUpcaster`](crate::event_upcaster::EventUpcaster).
/// - [`event_version`](Self::event_version) starts at `1` and is bumped only
///   when the payload schema of that variant changes.
/// - [`aggregate_version`](Self::aggregate_version) is the version of the
///   owning aggregate **after** this event has been applied. It is the value
///   the persistence layer uses for optimistic concurrency control.
pub trait DomainEvent:
    Clone + PartialEq + fmt::Debug + Serialize + DeserializeOwned + Send + Sync + 'static
{
    /// Globally unique identifier for this individual event instance.
    ///
    /// The id is generated when the event is produced (typically a UUIDv7 or
    /// similar) and never changes. It is used for deduplication, idempotency
    /// keys and to populate the `causation_id` of any event that is produced
    /// in reaction to this one.
    fn event_id(&self) -> &str;

    /// Stable type discriminator for this event variant.
    ///
    /// Conventionally formatted as `"AggregateName.VariantName"` (for example
    /// `"OrderEvent.Created"`) but any stable string is acceptable — it is
    /// the key used to dispatch upcasters and event handlers, so it must
    /// not change once events have been persisted.
    fn event_type(&self) -> &str;

    /// Schema version of this event payload.
    ///
    /// Starts at `1`. Bumping this number signals to
    /// [`EventUpcaster`](crate::event_upcaster::EventUpcaster) implementations
    /// that the on-disk shape of this `event_type` has evolved and old
    /// records need to be transformed before deserialization into the
    /// current Rust type.
    fn event_version(&self) -> usize;

    /// Version of the owning aggregate after this event has been applied.
    ///
    /// The persistence layer uses this value as the optimistic-concurrency
    /// token: appending an event whose `aggregate_version` does not match
    /// the next expected version yields an
    /// [`ErrorKind::Conflict`](crate::error::ErrorKind::Conflict).
    fn aggregate_version(&self) -> Version;
}
