///|
fn level_label(level : Level) -> String {
  level_to_string(level).trim().to_owned()
}

///|
fn json_quote(text : String) -> String {
  Json::string(text).stringify()
}

///|
fn escape_xml_text(text : String) -> String {
  escape_xml_chars(text.to_array(), 0, "")
}

///|
fn escape_xml_chars(chars : Array[Char], idx : Int, result : String) -> String {
  if idx >= chars.length() {
    result
  } else {
    let ch = chars[idx]
    let escaped = if ch == '&' {
      "&"
    } else if ch == '<' {
      "<"
    } else if ch == '>' {
      ">"
    } else if ch == '"' {
      """
    } else if ch == '\'' {
      "'"
    } else if ch.is_control() && ch != '\t' && ch != '\n' && ch != '\r' {
      "�"
    } else {
      ch.to_string()
    }
    escape_xml_chars(chars, idx + 1, result + escaped)
  }
}

///|
fn escape_logfmt_value(text : String) -> String {
  let chars = text.to_array()
  let escaped = escape_logfmt_chars(chars, 0, "")
  if chars.length() == 0 || should_quote_logfmt(chars, 0) {
    "\"" + escaped + "\""
  } else {
    escaped
  }
}

///|
fn escape_logfmt_chars(
  chars : Array[Char],
  idx : Int,
  result : String,
) -> String {
  if idx >= chars.length() {
    result
  } else {
    let ch = chars[idx]
    let escaped = if ch == '\\' {
      "\\\\"
    } else if ch == '"' {
      "\\\""
    } else if ch == '\n' {
      "\\n"
    } else if ch == '\r' {
      "\\r"
    } else if ch == '\t' {
      "\\t"
    } else if ch.is_control() {
      "_"
    } else {
      ch.to_string()
    }
    escape_logfmt_chars(chars, idx + 1, result + escaped)
  }
}

///|
fn should_quote_logfmt(chars : Array[Char], idx : Int) -> Bool {
  if idx >= chars.length() {
    false
  } else {
    let ch = chars[idx]
    if ch.is_whitespace() ||
      ch.is_control() ||
      ch == '=' ||
      ch == '"' ||
      ch == '\\' {
      true
    } else {
      should_quote_logfmt(chars, idx + 1)
    }
  }
}

///|
fn sanitize_logfmt_key(text : String) -> String {
  let chars = text.to_array()
  if chars.length() == 0 {
    "_"
  } else {
    sanitize_logfmt_key_chars(chars, 0, "")
  }
}

///|
fn sanitize_logfmt_key_chars(
  chars : Array[Char],
  idx : Int,
  result : String,
) -> String {
  if idx >= chars.length() {
    result
  } else {
    let ch = chars[idx]
    let safe = if ch.is_whitespace() ||
      ch.is_control() ||
      ch == '=' ||
      ch == '"' ||
      ch == '\\' {
      "_"
    } else {
      ch.to_string()
    }
    sanitize_logfmt_key_chars(chars, idx + 1, result + safe)
  }
}

///|
pub fn format_text(record : LogRecord) -> String {
  let result = "[" +
    record.timestamp.to_string() +
    "] " +
    level_to_string(record.level) +
    "  " +
    record.message
  format_fields(result, record.fields, 0)
}

///|
fn format_fields(
  result : String,
  fields : Array[(String, String)],
  idx : Int,
) -> String {
  if idx >= fields.length() {
    result
  } else {
    let (k, v) = fields[idx]
    let new_result = result + "  " + k + "=" + v
    format_fields(new_result, fields, idx + 1)
  }
}

///|
pub fn format_json(record : LogRecord) -> String {
  let result = "{\"time\":" +
    json_quote(record.timestamp.to_string()) +
    ",\"level\":" +
    json_quote(level_label(record.level)) +
    ",\"msg\":" +
    json_quote(record.message)
  format_json_fields(result, record.fields, 0)
}

///|
fn format_json_fields(
  result : String,
  fields : Array[(String, String)],
  idx : Int,
) -> String {
  if idx >= fields.length() {
    result + "}"
  } else {
    let (k, v) = fields[idx]
    let new_result = result + "," + json_quote(k) + ":" + json_quote(v)
    format_json_fields(new_result, fields, idx + 1)
  }
}

// ANSI color codes for terminal output

///|
fn ansi_color(level : Level) -> String {
  match level {
    Level::TRACE => ""
    Level::DEBUG => ""
    Level::INFO => ""
    Level::WARN => ""
    Level::ERROR => ""
  }
}

///|
fn ansi_reset() -> String {
  ""
}

///|
pub fn format_color(record : LogRecord) -> String {
  let color = ansi_color(record.level)
  let result = color +
    "[" +
    record.timestamp.to_string() +
    "] " +
    level_to_string(record.level) +
    "  " +
    record.message +
    ansi_reset()
  format_fields(result, record.fields, 0)
}

// Compact single-line format: LEVEL|timestamp|msg|k=v|k=v

///|
pub fn format_compact(record : LogRecord) -> String {
  let result = level_label(record.level) +
    "|" +
    record.timestamp.to_string() +
    "|" +
    record.message
  format_compact_fields(result, record.fields, 0)
}

///|
fn format_compact_fields(
  result : String,
  fields : Array[(String, String)],
  idx : Int,
) -> String {
  if idx >= fields.length() {
    result
  } else {
    let (k, v) = fields[idx]
    let new_result = result + "|" + k + "=" + v
    format_compact_fields(new_result, fields, idx + 1)
  }
}

// CSV format: timestamp,level,message,key1,val1,key2,val2,...

///|
pub fn format_csv(record : LogRecord) -> String {
  let result = escape_csv_field(record.timestamp.to_string()) +
    "," +
    escape_csv_field(level_label(record.level)) +
    "," +
    escape_csv_field(record.message)
  format_csv_fields(result, record.fields, 0)
}

///|
fn format_csv_fields(
  result : String,
  fields : Array[(String, String)],
  idx : Int,
) -> String {
  if idx >= fields.length() {
    result
  } else {
    let (k, v) = fields[idx]
    let new_result = result +
      "," +
      escape_csv_field(k) +
      "," +
      escape_csv_field(v)
    format_csv_fields(new_result, fields, idx + 1)
  }
}

///|
fn escape_csv_field(text : String) -> String {
  let chars = text.to_array()
  if csv_needs_quotes(chars, 0) {
    "\"" + escape_csv_quotes(chars, 0, "") + "\""
  } else {
    text
  }
}

///|
fn csv_needs_quotes(chars : Array[Char], idx : Int) -> Bool {
  if idx >= chars.length() {
    false
  } else {
    let ch = chars[idx]
    if ch == ',' || ch == '"' || ch == '\n' || ch == '\r' {
      true
    } else {
      csv_needs_quotes(chars, idx + 1)
    }
  }
}

///|
fn escape_csv_quotes(chars : Array[Char], idx : Int, result : String) -> String {
  if idx >= chars.length() {
    result
  } else {
    let ch = chars[idx]
    let escaped = if ch == '"' { "\"\"" } else { ch.to_string() }
    escape_csv_quotes(chars, idx + 1, result + escaped)
  }
}

// Format without timestamp (for testing)

///|
pub fn format_plain(record : LogRecord) -> String {
  let result = level_label(record.level) + " " + record.message
  format_fields(result, record.fields, 0)
}

// Pattern formatter with configurable format
// Supports: {t} = timestamp, {l} = level, {m} = message, {f} = fields

///|
pub fn format_pattern(record : LogRecord, pattern : String) -> String {
  replace_pattern(pattern, record, 0, "")
}

///|
fn replace_pattern(
  pattern : String,
  record : LogRecord,
  idx : Int,
  result : String,
) -> String {
  if idx >= pattern.length() {
    result
  } else {
    let ch = pattern[idx:idx + 1].to_owned()
    if ch == "{" {
      if idx + 2 < pattern.length() &&
        pattern[idx + 2:idx + 3].to_owned() == "}" {
        let key = pattern[idx + 1:idx + 2].to_owned()
        let replacement = get_pattern_value(key, record)
        replace_pattern(pattern, record, idx + 3, result + replacement)
      } else {
        replace_pattern(pattern, record, idx + 1, result + ch)
      }
    } else {
      replace_pattern(pattern, record, idx + 1, result + ch)
    }
  }
}

///|
fn get_pattern_value(key : String, record : LogRecord) -> String {
  if key == "t" {
    record.timestamp.to_string()
  } else if key == "l" {
    level_label(record.level)
  } else if key == "m" {
    record.message
  } else if key == "f" {
    format_all_fields(record.fields, 0, "")
  } else {
    "{" + key + "}"
  }
}

///|
fn format_all_fields(
  fields : Array[(String, String)],
  idx : Int,
  result : String,
) -> String {
  if idx >= fields.length() {
    result
  } else {
    let (k, v) = fields[idx]
    let sep = if idx > 0 { " " } else { "" }
    format_all_fields(fields, idx + 1, result + sep + k + "=" + v)
  }
}

// Logfmt format: key=value pairs, level and message as special keys

///|
pub fn format_logfmt(record : LogRecord) -> String {
  let result = "ts=" +
    record.timestamp.to_string() +
    " level=" +
    level_label(record.level) +
    " msg=" +
    escape_logfmt_value(record.message)
  format_logfmt_fields(result, record.fields, 0)
}

///|
fn format_logfmt_fields(
  result : String,
  fields : Array[(String, String)],
  idx : Int,
) -> String {
  if idx >= fields.length() {
    result
  } else {
    let (k, v) = fields[idx]
    let new_result = result +
      " " +
      sanitize_logfmt_key(k) +
      "=" +
      escape_logfmt_value(v)
    format_logfmt_fields(new_result, fields, idx + 1)
  }
}

// XML format

///|
pub fn format_xml(record : LogRecord) -> String {
  let result = "" +
    escape_xml_text(record.message) +
    ""
  format_xml_fields(result, record.fields, 0) + ""
}

///|
fn format_xml_fields(
  result : String,
  fields : Array[(String, String)],
  idx : Int,
) -> String {
  if idx >= fields.length() {
    result
  } else {
    let (k, v) = fields[idx]
    let new_result = result +
      "" +
      escape_xml_text(v) +
      ""
    format_xml_fields(new_result, fields, idx + 1)
  }
}

// NDJSON (Newline Delimited JSON) — same as JSON but guaranteed single line

///|
pub fn format_ndjson(record : LogRecord) -> String {
  format_json(record)
}