///|
pub struct ArchiveKey {
  year : Int
  month : Int
  category : String
  sender_domain : String
} derive(Eq, Debug)

///|
pub struct ArchiveBucket {
  key : ArchiveKey
  messages : Array[MailMessage]
} derive(Eq, Debug)

///|
pub fn archive_key(message : MailMessage) -> ArchiveKey {
  let date = message_date(message)
  {
    year: date.year,
    month: date_month_number(date),
    category: category_name(classify_message(message)),
    sender_domain: sender_domain(message),
  }
}

///|
pub fn archive_key_string(key : ArchiveKey) -> String {
  key.year.to_string() +
  "/" +
  pad2(key.month) +
  "/" +
  key.category +
  "/" +
  key.sender_domain
}

///|
fn pad2(value : Int) -> String {
  if value < 10 {
    "0" + value.to_string()
  } else {
    value.to_string()
  }
}

///|
pub fn group_archive(messages : Array[MailMessage]) -> Array[ArchiveBucket] {
  let buckets : Array[ArchiveBucket] = []
  for message in messages {
    add_to_bucket(buckets, message)
  }
  buckets
}

///|
fn add_to_bucket(buckets : Array[ArchiveBucket], message : MailMessage) -> Unit {
  let key = archive_key(message)
  let key_text = archive_key_string(key)
  let mut i = 0
  while i < buckets.length() {
    if archive_key_string(buckets[i].key) == key_text {
      let next_messages : Array[MailMessage] = []
      for item in buckets[i].messages {
        next_messages.push(item)
      }
      next_messages.push(message)
      buckets[i] = { key: buckets[i].key, messages: next_messages }
      return
    }
    i = i + 1
  }
  buckets.push({ key, messages: [message] })
}

///|
pub fn archive_bucket_count(messages : Array[MailMessage]) -> Int {
  group_archive(messages).length()
}

///|
pub fn archive_subjects(bucket : ArchiveBucket) -> Array[String] {
  let out : Array[String] = []
  for message in bucket.messages {
    out.push(message.subject)
  }
  out
}

///|
pub fn find_archive_bucket(
  buckets : Array[ArchiveBucket],
  key_text : String,
) -> ArchiveBucket? {
  for bucket in buckets {
    if archive_key_string(bucket.key) == key_text {
      return Some(bucket)
    }
  }
  None
}

///|
pub fn archive_manifest(messages : Array[MailMessage]) -> String {
  let mut out = ""
  for bucket in group_archive(messages) {
    out = out +
      archive_key_string(bucket.key) +
      " " +
      bucket.messages.length().to_string() +
      "\n"
  }
  out
}

///|
pub fn archive_by_category(
  messages : Array[MailMessage],
  category : MailCategory,
) -> Array[MailMessage] {
  let out : Array[MailMessage] = []
  for message in messages {
    if classify_message(message) == category {
      out.push(message)
    }
  }
  out
}

///|
pub fn archive_by_year_month(
  messages : Array[MailMessage],
  year : Int,
  month : Int,
) -> Array[MailMessage] {
  let out : Array[MailMessage] = []
  for message in messages {
    let key = archive_key(message)
    if key.year == year && key.month == month {
      out.push(message)
    }
  }
  out
}

///|
pub fn archive_by_sender_domain(
  messages : Array[MailMessage],
  domain : String,
) -> Array[MailMessage] {
  let out : Array[MailMessage] = []
  let target = lower_ascii(domain)
  for message in messages {
    if sender_domain(message) == target {
      out.push(message)
    }
  }
  out
}

///|
pub fn archive_health_report(messages : Array[MailMessage]) -> String {
  let mut out = "archive messages=" + messages.length().to_string()
  out = out + " buckets=" + archive_bucket_count(messages).to_string()
  out = out + " risky=" + risky_archive_count(messages).to_string()
  out = out + " errors=" + archive_lint_error_count(messages).to_string()
  out
}

///|
fn risky_archive_count(messages : Array[MailMessage]) -> Int {
  let mut count = 0
  for message in messages {
    if is_high_risk(message) {
      count = count + 1
    }
  }
  count
}

///|
fn archive_lint_error_count(messages : Array[MailMessage]) -> Int {
  let mut count = 0
  for message in messages {
    if error_count(lint_message(message)) > 0 {
      count = count + 1
    }
  }
  count
}