///|
/// Coarse category used to classify where a failure happened.
pub(all) enum MailErrorKind {
  /// Address parsing or validation failed.
  InvalidAddress
  /// Header construction or validation failed.
  InvalidHeader
  /// A CR/LF or control byte was found where it must not appear.
  HeaderInjection
  /// MIME structure or Content-Type/Disposition problem.
  Mime
  /// Transfer-encoding failure (QP / base64 / line width).
  Encode
  /// The message exceeded a configured length limit.
  ContentLength
  /// SMTP protocol / session problem.
  Smtp
  /// Server response carried a permanent (5xx) error.
  SmtpPermanent
  /// Server response carried a transient (4xx) error.
  SmtpTransient
  /// AUTH handshake failed.
  Auth
  /// Underlying transport / I/O failure.
  Transport
  /// Configuration problem.
  Config
  /// The operation timed out.
  Timeout
  /// The operation is unsupported (e.g. socket adapter on a non-native target).
  Unsupported
  /// Generic I/O failure.
  Io
} derive(Eq, Debug)

///|
/// The unified error type for MoonMail. Every failure carries a stable
/// `ReasonCode` (see `docs/REASON_CODES.md`) plus a human-readable message.
pub struct MailError {
  kind : MailErrorKind
  reason : ReasonCode
  message : String
} derive(Eq, Debug)

///|
/// Raised by MoonMail functions that fail. The payload is a `MailError` that
/// carries the stable reason code and a human-readable message.
pub(all) suberror MailFailure {
  MailFailure(MailError)
} derive(@debug.Debug)

///|
pub fn MailFailure::of(
  kind : MailErrorKind,
  reason : ReasonCode,
  message : String,
) -> MailFailure {
  MailFailure(MailError::of(kind, reason, message))
}

///|
pub fn MailFailure::mail_error(self : MailFailure) -> MailError {
  match self {
    MailFailure(err) => err
  }
}

///|
pub impl Show for MailFailure with fn output(self, logger) {
  logger.write_string("MailFailure(\{self.mail_error()})")
}

///|
pub fn MailFailure::invalid_address(message : String) -> MailFailure {
  MailFailure(MailError::invalid_address(message))
}

///|
pub fn MailFailure::header_injection(message : String) -> MailFailure {
  MailFailure(MailError::header_injection(message))
}

///|
pub fn MailFailure::invalid_header(message : String) -> MailFailure {
  MailFailure(MailError::invalid_header(message))
}

///|
pub fn MailFailure::mime(message : String) -> MailFailure {
  MailFailure(MailError::mime(message))
}

///|
pub fn MailFailure::encode(message : String) -> MailFailure {
  MailFailure(MailError::encode(message))
}

///|
pub fn MailFailure::content_length(message : String) -> MailFailure {
  MailFailure(MailError::content_length(message))
}

///|
pub fn MailFailure::smtp(reason : ReasonCode, message : String) -> MailFailure {
  MailFailure(MailError::smtp(reason, message))
}

///|
pub fn MailFailure::smtp_permanent(message : String) -> MailFailure {
  MailFailure(MailError::smtp_permanent(message))
}

///|
pub fn MailFailure::smtp_transient(message : String) -> MailFailure {
  MailFailure(MailError::smtp_transient(message))
}

///|
pub fn MailFailure::auth(message : String) -> MailFailure {
  MailFailure(MailError::auth(message))
}

///|
pub fn MailFailure::transport(message : String) -> MailFailure {
  MailFailure(MailError::transport(message))
}

///|
pub fn MailFailure::config(message : String) -> MailFailure {
  MailFailure(MailError::config(message))
}

///|
pub fn MailFailure::timeout(message : String) -> MailFailure {
  MailFailure(MailError::timeout(message))
}

///|
pub fn MailFailure::unsupported(message : String) -> MailFailure {
  MailFailure(MailError::unsupported(message))
}

///|
pub fn MailFailure::io(message : String) -> MailFailure {
  MailFailure(MailError::io(message))
}

///|
pub fn MailError::of(
  kind : MailErrorKind,
  reason : ReasonCode,
  message : String,
) -> MailError {
  { kind, reason, message }
}

///|
pub fn MailError::invalid_address(message : String) -> MailError {
  { kind: InvalidAddress, reason: MM_ADDR_002, message }
}

///|
pub fn MailError::header_injection(message : String) -> MailError {
  { kind: HeaderInjection, reason: MM_INJ_001, message }
}

///|
pub fn MailError::invalid_header(message : String) -> MailError {
  { kind: InvalidHeader, reason: MM_HEADER_001, message }
}

///|
pub fn MailError::mime(message : String) -> MailError {
  { kind: Mime, reason: MM_MIME_001, message }
}

///|
pub fn MailError::encode(message : String) -> MailError {
  { kind: Encode, reason: MM_ENCOD_005, message }
}

///|
pub fn MailError::content_length(message : String) -> MailError {
  { kind: ContentLength, reason: MM_ENCOD_005, message }
}

///|
pub fn MailError::smtp(reason : ReasonCode, message : String) -> MailError {
  { kind: Smtp, reason, message }
}

///|
pub fn MailError::smtp_permanent(message : String) -> MailError {
  { kind: SmtpPermanent, reason: MM_SMTP_003, message }
}

///|
pub fn MailError::smtp_transient(message : String) -> MailError {
  { kind: SmtpTransient, reason: MM_SMTP_003, message }
}

///|
pub fn MailError::auth(message : String) -> MailError {
  { kind: Auth, reason: MM_AUTH_001, message }
}

///|
pub fn MailError::transport(message : String) -> MailError {
  { kind: Transport, reason: MM_TRANS_001, message }
}

///|
pub fn MailError::config(message : String) -> MailError {
  { kind: Config, reason: Other, message }
}

///|
pub fn MailError::timeout(message : String) -> MailError {
  { kind: Timeout, reason: Other, message }
}

///|
pub fn MailError::unsupported(message : String) -> MailError {
  { kind: Unsupported, reason: Other, message }
}

///|
pub fn MailError::io(message : String) -> MailError {
  { kind: Io, reason: MM_TRANS_001, message }
}

///|
pub fn MailError::message(self : MailError) -> String {
  self.message
}

///|
pub fn MailError::kind(self : MailError) -> MailErrorKind {
  self.kind
}

///|
pub fn MailError::reason(self : MailError) -> ReasonCode {
  self.reason
}

///|

///|
pub fn MailErrorKind::to_string(self : MailErrorKind) -> String {
  match self {
    InvalidAddress => "invalid_address"
    InvalidHeader => "invalid_header"
    HeaderInjection => "header_injection"
    Mime => "mime"
    Encode => "encode"
    ContentLength => "content_length"
    Smtp => "smtp"
    SmtpPermanent => "smtp_permanent"
    SmtpTransient => "smtp_transient"
    Auth => "auth"
    Transport => "transport"
    Config => "config"
    Timeout => "timeout"
    Unsupported => "unsupported"
    Io => "io"
  }
}

///|
pub impl Show for MailErrorKind with fn output(self, logger) {
  logger.write_string(self.to_string())
}

///|
pub impl Show for MailError with fn output(self, logger) {
  logger.write_string(
    "MailError(\{self.kind}, \{self.reason.to_string()}, \{self.message.escape(quote=true)})",
  )
}