///|
fn print_validation(
  report : @moonpay.MessageValidation,
  format : String,
) -> Unit {
  match format {
    "json" => println(report.to_json())
    "markdown" | "md" => println(report.to_markdown())
    _ => println(report.to_text())
  }
}

///|
fn print_reconciliation(
  report : @moonpay.ReconciliationReport,
  format : String,
) -> Unit {
  match format {
    "json" => println(report.to_json())
    "markdown" | "md" => println(report.to_markdown())
    _ => println(report.to_text())
  }
}

///|
fn print_lifecycle(
  report : @moonpay.LifecycleReport,
  format : String,
) -> Unit {
  match format {
    "json" => println(report.to_json())
    "markdown" | "md" => println(report.to_markdown())
    _ => println(report.to_text())
  }
}

///|
fn print_statement_audit(
  report : @moonpay.StatementAuditReport,
  format : String,
) -> Unit {
  match format {
    "json" => println(report.to_json())
    "markdown" | "md" => println(report.to_markdown())
    _ => println(report.to_text())
  }
}

///|
fn print_statement_portfolio(
  report : @moonpay.StatementPortfolioReport,
  format : String,
) -> Unit {
  match format {
    "json" => println(report.to_json())
    "markdown" | "md" => println(report.to_markdown())
    _ => println(report.to_text())
  }
}

///|
fn print_status_timeline(
  report : @moonpay.StatusTimelineReport,
  format : String,
) -> Unit {
  match format {
    "json" => println(report.to_json())
    "markdown" | "md" => println(report.to_markdown())
    _ => println(report.to_text())
  }
}

///|
fn print_return_ledger(
  report : @moonpay.ReturnLedgerReport,
  format : String,
) -> Unit {
  match format {
    "json" => println(report.to_json())
    "markdown" | "md" => println(report.to_markdown())
    _ => println(report.to_text())
  }
}

///|
fn read_format_and_paths(
  args : Array[String],
  start_index : Int,
) -> (String, Array[String]) {
  let mut format = "text"
  let paths : Array[String] = []
  let mut i = start_index
  while i < args.length() {
    if args[i] == "--format" && i + 1 < args.length() {
      format = args[i + 1]
      i += 2
    } else if args[i] == "--json" {
      format = "json"
      i += 1
    } else if args[i] == "--markdown" || args[i] == "--md" {
      format = "markdown"
      i += 1
    } else {
      paths.push(args[i])
      i += 1
    }
  }
  (format, paths)
}

