///|
/// Native gateway executor.
///
/// This package is the MoonBit-native entrypoint for executing vcdb gateway
/// requests without the JavaScript runtime adapter.

// Uses `async fn main` — moonbitlang/async event loop is started automatically.

///|
pub struct NativeGateway {
  manager : @gateway.CollectionManager[@storage.MemoryStorage]
}

///|
pub fn NativeGateway::new() -> NativeGateway {
  let storage = @storage.MemoryStorage::new()
  { manager: @gateway.CollectionManager::with_storage(storage) }
}

///|
pub async fn NativeGateway::execute(
  self : NativeGateway,
  matches : @argparse.Matches,
) -> @gateway.ApiResponse {
  match matches.subcommand {
    Some(("healthz", _)) => self.request("GET", "/healthz", "")
    Some(("collections", sub_matches)) => self.execute_collections(sub_matches)
    Some(("points", sub_matches)) => self.execute_points(sub_matches)
    Some(("request", sub_matches)) => self.execute_raw_request(sub_matches)
    _ => @gateway.ApiResponse::err("Unknown command")
  }
}

///|
/// Check if the command is a direct CLI command (dump/load) that
/// bypasses the gateway and writes its own output.
pub async fn NativeGateway::try_execute_direct(
  _self : NativeGateway,
  matches : @argparse.Matches,
) -> Bool {
  match matches.subcommand {
    Some(("dump", sub)) => {
      execute_dump(sub)
      true
    }
    Some(("load", sub)) => {
      execute_load(sub)
      true
    }
    _ => false
  }
}

///|
async fn NativeGateway::execute_collections(
  self : NativeGateway,
  matches : @argparse.Matches,
) -> @gateway.ApiResponse {
  match matches.subcommand {
    Some(("list", _)) => self.request("GET", "/collections", "")
    Some(("create", sub)) => self.create_collection(sub)
    Some(("info", sub)) => {
      let name = match @cli.get_value(sub, "name") {
        Some(it) => it
        None => return @gateway.ApiResponse::err("Collection name is required")
      }
      self.request("GET", "/collections/" + name, "")
    }
    Some(("delete", sub)) => {
      let name = match @cli.get_value(sub, "name") {
        Some(it) => it
        None => return @gateway.ApiResponse::err("Collection name is required")
      }
      self.request("DELETE", "/collections/" + name, "")
    }
    _ => @gateway.ApiResponse::err("Unknown collections subcommand")
  }
}

///|
async fn NativeGateway::execute_points(
  self : NativeGateway,
  matches : @argparse.Matches,
) -> @gateway.ApiResponse {
  match matches.subcommand {
    Some(("upsert", sub)) => self.upsert_point(sub)
    Some(("get", sub)) => self.get_point(sub)
    Some(("delete", sub)) => self.delete_point(sub)
    Some(("search", sub)) => self.search_points(sub)
    _ => @gateway.ApiResponse::err("Unknown points subcommand")
  }
}

///|
pub async fn NativeGateway::run(
  self : NativeGateway,
  args : Array[String],
) -> String {
  let cmd = @cli.vcdb_command()
  let matches = cmd.parse(argv=args, env={}) catch { e => return e.to_string() }
  // Direct commands (dump/load) handle their own output
  if self.try_execute_direct(matches) {
    return ""
  }
  self.execute(matches).to_json().stringify()
}

///|
async fn main {
  let app = NativeGateway::new()
  let result = app.run(command_args()) catch { e => e.to_string() }
  if result.length() > 0 {
    println(result)
  }
}

