///|
/// Parsed command-line options kept private to the executable package.
struct CliOptions {
  action : String
  format : String
  cleaning : String
  data : String
  sample_rate : Double
  repetitions : Int
}

///|
suberror CliError {
  InvalidData
}

///|
fn print_usage() -> Unit {
  println("moonbit-hrvkit ")
  println("  --action ")
  println("  --format      Output format (default: csv)")
  println("  --cleaning ")
  println("  --sample-rate       Tachogram rate for report (default: 4)")
  println("  --repetitions        Benchmark repetitions (default: 10)")
  println("  --data     CSV or JSON input")
}

///|
fn parse_cli_options(args : Array[String]) -> CliOptions {
  let mut action = ""
  let mut format = "csv"
  let mut cleaning = "remove"
  let mut data = ""
  let mut sample_rate = 4.0
  let mut repetitions = 10
  let mut i = 1
  while i < args.length() {
    if args[i] == "--action" && i + 1 < args.length() {
      action = args[i + 1]
      i += 2
    } else if args[i] == "--format" && i + 1 < args.length() {
      format = args[i + 1]
      i += 2
    } else if args[i] == "--cleaning" && i + 1 < args.length() {
      cleaning = args[i + 1]
      i += 2
    } else if args[i] == "--data" && i + 1 < args.length() {
      data = args[i + 1]
      i += 2
    } else if args[i] == "--sample-rate" && i + 1 < args.length() {
      sample_rate = @strconv.from_str(args[i + 1]) catch { _ => 4.0 }
      i += 2
    } else if args[i] == "--repetitions" && i + 1 < args.length() {
      repetitions = @strconv.from_str(args[i + 1]) catch { _ => 10 }
      i += 2
    } else {
      i += 1
    }
  }
  { action, format, cleaning, data, sample_rate, repetitions }
}

///|
fn cleaning_method(name : String) -> @hrvkit.CleaningMethod {
  match name {
    "median" => @hrvkit.InterpolateLocalMedian
    "linear" => @hrvkit.InterpolateLinear
    _ => @hrvkit.Remove
  }
}

///|
fn parse_intervals(options : CliOptions) -> Array[Double] raise CliError {
  if options.format == "json" {
    try {
      let json_data = @json.parse(options.data)
      @json.from_json(json_data)
    } catch {
      _ => raise CliError::InvalidData
    }
  } else {
    @hrvkit.parse_rr_csv(options.data) catch {
      _ => raise CliError::InvalidData
    }
  }
}

///|
fn emit_clean(options : CliOptions, config : @hrvkit.HrvConfig) -> Unit {
  try {
    let intervals = parse_intervals(options)
    let (cleaned, quality) = @hrvkit.clean_rr_intervals(
      intervals,
      cleaning_method(options.cleaning),
      config,
    )
    if options.format == "json" {
      let output : Map[String, Json] = Default::default()
      output.set("cleaned", cleaned.to_json())
      output.set("quality", quality.to_json())
      println(Json::object(output).stringify())
    } else {
      let row = []
      for value in cleaned {
        row.push(value.to_string())
      }
      println(@hrvkit.to_csv([row]))
    }
  } catch {
    _ => println("error: invalid interval data")
  }
}

///|
fn emit_metrics(options : CliOptions, config : @hrvkit.HrvConfig) -> Unit {
  try {
    let intervals = parse_intervals(options)
    let (cleaned, quality) = @hrvkit.clean_rr_intervals(
      intervals,
      cleaning_method(options.cleaning),
      config,
    )
    let metrics = @hrvkit.calculate_metrics(cleaned, config, quality)
    if options.format == "json" {
      println(metrics.to_json().stringify())
    } else {
      println(@hrvkit.serialize_metrics_csv(metrics))
    }
  } catch {
    _ => println("error: invalid interval data")
  }
}

///|
fn emit_trends(options : CliOptions, config : @hrvkit.HrvConfig) -> Unit {
  try {
    let history : Array[@hrvkit.MorningMeasurement] = if options.format ==
      "json" {
      @json.from_json(@json.parse(options.data))
    } else {
      @hrvkit.parse_morning_csv(options.data)
    }
    let trends = @hrvkit.calculate_morning_trends(
      history,
      config.rolling_window,
      config,
    )
    if options.format == "json" {
      println(trends.to_json().stringify())
    } else {
      println(@hrvkit.serialize_trends_csv(trends))
    }
  } catch {
    _ => println("error: invalid morning measurement data")
  }
}

///|
fn emit_report(options : CliOptions, config : @hrvkit.HrvConfig) -> Unit {
  try {
    let intervals = parse_intervals(options)
    let defaults = @hrvkit.AnalysisOptions::default()
    let report_options : @hrvkit.AnalysisOptions = {
      cleaning_method: cleaning_method(options.cleaning),
      sample_rate_hz: options.sample_rate,
      remove_trend: defaults.remove_trend,
      window_function: defaults.window_function,
      segment_size: defaults.segment_size,
      segment_hop: defaults.segment_hop,
      nonlinear_enabled: defaults.nonlinear_enabled,
    }
    let report = @hrvkit.analyze_rr(intervals, config, report_options)
    if options.format == "json" {
      println(report.to_json().stringify())
    } else {
      println(@hrvkit.report_headline_csv(report))
      println(@hrvkit.report_summary(report))
    }
  } catch {
    _ => println("error: invalid interval data")
  }
}

///|
fn emit_quality(options : CliOptions, config : @hrvkit.HrvConfig) -> Unit {
  try {
    let intervals = parse_intervals(options)
    let diagnostic = @hrvkit.diagnose_signal(intervals, config)
    if options.format == "json" {
      println(diagnostic.to_json().stringify())
    } else {
      println(@hrvkit.diagnostic_csv(diagnostic))
      println(@hrvkit.diagnostic_recommendation(diagnostic))
    }
  } catch {
    _ => println("error: invalid interval data")
  }
}

///|
fn emit_benchmark(options : CliOptions, _config : @hrvkit.HrvConfig) -> Unit {
  let repetitions = if options.repetitions < 1 {
    1
  } else {
    options.repetitions
  }
  let benchmark_config : @hrvkit.BenchmarkConfig = {
    recording_length: 256,
    repetitions,
    baseline_rr: 800.0,
    sample_rate_hz: options.sample_rate,
  }
  let summary = @hrvkit.run_benchmark(benchmark_config)
  if options.format == "json" {
    println(summary.to_json().stringify())
  } else {
    println(@hrvkit.benchmark_csv(summary))
  }
}

///|
fn main {
  let options = parse_cli_options(@env.args())
  if options.action == "" ||
    options.action == "help" ||
    options.action == "--help" {
    print_usage()
    return
  }
  if options.action == "version" {
    println("moonbit-hrvkit 0.3.0")
    return
  }
  let config = @hrvkit.HrvConfig::default()
  match options.action {
    "clean" => emit_clean(options, config)
    "metrics" => emit_metrics(options, config)
    "trends" => emit_trends(options, config)
    "report" => emit_report(options, config)
    "quality" => emit_quality(options, config)
    "benchmark" => emit_benchmark(options, config)
    _ => print_usage()
  }
}