///|
/// MoonPay Flow CLI entry point.
fn main {
  let args = @env.args()
  let program = if args.length() > 0 { args[0] } else { "moonpay-flow" }
  if args.length() <= 1 || args[1] == "--help" || args[1] == "-h" {
    println("MoonPay Flow \{@moonpay.version()}")
    println("Usage:")
    println("  \{program} [--format text|json|markdown] ")
    println("  \{program} reconcile   [--format ...]")
    println("  \{program} lifecycle    [--format ...]")
    println("  \{program} audit  [--format ...]")
    println("  \{program} audit-portfolio  [--format ...]")
    println("  \{program} timeline  [status.xml ...] [--format ...]")
    println("  \{program} return-ledger   [camt.053.xml ...] [--format ...]")
    println("  \{program} --list-rules")
    return
  }
  if args[1] == "--version" {
    println(@moonpay.version())
    return
  }
  if args[1] == "--list-rules" {
    for rule in @moonpay.rule_catalog() {
      println(rule.code + " - " + rule.description)
    }
    return
  }
  if args[1] == "reconcile" {
    let (format, paths) = read_format_and_paths(args, 2)
    if paths.length() < 2 {
      println("reconcile requires a pain.001 file and a status report file.")
      return
    }
    try {
      let pain_xml = @fs.read_file_to_string(paths[0])
      let status_xml = @fs.read_file_to_string(paths[1])
      let report = @moonpay.reconcile_pain001_status(pain_xml, status_xml)
      print_reconciliation(report, format)
    } catch {
      @fs.IOError(message) => println("Cannot read input: \{message}")
      @moonpay.XmlParseError(message) => println("Invalid XML: \{message}")
      _ => println("MoonPay Flow error")
    }
    return
  }
  if args[1] == "lifecycle" {
    let (format, paths) = read_format_and_paths(args, 2)
    if paths.length() < 3 {
      println("lifecycle requires pain.001, status and camt.053 files.")
      return
    }
    try {
      let pain_xml = @fs.read_file_to_string(paths[0])
      let status_xml = @fs.read_file_to_string(paths[1])
      let statement_xml = @fs.read_file_to_string(paths[2])
      let report = @moonpay.lifecycle_report(pain_xml, status_xml, statement_xml)
      print_lifecycle(report, format)
    } catch {
      @fs.IOError(message) => println("Cannot read input: \{message}")
      @moonpay.XmlParseError(message) => println("Invalid XML: \{message}")
      _ => println("MoonPay Flow error")
    }
    return
  }
  if args[1] == "audit" {
    let (format, paths) = read_format_and_paths(args, 2)
    if paths.length() < 1 {
      println("audit requires a camt.053 file.")
      return
    }
    try {
      let statement_xml = @fs.read_file_to_string(paths[0])
      let report = @moonpay.statement_audit(statement_xml)
      print_statement_audit(report, format)
    } catch {
      @fs.IOError(message) => println("Cannot read input: \{message}")
      @moonpay.XmlParseError(message) => println("Invalid XML: \{message}")
      _ => println("MoonPay Flow error")
    }
    return
  }
  if args[1] == "audit-portfolio" {
    let (format, paths) = read_format_and_paths(args, 2)
    if paths.length() < 1 {
      println("audit-portfolio requires a camt.053 file.")
      return
    }
    try {
      let statement_xml = @fs.read_file_to_string(paths[0])
      let report = @moonpay.statement_portfolio_audit(statement_xml)
      print_statement_portfolio(report, format)
    } catch {
      @fs.IOError(message) => println("Cannot read input: \{message}")
      @moonpay.XmlParseError(message) => println("Invalid XML: \{message}")
      _ => println("MoonPay Flow error")
    }
    return
  }
  if args[1] == "timeline" {
    let (format, paths) = read_format_and_paths(args, 2)
    if paths.length() < 1 {
      println("timeline requires a pain.001 or pacs.008 file.")
      return
    }
    try {
      let payment_xml = @fs.read_file_to_string(paths[0])
      let status_xmls : Array[String] = []
      for i = 1; i < paths.length(); i = i + 1 {
        status_xmls.push(@fs.read_file_to_string(paths[i]))
      }
      let report = @moonpay.status_timeline_report(payment_xml, status_xmls)
      print_status_timeline(report, format)
    } catch {
      @fs.IOError(message) => println("Cannot read input: \{message}")
      @moonpay.XmlParseError(message) => println("Invalid XML: \{message}")
      _ => println("MoonPay Flow error")
    }
    return
  }
  if args[1] == "return-ledger" {
    let (format, paths) = read_format_and_paths(args, 2)
    if paths.length() < 2 {
      println("return-ledger requires a pain.001 or pacs.008 file and at least one camt.053 file.")
      return
    }
    try {
      let payment_xml = @fs.read_file_to_string(paths[0])
      let statement_xmls : Array[String] = []
      for i = 1; i < paths.length(); i = i + 1 {
        statement_xmls.push(@fs.read_file_to_string(paths[i]))
      }
      let report = @moonpay.return_ledger_report(payment_xml, statement_xmls)
      print_return_ledger(report, format)
    } catch {
      @fs.IOError(message) => println("Cannot read input: \{message}")
      @moonpay.XmlParseError(message) => println("Invalid XML: \{message}")
      _ => println("MoonPay Flow error")
    }
    return
  }
  let (format, paths) = read_format_and_paths(args, 1)
  match paths {
    [] => println("Missing ISO 20022 XML path.")
    [file_path, ..] =>
      try {
        let xml = @fs.read_file_to_string(file_path)
        let report = @moonpay.validate_message(xml)
        print_validation(report, format)
      } catch {
        @fs.IOError(message) => println("Cannot read \{file_path}: \{message}")
        @moonpay.XmlParseError(message) => println("Invalid XML in \{file_path}: \{message}")
        _ => println("MoonPay Flow error")
      }
  }
}