///|
pub enum TransferEncoding {
  SevenBit
  EightBit
  Binary
  Base64
  QuotedPrintable
  UnknownEncoding(String)
} derive(Eq, Debug)

///|
pub enum BodyKind {
  PlainText
  HtmlText
  Multipart(String)
  Attachment
  UnknownBody
} derive(Eq, Debug)

///|
pub struct Header {
  name : String
  value : String
} derive(Eq, Debug)

///|
pub struct Address {
  display : String
  mailbox : String
} derive(Eq, Debug)

///|
pub struct ContentType {
  media_type : String
  subtype : String
  charset : String
  boundary : String
  name : String
  params : Array[(String, String)]
} derive(Eq, Debug)

///|
pub struct MailPart {
  headers : Array[Header]
  content_type : ContentType
  encoding : TransferEncoding
  disposition : String
  filename : String
  raw_body : String
  decoded_body : String
  children : Array[MailPart]
} derive(Eq, Debug)

///|
pub struct MailMessage {
  headers : Array[Header]
  from : Array[Address]
  to : Array[Address]
  cc : Array[Address]
  subject : String
  date : String
  message_id : String
  root : MailPart
} derive(Eq, Debug)

///|
pub struct ParseIssue {
  code : String
  message : String
} derive(Eq, Debug)

///|
pub struct ParseResult {
  message : MailMessage
  issues : Array[ParseIssue]
} derive(Eq, Debug)

///|
pub fn header(name : String, value : String) -> Header {
  { name, value }
}

///|
pub fn address(display : String, mailbox : String) -> Address {
  { display, mailbox }
}

///|
pub fn default_content_type() -> ContentType {
  {
    media_type: "text",
    subtype: "plain",
    charset: "us-ascii",
    boundary: "",
    name: "",
    params: [],
  }
}

///|
pub fn content_type(
  media_type : String,
  subtype : String,
  charset : String,
  boundary : String,
  name : String,
  params : Array[(String, String)],
) -> ContentType {
  { media_type, subtype, charset, boundary, name, params }
}

///|
pub fn empty_part() -> MailPart {
  {
    headers: [],
    content_type: default_content_type(),
    encoding: SevenBit,
    disposition: "",
    filename: "",
    raw_body: "",
    decoded_body: "",
    children: [],
  }
}

///|
pub fn mail_part(
  headers : Array[Header],
  content_type : ContentType,
  encoding : TransferEncoding,
  disposition : String,
  filename : String,
  raw_body : String,
  decoded_body : String,
  children : Array[MailPart],
) -> MailPart {
  {
    headers,
    content_type,
    encoding,
    disposition,
    filename,
    raw_body,
    decoded_body,
    children,
  }
}

///|
pub fn empty_message() -> MailMessage {
  {
    headers: [],
    from: [],
    to: [],
    cc: [],
    subject: "",
    date: "",
    message_id: "",
    root: empty_part(),
  }
}

///|
pub fn parse_issue(code : String, message : String) -> ParseIssue {
  { code, message }
}

///|
pub fn body_kind(part : MailPart) -> BodyKind {
  if part.content_type.media_type == "multipart" {
    Multipart(part.content_type.subtype)
  } else if part.disposition == "attachment" {
    Attachment
  } else if part.content_type.media_type == "text" &&
    part.content_type.subtype == "plain" {
    PlainText
  } else if part.content_type.media_type == "text" &&
    part.content_type.subtype == "html" {
    HtmlText
  } else {
    UnknownBody
  }
}

///|
pub fn is_multipart(part : MailPart) -> Bool {
  part.content_type.media_type == "multipart" &&
  part.content_type.boundary != ""
}

///|
pub fn is_attachment(part : MailPart) -> Bool {
  part.disposition == "attachment" || part.filename != ""
}

///|
pub fn is_text(part : MailPart) -> Bool {
  part.content_type.media_type == "text"
}

///|
pub fn is_plain(part : MailPart) -> Bool {
  part.content_type.media_type == "text" && part.content_type.subtype == "plain"
}

///|
pub fn is_html(part : MailPart) -> Bool {
  part.content_type.media_type == "text" && part.content_type.subtype == "html"
}

///|
pub fn has_issues(result : ParseResult) -> Bool {
  result.issues.length() > 0
}