///|
/// CloudEvents Specification JSON Schema
/// Ref: https://github.com/cloudevents/spec/blob/v1.0.2/cloudevents/formats/cloudevents.json
pub struct CloudEvents[Data] {
  /// Identifies the event.
  id : String
  /// Identifies the context in which an event happened.
  source : String
  /// The version of the CloudEvents specification which the event uses.
  specversion : String
  /// Describes the type of event related to the originating occurrence.
  type_ : String
  /// Content type of the data value. Must adhere to RFC 2046 format.
  datacontenttype : String?
  /// Identifies the schema that data adheres to.
  dataschema : String?
  /// Describes the subject of the event in the context of the event producer (identified by source).
  subject : String?
  /// Timestamp of when the occurrence happened. Must adhere to RFC 3339.
  time : String?
  /// The event payload.
  data : Data
  /// Base64 encoded event payload. Must adhere to RFC4648.
  data_base64 : String?
} derive (
  FromJson(fields(type_(rename="type"))),
  ToJson(fields(type_(rename="type"))),
  Show,
  Eq,
)

///|
pub fn[Data] CloudEvents::new(
  id~ : String,
  source~ : String,
  specversion~ : String,
  type_~ : String,
  datacontenttype? : String? = None,
  dataschema? : String? = None,
  subject? : String? = None,
  time? : String? = None,
  data~ : Data,
  data_base64? : String? = None,
) -> CloudEvents[Data] {
  CloudEvents::{
    id,
    source,
    specversion,
    type_,
    datacontenttype,
    dataschema,
    subject,
    time,
    data,
    data_base64,
  }
}

///|
/// CloudEvents Specification with Extension Fields
pub struct ExtendedCloudEvents[Data, ExtendedFields] {
  /// Identifies the event.
  id : String
  /// Identifies the context in which an event happened.
  source : String
  /// The version of the CloudEvents specification which the event uses.
  specversion : String
  /// Describes the type of event related to the originating occurrence.
  type_ : String
  /// Content type of the data value. Must adhere to RFC 2046 format.
  datacontenttype : String?
  /// Identifies the schema that data adheres to.
  dataschema : String?
  /// Describes the subject of the event in the context of the event producer (identified by source).
  subject : String?
  /// Timestamp of when the occurrence happened. Must adhere to RFC 3339.
  time : String?
  /// The event payload.
  data : Data
  /// Base64 encoded event payload. Must adhere to RFC4648.
  data_base64 : String?
  /// Extension fields
  ///
  /// When serialized to JSON, the fields of this struct will be merged into the top-level CloudEvent JSON object.
  /// They will NOT be nested under an "extended_field" key.
  ///
  /// if you want to use safe ToJson implementation, please implement ToJsonObject trait for ExtendedFields.
  /// otherwise, unsafe ToJson implementation will be used.
  /// 
  extended_field : ExtendedFields
} derive(Show, Eq)

///|
priv struct UnsafeToJsonEnabledExtendedCloudEvent[Data, E](
  ExtendedCloudEvents[Data, E]
)

///|
/// wrapper type that enables unsafe JSON serialization for `ExtendedCloudEvent`
/// instances.
/// This type provides JSON serialization by requiring that the
/// extended fields implement the `ToJson` trait. This allows for more flexibility
/// but may lead to runtime errors if the extended fields do not serialize
/// to JSON objects that can be merged into the top-level CloudEvent JSON structure.
impl[Data : ToJson, E : ToJson] ToJson for UnsafeToJsonEnabledExtendedCloudEvent[
  Data,
  E,
] with to_json(self) -> Json {
  let base = self.0.to_cloud_event().to_json().as_object().unwrap()
  let extended = self.extended_field.to_json().as_object().unwrap()
  Json::object(base.merge(extended))
}

///|
pub fn[Data : ToJson, E : ToJson] ExtendedCloudEvents::unsafe_to_json_enabled(
  self : ExtendedCloudEvents[Data, E],
) -> &ToJson {
  let _ = self.data as &ToJson
  let _ = self.extended_field as &ToJson
  UnsafeToJsonEnabledExtendedCloudEvent(self)
}

///|
/// Wrapper type that enables safe JSON serialization for `ExtendedCloudEvent`
/// instances.
///
/// This type provides type-safe JSON serialization by requiring that the
/// extended fields implement the `ToJsonObject` trait instead of the more
/// general `ToJson` trait. This ensures that extended fields are properly
/// serialized as JSON objects that can be safely merged into the top-level
/// CloudEvent JSON structure.
priv struct SafeToJsonEnabledExtendedCloudEvent[Data, E](
  ExtendedCloudEvents[Data, E]
)

///|
impl[Data : ToJson, E : ToJsonObject] ToJson for SafeToJsonEnabledExtendedCloudEvent[
  Data,
  E,
] with to_json(self) -> Json {
  let base = self.0.to_cloud_event().to_json().as_object().unwrap()
  let extended = self.extended_field.to_json_object()
  Json::object(base.merge(extended))
}

///|
pub fn[Data : ToJson, E : ToJsonObject] ExtendedCloudEvents::safe_to_json_enabled(
  self : ExtendedCloudEvents[Data, E],
) -> &ToJson {
  let _ = self.data as &ToJson
  let _ = self.extended_field as &ToJsonObject
  SafeToJsonEnabledExtendedCloudEvent(self)
}

///|
/// impl constuctor of ExtendedCloudEvent
pub fn[Data, ExtendedFields] ExtendedCloudEvents::new(
  id~ : String,
  source~ : String,
  specversion~ : String,
  type_~ : String,
  datacontenttype? : String? = None,
  dataschema? : String? = None,
  subject? : String? = None,
  time? : String? = None,
  data~ : Data,
  data_base64? : String? = None,
  extended_field~ : ExtendedFields,
) -> ExtendedCloudEvents[Data, ExtendedFields] {
  ExtendedCloudEvents::{
    id,
    source,
    specversion,
    type_,
    datacontenttype,
    dataschema,
    subject,
    time,
    data,
    data_base64,
    extended_field,
  }
}

///|
fn[Data, ExtendedFields] ExtendedCloudEvents::to_cloud_event(
  self : ExtendedCloudEvents[Data, ExtendedFields],
) -> CloudEvents[Data] {
  CloudEvents::{
    id: self.id,
    source: self.source,
    specversion: self.specversion,
    type_: self.type_,
    datacontenttype: self.datacontenttype,
    dataschema: self.dataschema,
    subject: self.subject,
    time: self.time,
    data: self.data,
    data_base64: self.data_base64,
  }
}

///|
/// Trait for converting extended fields to JSON objects
pub(open) trait ToJsonObject {
  to_json_object(Self) -> Map[String, Json]
}

///|
pub impl[Data : @json.FromJson, E : @json.FromJson] @json.FromJson for ExtendedCloudEvents[
  Data,
  E,
] with from_json(json : Json, json_path : @json.JsonPath) -> ExtendedCloudEvents[
  Data,
  E,
] raise @json.JsonDecodeError {
  let base = CloudEvents::from_json(json, json_path)
  let extended_field = E::from_json(json, json_path)
  ExtendedCloudEvents::{
    id: base.id,
    source: base.source,
    specversion: base.specversion,
    type_: base.type_,
    datacontenttype: base.datacontenttype,
    dataschema: base.dataschema,
    subject: base.subject,
    time: base.time,
    data: base.data,
    data_base64: base.data_base64,
    extended_field,
  }
}