///|
fn command_args() -> Array[String] {
  let argv = @env.args()
  if argv.length() <= 1 {
    []
  } else {
    let args : Array[String] = []
    for i in 1.. String {
  @gateway.json_object([
    (
      "points",
      @gateway.json_array([
        @gateway.json_object([
          ("id", id.to_double().to_json()),
          ("vector", @gateway.vector_to_json(vector)),
          ("payload", payload),
        ]),
      ]),
    ),
  ]).stringify()
}

///|
fn build_search_body(vector : Array[Double], limit : Int) -> String {
  @gateway.json_object([
    ("vector", @gateway.vector_to_json(vector)),
    ("limit", limit.to_double().to_json()),
    ("with_payload", true.to_json()),
  ]).stringify()
}

///|
fn build_collection_body(
  dim : Int,
  metric : @types.Metric,
  strategy : @types.Strategy,
) -> String {
  @gateway.json_object([
    ("dim", dim.to_double().to_json()),
    ("metric", metric.to_name().to_json()),
    ("strategy", strategy.to_name().to_json()),
  ]).stringify()
}

///|
async fn NativeGateway::request(
  self : NativeGateway,
  http_method : String,
  path : String,
  body : String,
) -> @gateway.ApiResponse {
  @gateway.execute_path_request(self.manager, http_method, path, body)
}

///|
async fn NativeGateway::create_collection(
  self : NativeGateway,
  matches : @argparse.Matches,
) -> @gateway.ApiResponse {
  let name = match @cli.get_value(matches, "name") {
    Some(it) => it
    None => return @gateway.ApiResponse::err("Collection name is required")
  }
  let dim = @cli.get_int_value_or(matches, "dim", 128)
  let metric = match @cli.get_value(matches, "metric") {
    Some(value) =>
      match @cli.parse_metric(value) {
        Some(it) => it
        None => return @gateway.ApiResponse::err("Invalid metric")
      }
    None => @types.Cosine
  }
  let strategy = match @cli.get_value(matches, "strategy") {
    Some(value) =>
      match @cli.parse_strategy(value) {
        Some(it) => it
        None => return @gateway.ApiResponse::err("Invalid strategy")
      }
    None => @types.Strategy::default_hnsw()
  }
  let body = build_collection_body(dim, metric, strategy)
  self.request("POST", "/collections/" + name, body)
}

///|
async fn NativeGateway::upsert_point(
  self : NativeGateway,
  matches : @argparse.Matches,
) -> @gateway.ApiResponse {
  let collection = match @cli.get_value(matches, "collection") {
    Some(it) => it
    None => return @gateway.ApiResponse::err("Collection name is required")
  }
  let id = match @cli.get_int_value(matches, "id") {
    Some(it) => it.to_int64()
    None => return @gateway.ApiResponse::err("Point ID is required")
  }
  let vector = match @cli.get_value(matches, "vector") {
    Some(value) =>
      match @cli.parse_vector(value) {
        Some(it) => it
        None => return @gateway.ApiResponse::err("Invalid vector")
      }
    None => return @gateway.ApiResponse::err("Vector is required")
  }
  let payload = match @cli.get_value(matches, "payload") {
    Some(value) => @cli.parse_payload(value)
    None => @gateway.json_object([])
  }
  let body = build_upsert_body(id, vector, payload)
  self.request("PUT", "/collections/" + collection + "/points", body)
}

///|
async fn NativeGateway::get_point(
  self : NativeGateway,
  matches : @argparse.Matches,
) -> @gateway.ApiResponse {
  let collection = match @cli.get_value(matches, "collection") {
    Some(it) => it
    None => return @gateway.ApiResponse::err("Collection name is required")
  }
  let id = match @cli.get_value(matches, "id") {
    Some(it) => it
    None => return @gateway.ApiResponse::err("Point ID is required")
  }
  self.request("GET", "/collections/" + collection + "/points/" + id, "")
}

///|
async fn NativeGateway::delete_point(
  self : NativeGateway,
  matches : @argparse.Matches,
) -> @gateway.ApiResponse {
  let collection = match @cli.get_value(matches, "collection") {
    Some(it) => it
    None => return @gateway.ApiResponse::err("Collection name is required")
  }
  let id = match @cli.get_value(matches, "id") {
    Some(it) => it
    None => return @gateway.ApiResponse::err("Point ID is required")
  }
  self.request("DELETE", "/collections/" + collection + "/points/" + id, "")
}

///|
async fn NativeGateway::search_points(
  self : NativeGateway,
  matches : @argparse.Matches,
) -> @gateway.ApiResponse {
  let collection = match @cli.get_value(matches, "collection") {
    Some(it) => it
    None => return @gateway.ApiResponse::err("Collection name is required")
  }
  let vector = match @cli.get_value(matches, "vector") {
    Some(value) =>
      match @cli.parse_vector(value) {
        Some(it) => it
        None => return @gateway.ApiResponse::err("Invalid vector")
      }
    None => return @gateway.ApiResponse::err("Vector is required")
  }
  let limit = @cli.get_int_value_or(matches, "k", 10)
  let body = build_search_body(vector, limit)
  self.request("POST", "/collections/" + collection + "/points/search", body)
}

///|
async fn NativeGateway::execute_raw_request(
  self : NativeGateway,
  matches : @argparse.Matches,
) -> @gateway.ApiResponse {
  let http_method = @cli.get_value_or(matches, "method", "GET")
  let path = @cli.get_value_or(matches, "path", "/healthz")
  let body = @cli.get_value_or(matches, "body", "")
  self.request(http_method, path, body)
}