///|
fn demo_series() -> Array[@model.Series] {
  [
    @model.Series::new(
      "http_requests_total",
      @model.LabelSet::new([("job", "api"), ("instance", "api-1")]),
      [
        { timestamp: 0.0, value: 10.0, },
        { timestamp: 60000.0, value: 20.0, },
        { timestamp: 120000.0, value: 35.0, },
        { timestamp: 180000.0, value: 55.0, },
        { timestamp: 240000.0, value: 80.0, },
        { timestamp: 300000.0, value: 110.0, },
      ],
    ),
    @model.Series::new(
      "http_requests_total",
      @model.LabelSet::new([("job", "api"), ("instance", "api-2")]),
      [
        { timestamp: 0.0, value: 5.0, },
        { timestamp: 60000.0, value: 9.0, },
        { timestamp: 120000.0, value: 15.0, },
        { timestamp: 180000.0, value: 23.0, },
        { timestamp: 240000.0, value: 32.0, },
        { timestamp: 300000.0, value: 44.0, },
      ],
    ),
  ]
}

///|
fn print_help() -> Unit {
  println("MoonPromQL")
  println("")
  println("Usage:")
  println("  moon run cmd/moonpromql -- eval  [--at ]")
  println(
    "  moon run cmd/moonpromql -- eval-json   [--at ]",
  )
  println("  moon run cmd/moonpromql -- parse ")
  println("  moon run cmd/moonpromql -- demo")
  println("")
  println("Examples:")
  println(
    "  moon run cmd/moonpromql -- eval \"sum by (job) (rate(http_requests_total[5m]))\" --at 300000",
  )
  println(
    "  moon run cmd/moonpromql -- eval \"http_requests_total{instance=\\\"api-1\\\"}\" --at 300000",
  )
}

///|
fn parse_timestamp(value : String) -> Double raise @model.PromqlError {
  @string.parse_double(value) catch {
    _ => raise @model.EvalError("invalid --at timestamp `\{value}`")
  }
}

///|
fn run_eval(query : String, eval_time : Double) -> Unit {
  let context = @eval.EvalContext::new(demo_series(), eval_time)
  let result = @eval.query(query, context) catch {
    error => {
      println("error: \{error}")
      abort("query failed")
    }
  }
  println(result.to_string())
}

///|
fn run_eval_json(
  query : String,
  series_json : String,
  eval_time : Double,
) -> Unit {
  let series = @adapters_json.parse_series(series_json) catch {
    error => {
      println("error: \{error}")
      abort("query failed")
    }
  }
  let context = @eval.EvalContext::new(series, eval_time)
  let result = @eval.query(query, context) catch {
    error => {
      println("error: \{error}")
      abort("query failed")
    }
  }
  println(result.to_string())
}

///|
fn run_parse(query : String) -> Unit {
  let expression = @parser.parse(query) catch {
    error => {
      println("error: \{error}")
      abort("query failed")
    }
  }
  let kind = match expression {
    @ast.Number(_) => "number"
    @ast.StringLiteral(_) => "string"
    @ast.Selector(_) => "selector"
    @ast.Call(_, _) => "function call"
    @ast.Aggregate(_, _, _, _) => "aggregation"
    @ast.Unary(_, _) => "unary expression"
    @ast.Binary(_, _, _, _, _) => "binary expression"
  }
  println("parsed \{kind}")
}

///|
fn main {
  let args = @env.args()
  if args.length() < 2 {
    print_help()
    return
  }
  match args[1] {
    "help" | "--help" | "-h" => print_help()
    "demo" => run_eval("rate(http_requests_total[5m])", 300000.0)
    "parse" =>
      if args.length() < 3 {
        abort("error: parse requires a query")
      } else {
        run_parse(args[2])
      }
    "eval" =>
      if args.length() < 3 {
        abort("error: eval requires a query")
      } else {
        let mut eval_time = 300000.0
        if args.length() >= 5 && args[3] == "--at" {
          eval_time = parse_timestamp(args[4]) catch {
            error => {
              println("error: \{error}")
              abort("query failed")
            }
          }
        }
        run_eval(args[2], eval_time)
      }
    "eval-json" =>
      if args.length() < 4 {
        abort("error: eval-json requires a query and a JSON series fixture")
      } else {
        let mut eval_time = 300000.0
        if args.length() >= 6 && args[4] == "--at" {
          eval_time = parse_timestamp(args[5]) catch {
            error => {
              println("error: \{error}")
              abort("query failed")
            }
          }
        }
        run_eval_json(args[2], args[3], eval_time)
      }
    _ => abort("error: unknown command; use --help")
  }
}