///|
/// Prints concise command usage.
fn print_usage() -> Unit {
  println("MoonRRule - timezone-safe scheduling toolkit")
  println("")
  println("Usage:")
  println("  moonrrule rrule   [COUNT]")
  println("  moonrrule cron   [COUNT]")
  println("  moonrrule zone  ")
  println("  moonrrule validate-rrule ")
  println("  moonrrule validate-cron ")
  println("")
  println("Examples:")
  println("  moonrrule rrule \"FREQ=DAILY;COUNT=3\" 2026-08-01T09:00:00Z")
  println("  moonrrule cron \"0 9 * * MON-FRI\" 2026-08-01T00:00:00Z 5")
  println("  moonrrule zone Asia/Shanghai 2026-08-01T01:00:00Z")
}

///|
/// Parses an optional positive output count.
fn output_count(args : Array[String], index : Int) -> Int {
  if args.length() <= index {
    return 10
  }
  let value = @string.parse_int(args[index]) catch { _ => return 10 }
  value.max(1).min(1000)
}

///|
/// Executes RRULE expansion.
fn run_rrule(args : Array[String]) -> Unit {
  if args.length() < 4 {
    println("error: rrule requires RULE and DTSTART")
    return
  }
  let rule = @rrule.parse(args[2]) catch {
    @rrule.RuleError(message) => {
      println("invalid RRULE: " + message)
      return
    }
  }
  let start = @calendar.parse_local_datetime(args[3]) catch {
    @calendar.CalendarError(message) => {
      println("invalid DTSTART: " + message)
      return
    }
  }
  let count = output_count(args, 4)
  let values = @rrule.take(rule, start, count) catch {
    @rrule.RuleError(message) => {
      println("generation failed: " + message)
      return
    }
  }
  println("rule=" + rule.to_string())
  println("dtstart=" + start.to_iso_string())
  for index, value in values {
    println((index + 1).to_string() + "\t" + value.to_iso_string())
  }
}

///|
/// Executes Cron expansion.
fn run_cron(args : Array[String]) -> Unit {
  if args.length() < 4 {
    println("error: cron requires EXPRESSION and AFTER")
    return
  }
  let expression = @cron.parse(args[2]) catch {
    @cron.CronError(message) => {
      println("invalid Cron expression: " + message)
      return
    }
  }
  let after = @calendar.parse_local_datetime(args[3]) catch {
    @calendar.CalendarError(message) => {
      println("invalid AFTER value: " + message)
      return
    }
  }
  let count = output_count(args, 4)
  let values = expression.next_n(after, count) catch {
    @cron.CronError(message) => {
      println("Cron search failed: " + message)
      return
    }
  }
  println("cron=" + expression.to_string())
  for index, value in values {
    println((index + 1).to_string() + "\t" + value.to_iso_string())
  }
}

///|
/// Converts a UTC instant to one built-in timezone.
fn run_zone(args : Array[String]) -> Unit {
  if args.length() < 4 {
    println("error: zone requires ZONE and UTC-INSTANT")
    return
  }
  guard @timezone.by_name(args[2]) is Some(zone) else {
    println(
      "unknown zone; built-ins: UTC, Asia/Shanghai, Europe/Berlin, America/New_York",
    )
    return
  }
  let utc_value = @calendar.parse_local_datetime(args[3]) catch {
    @calendar.CalendarError(message) => {
      println("invalid UTC instant: " + message)
      return
    }
  }
  let instant = utc_value.to_utc_instant()
  let wall_time = zone.to_local(instant) catch {
    @timezone.TimeZoneError(message) => {
      println("timezone conversion failed: " + message)
      return
    }
  }
  println("zone=" + zone.name())
  println("utc=" + utc_value.to_iso_string() + "Z")
  println("local=" + wall_time.to_iso_string())
  println("offset=" + zone.offset_at(instant).to_string())
}

///|
/// Validates and canonicalizes one RRULE.
fn validate_rrule(args : Array[String]) -> Unit {
  if args.length() < 3 {
    println("error: validate-rrule requires RULE")
    return
  }
  let rule = @rrule.parse(args[2]) catch {
    @rrule.RuleError(message) => {
      println("invalid\n" + message)
      return
    }
  }
  println("valid\n" + rule.to_string())
}

///|
/// Validates and explains one Cron expression.
fn validate_cron(args : Array[String]) -> Unit {
  if args.length() < 3 {
    println("error: validate-cron requires EXPRESSION")
    return
  }
  let expression = @cron.parse(args[2]) catch {
    @cron.CronError(message) => {
      println("invalid\n" + message)
      return
    }
  }
  println("valid\n" + expression.explain())
}

///|
/// MoonRRule command entry point.
fn main {
  let args = @env.args()
  if args.length() < 2 {
    print_usage()
    return
  }
  match args[1] {
    "rrule" => run_rrule(args)
    "cron" => run_cron(args)
    "zone" => run_zone(args)
    "validate-rrule" => validate_rrule(args)
    "validate-cron" => validate_cron(args)
    "help" | "--help" | "-h" => print_usage()
    command => {
      println("error: unknown command '" + command + "'")
      print_usage()
    }
  }
}