///|
pub struct Gateway {
  priv host : String
  priv port : Int
  priv model : String
  priv logger : @pino.Logger
  priv started_at_ms : Int
  /// Auth
  priv auth : AuthService
  /// Proxy routing and circuit breakers
  priv proxy : ProxyService
  /// Usage tracking and cost calculation
  priv usage : UsageService
  /// App configuration and settings
  priv config : ConfigService
  /// Session management
  priv session : SessionService
  /// Local workspace and file dialog operations
  priv workspace : WorkspaceService
  /// App runtime status and local tool lifecycle operations
  priv runtime : RuntimeService
  /// Skill discovery, installation, migration, and repository operations
  priv skills : SkillService
  /// OpenClaw and Hermes provider config
  priv openclaw : OpenClawService
  /// Authentication for privileged local management operations.
  priv control : ControlPlaneAuth
}

///|
pub fn Gateway::new(
  port~ : Int,
  host? : String = @suite.default_host(),
  model? : String = @suite.default_model(),
) -> Gateway {
  let logger = @pino.logger("moongate", @pino.Transport::console())
  {
    host,
    port,
    model,
    logger,
    started_at_ms: @async.now().to_int(),
    auth: AuthService::new(),
    proxy: ProxyService::new(host, port),
    usage: UsageService::new(),
    config: ConfigService::new(),
    session: SessionService::new(),
    workspace: WorkspaceService::new(),
    runtime: RuntimeService::new(),
    skills: SkillService::new(),
    openclaw: OpenClawService::new(),
    control: ControlPlaneAuth::new(),
  }
}

///|
pub fn Gateway::host(self : Gateway) -> String {
  self.host
}

///|
pub fn Gateway::port(self : Gateway) -> Int {
  self.port
}

///|
fn moon_first_stream_event_ms(current : Int?, started_at_ms : Int?) -> Int? {
  match current {
    Some(_) => current
    None =>
      match started_at_ms {
        Some(started_at) => Some(max_int(@async.now().to_int() - started_at, 0))
        None => Some(0)
      }
  }
}

///|
fn ProviderRouteTarget::can_use_codex_compatible_upstream(
  self : ProviderRouteTarget,
) -> Bool {
  self.provider_type != Some("codex_oauth") &&
  self.base_url is Some(_) &&
  self.api_key is Some(_)
}

///|
fn ProviderRouteTarget::codex_uses_chat_completions(
  self : ProviderRouteTarget,
) -> Bool {
  if self.api_format == Some("openai_chat") {
    return true
  }
  match self.base_url {
    Some(base_url) => base_url.trim().has_suffix("/chat/completions")
    None => false
  }
}

///|
fn ProviderRouteTarget::can_use_claude_compatible_upstream(
  self : ProviderRouteTarget,
) -> Bool {
  self.provider_type != Some("codex_oauth") &&
  self.base_url is Some(_) &&
  self.api_key is Some(_)
}

///|
fn ProviderRouteTarget::claude_api_format(self : ProviderRouteTarget) -> String {
  if self.provider_type == Some("codex_oauth") {
    return "openai_responses"
  }
  match self.api_format {
    Some("openai_chat") => "openai_chat"
    Some("openai_responses") => "openai_responses"
    Some("gemini_native") => "gemini_native"
    _ => "anthropic"
  }
}

///|
fn ProviderRouteTarget::claude_api_format_for_model(
  self : ProviderRouteTarget,
  model : String,
) -> String {
  if !self.is_github_copilot_upstream() {
    return self.claude_api_format()
  }
  match self.copilot_model_vendor(model) {
    Some(vendor) if vendor.to_lower() == "openai" => "openai_responses"
    _ => "openai_chat"
  }
}

///|
fn ProviderRouteTarget::copilot_model_vendor(
  self : ProviderRouteTarget,
  model : String,
) -> String? {
  guard self.settings_config is Some(Object(settings)) else { return None }
  for
    key in [
      "copilotModels", "copilot_models", "modelCatalog", "model_catalog", "models",
    ] {
    match settings.get(key) {
      Some(models) =>
        match copilot_model_vendor_from_models_json(model, models) {
          Some(vendor) => return Some(vendor)
          None => ()
        }
      None => ()
    }
  }
  None
}

///|
fn claude_api_format_needs_transform(api_format : String) -> Bool {
  api_format == "openai_chat" ||
  api_format == "openai_responses" ||
  api_format == "gemini_native"
}

///|
const MOONGATE_PROXY_MANAGED_PLACEHOLDER = "PROXY_MANAGED"

///|
const HERMES_WEB_OFFLINE_ERROR = "hermes_web_offline"

///|
fn moon_is_managed_account_upstream_url(url : String) -> Bool {
  let (origin, path) = split_origin_and_path(url)
  let host = extract_host(origin).to_lower()
  host == "githubcopilot.com" ||
  host.has_suffix(".githubcopilot.com") ||
  (host == "chatgpt.com" && path.has_prefix("/backend-api/codex"))
}

///|
fn parse_custom_user_agent(value : String?) -> String? {
  match value {
    Some(raw) => {
      let trimmed = raw.trim().to_owned()
      if trimmed.is_empty() || !custom_user_agent_header_value_valid(trimmed) {
        None
      } else {
        Some(trimmed)
      }
    }
    None => None
  }
}

///|
fn custom_user_agent_header_value_valid(value : String) -> Bool {
  for ch in value {
    let code = ch.to_int()
    if code == 9 {
      continue
    }
    if code < 32 || code == 127 {
      return false
    }
  }
  true
}

///|
fn moon_claude_code_beta_header(existing_beta : String?) -> String {
  let claude_code_beta = "claude-code-20250219"
  match existing_beta {
    Some(beta) if beta.contains(claude_code_beta) => beta
    Some(beta) if !beta.trim().is_empty() => "\{claude_code_beta},\{beta}"
    _ => claude_code_beta
  }
}

///|
fn codex_compatible_responses_endpoint(endpoint : String) -> String {
  let query = match endpoint.find("?") {
    Some(index) => endpoint[index:]
    None => ""
  }
  "/responses\{query}"
}

///|
fn codex_compatible_chat_completions_endpoint(endpoint : String) -> String {
  let query = match endpoint.find("?") {
    Some(index) => endpoint[index:]
    None => ""
  }
  "/chat/completions\{query}"
}

///|
fn claude_transform_endpoint(
  api_format : String,
  is_stream : Bool,
  request_path? : StringView = "/v1/messages",
) -> String {
  let request_path_only = match request_path.find("?") {
    Some(index) => request_path[:index]
    None => request_path
  }
  if !is_claude_messages_path(request_path_only) {
    return request_path.to_owned()
  }
  let query = claude_transform_passthrough_query(
    request_path, api_format, is_stream,
  )
  let target = match api_format {
    "openai_responses" => "/v1/responses"
    "openai_chat" => "/v1/chat/completions"
    "gemini_native" =>
      if is_stream {
        "/v1beta/models/__MODEL__:streamGenerateContent"
      } else {
        "/v1beta/models/__MODEL__:generateContent"
      }
    _ => "/v1/messages"
  }
  match query {
    Some(query) if !query.is_empty() => "\{target}?\{query}"
    _ => target
  }
}

///|
fn is_claude_messages_path(path : StringView) -> Bool {
  path == "/v1/messages"
}

///|
fn claude_transform_passthrough_query(
  request_path : StringView,
  api_format : String,
  is_stream : Bool,
) -> String? {
  match request_path.find("?") {
    Some(index) => {
      let path = request_path[:index]
      let query = request_path[index + 1:]
      let stripped = if is_claude_messages_path(path) {
        strip_claude_beta_query(query)
      } else {
        Some(query.to_owned())
      }
      gemini_stream_query_extra(api_format, is_stream, stripped)
    }
    None => gemini_stream_query_extra(api_format, is_stream, None)
  }
}

///|
fn strip_claude_beta_query(query : StringView) -> String? {
  let parts : Array[String] = []
  for pair in query.split("&") {
    if !pair.is_empty() && !pair.has_prefix("beta=") {
      parts.push(pair.to_owned())
    }
  }
  if parts.is_empty() {
    None
  } else {
    Some(parts.join("&"))
  }
}

///|
fn gemini_stream_query_extra(
  api_format : String,
  is_stream : Bool,
  query : String?,
) -> String? {
  if api_format != "gemini_native" {
    return query
  }
  let parts : Array[String] = []
  match query {
    Some(query) =>
      for pair in query.split("&") {
        if !pair.is_empty() && !pair.has_prefix("alt=") {
          parts.push(pair.to_owned())
        }
      }
    None => ()
  }
  if is_stream {
    parts.push("alt=sse")
  }
  Some(parts.join("&"))
}

///|
fn append_query_to_full_url(
  base_url : String,
  endpoint_trimmed : String,
) -> String {
  match endpoint_trimmed.find("?") {
    Some(index) => {
      let query = endpoint_trimmed[index + 1:]
      if query.is_empty() {
        base_url
      } else if base_url.contains("?") {
        "\{base_url}&\{query}"
      } else {
        "\{base_url}?\{query}"
      }
    }
    None => base_url
  }
}

///|
priv enum MoonClientFormat {
  Claude
  Codex
  OpenAI
  Gemini
  GeminiCli
  Unknown
} derive(Eq)

///|
fn MoonClientFormat::to_string(self : MoonClientFormat) -> String {
  match self {
    Claude => "claude"
    Codex => "codex"
    OpenAI => "openai"
    Gemini => "gemini"
    GeminiCli => "gemini_cli"
    Unknown => "unknown"
  }
}

///|
fn moon_client_format_from_path(path : String) -> MoonClientFormat {
  if path.contains("/v1/messages") {
    Claude
  } else if path.contains("/v1/responses") {
    Codex
  } else if path.contains("/v1/chat/completions") {
    OpenAI
  } else if path.contains("/v1internal/") && path.contains("generateContent") {
    GeminiCli
  } else if (path.contains("/v1beta/") || path.contains("/v1/")) &&
    path.contains("generateContent") {
    Gemini
  } else if path.contains("generateContent") {
    Gemini
  } else {
    Unknown
  }
}

///|
fn moon_client_format_from_body(body : Json) -> MoonClientFormat {
  guard body is Object(obj) else { return Unknown }
  if obj.get("messages") is Some(_) &&
    obj.get("model") is Some(_) &&
    obj.get("response_format") is None &&
    obj.get("contents") is None {
    if obj.get("max_tokens") is Some(_) {
      return Claude
    }
    return OpenAI
  }
  if obj.get("input") is Some(_) {
    return Codex
  }
  if obj.get("contents") is Some(_) {
    return Gemini
  }
  Unknown
}

///|
fn moon_client_format_for_request(
  path : String,
  body : Json?,
) -> MoonClientFormat {
  let format = moon_client_format_from_path(path)
  if format != Unknown {
    return format
  }
  match body {
    Some(body) => moon_client_format_from_body(body)
    None => Unknown
  }
}

///|
priv struct MoonClientSession {
  session_id : String
  client_provided : Bool
}

///|
fn MoonClientSession::upstream_session_id(self : MoonClientSession) -> String? {
  if self.client_provided {
    Some(self.session_id)
  } else {
    None
  }
}

///|
fn json_metadata_string(body : Json, key : String) -> String? {
  guard body is Object(obj) else { return None }
  guard obj.get("metadata") is Some(Object(metadata)) else { return None }
  match metadata.get(key) {
    Some(String(value)) => Some(value)
    _ => None
  }
}

///|
fn json_bool_field(json : Json, key : String) -> Bool? {
  guard json is Object(obj) else { return None }
  match obj.get(key) {
    Some(true) => Some(true)
    Some(false) => Some(false)
    _ => None
  }
}

///|
fn json_string_field(json : Json, key : String) -> String? {
  guard json is Object(obj) else { return None }
  match obj.get(key) {
    Some(String(value)) if !value.is_empty() => Some(value)
    _ => None
  }
}

///|
fn normalize_gemini_endpoint(path : StringView) -> String {
  let raw = path.to_owned()
  if raw.has_prefix("/gemini/v1beta/") {
    "/v1beta/\{raw["/gemini/v1beta/".length():]}"
  } else if raw.has_prefix("/gemini/v1/") {
    "/v1/\{raw["/gemini/v1/".length():]}"
  } else {
    raw
  }
}

///|
fn gemini_model_from_endpoint(endpoint : String) -> String {
  match endpoint.find("/models/") {
    Some(index) => {
      let rest = endpoint[index + "/models/".length():]
      let without_query = match rest.find("?") {
        Some(end) => rest[:end]
        None => rest
      }
      let model = match without_query.find(":") {
        Some(end) => without_query[:end].to_owned()
        None => without_query.to_owned()
      }
      if model.is_empty() {
        "unknown"
      } else {
        normalize_gemini_model_id(model)
      }
    }
    None => "unknown"
  }
}

///|
fn gemini_base_url() -> String {
  match getenv_or_none("GOOGLE_GEMINI_BASE_URL") {
    Some(url) if !url.is_empty() => url
    _ =>
      match getenv_or_none("GEMINI_BASE_URL") {
        Some(url) if !url.is_empty() => url
        _ => "https://generativelanguage.googleapis.com"
      }
  }
}

///|
fn gemini_base_is_full_url() -> Bool {
  env_bool("MOONGATE_GEMINI_FULL_URL")
}

///|
fn codex_base_is_full_url() -> Bool {
  env_bool("MOONGATE_CODEX_FULL_URL")
}

///|
fn claude_base_is_full_url() -> Bool {
  env_bool("MOONGATE_CLAUDE_FULL_URL")
}

///|
fn moon_model_mapping_from_env() -> ModelMapping? {
  let mapping = ModelMapping::new(
    haiku_model=env_first_non_empty(["ANTHROPIC_DEFAULT_HAIKU_MODEL"]),
    sonnet_model=env_first_non_empty(["ANTHROPIC_DEFAULT_SONNET_MODEL"]),
    opus_model=env_first_non_empty(["ANTHROPIC_DEFAULT_OPUS_MODEL"]),
    default_model=env_first_non_empty(["ANTHROPIC_MODEL"]),
  )
  if mapping.has_mapping() {
    Some(mapping)
  } else {
    None
  }
}

///|
fn env_first_non_empty(keys : Array[String]) -> String? {
  for key in keys {
    match getenv_or_none(key) {
      Some(value) if !value.trim().is_empty() =>
        return Some(value.trim().to_owned())
      _ => continue
    }
  }
  None
}

///|
fn env_first_non_negative_decimal(keys : Array[String]) -> String? {
  for key in keys {
    match getenv_or_none(key) {
      Some(value) if usage_non_negative_decimal(value) =>
        return Some(value.trim().to_owned())
      _ => continue
    }
  }
  None
}

///|
fn url_contains_query_key(url : String) -> Bool {
  url.contains("?key=") || url.contains("&key=")
}

///|
fn env_bool(key : String) -> Bool {
  match getenv_or_none(key) {
    Some(value) => {
      let normalized = value.trim().to_lower()
      normalized == "1" ||
      normalized == "true" ||
      normalized == "yes" ||
      normalized == "on"
    }
    None => false
  }
}

///|
fn gemini_api_key() -> String? {
  match getenv_or_none("GEMINI_API_KEY") {
    Some(key) if !key.is_empty() => Some(key)
    _ =>
      match getenv_or_none("GOOGLE_API_KEY") {
        Some(key) if !key.is_empty() => Some(key)
        _ => None
      }
  }
}

///|
fn getenv_or_none(key : String) -> String? {
  @os.getenv(key) catch {
    _ => None
  }
}

///|
fn moon_normalize_app_type(app : String) -> String {
  match app.trim().to_lower() {
    "claude-desktop" => "claude-desktop"
    "codex" => "codex"
    "gemini" => "gemini"
    "opencode" => "opencode"
    "openclaw" => "openclaw"
    "hermes" => "hermes"
    "omo" => "omo"
    "omo-slim" => "omo-slim"
    _ => "claude"
  }
}

///|
fn moon_home_dir() -> String {
  @os.home() catch {
    _ => "."
  }
}

///|
fn moon_ascii_digit_at(value : String, index : Int) -> Bool {
  let ch = value[index]
  ch >= '0' && ch <= '9'
}

///|
async fn moon_ensure_directory(path : String) -> Bool {
  @fsx.make_directory(path, recursive=true, exists_ok=true) catch {
    _ => return false
  }
  true
}

///|
async fn moon_open_directory_result_json(path : String) -> Result[Json, String] {
  if !moon_ensure_directory(path) {
    return Err("open_directory_create_failed")
  }
  Ok(Json::boolean(true))
}

///|
fn moon_preview(content : String, limit : Int) -> String {
  if content.length() <= limit {
    content
  } else {
    content[:limit].to_owned()
  }
}

///|
fn moon_count_case_insensitive_matches(
  haystack : String,
  needle : String,
) -> (Int, Int?) {
  let lower = haystack.to_lower()
  let query = needle.to_lower()
  if query.is_empty() {
    return (0, None)
  }
  let mut count = 0
  let mut first : Int? = None
  let mut offset = 0
  while offset <= lower.length() {
    match lower[offset:].find(query) {
      Some(found) => {
        let pos = offset + found
        if first is None {
          first = Some(pos)
        }
        count = count + 1
        offset = pos + query.length()
      }
      None => break
    }
  }
  (count, first)
}

///|
fn moon_preserve_secret_if_empty(
  incoming : Map[String, Json],
  existing : Map[String, Json],
  object_key : String,
  secret_key : String,
) -> Unit {
  guard incoming.get(object_key) is Some(Object(incoming_obj)) else { return }
  guard existing.get(object_key) is Some(Object(existing_obj)) else { return }
  let incoming_secret = json_string_from_map(incoming_obj, secret_key).unwrap_or(
    "",
  )
  let existing_secret = json_string_from_map(existing_obj, secret_key).unwrap_or(
    "",
  )
  if incoming_secret.is_empty() && !existing_secret.is_empty() {
    incoming_obj[secret_key] = Json::string(existing_secret)
    incoming[object_key] = Json::object(incoming_obj)
  }
}

///|
fn moon_optional_path_from_request(path : StringView, body : Json?) -> String? {
  match proxy_string_request(path, body, "path") {
    Some(value) if !value.trim().is_empty() => return Some(value)
    Some(_) => return None
    None => ()
  }
  match body {
    Some(Object(obj)) =>
      match obj.get("path") {
        Some(Null) => None
        _ => None
      }
    _ => None
  }
}

///|
fn moon_json_primary_api_key_any(content : String) -> Bool {
  let parsed : Result[Json, Error] = moon_result(() => @json.parse(content))
  match parsed {
    Ok(Object(obj)) =>
      match json_string_from_map(obj, "primaryApiKey") {
        Some(value) => value == "any"
        None => false
      }
    _ => false
  }
}

///|
async fn moon_env_conflicts_json(app : String) -> Json {
  moon_env_conflicts_json_with_shell_files(app, moon_env_shell_config_paths())
}

///|
async fn moon_push_shell_env_conflicts(
  conflicts : Array[Json],
  file_path : String,
  keywords : Array[String],
) -> Unit {
  let content = @fsx.read_file(file_path) catch { _ => return }
  let mut line_number = 0
  for line_view in content.split("\n") {
    line_number += 1
    let trimmed = line_view.to_owned().trim().to_owned()
    guard moon_env_assignment_line(trimmed) is Some((var_name, var_value)) else {
      continue
    }
    if moon_env_name_matches_keywords(var_name, keywords) {
      conflicts.push(
        {
          "varName": Json::string(var_name),
          "varValue": Json::string(moon_trim_env_value_quotes(var_value)),
          "sourceType": Json::string("file"),
          "sourcePath": Json::string("\{file_path}:\{line_number}"),
        }.to_json(),
      )
    }
  }
}

///|
fn moon_env_conflict_keys(app : String) -> Array[String] {
  match app {
    "codex" =>
      ["OPENAI_API_KEY", "OPENAI_BASE_URL", "CODEX_HOME", "CODEX_AUTH_TOKEN"]
    "gemini" =>
      [
        "GEMINI_API_KEY", "GOOGLE_API_KEY", "GEMINI_BASE_URL", "GOOGLE_GEMINI_BASE_URL",
      ]
    "opencode" | "omo" | "omo-slim" =>
      [
        "OPENCODE_CONFIG_DIR", "OPENCODE_DB", "OPENAI_API_KEY", "ANTHROPIC_API_KEY",
      ]
    "openclaw" =>
      [
        "OPENCLAW_CONFIG_DIR", "OPENAI_API_KEY", "ANTHROPIC_API_KEY", "GEMINI_API_KEY",
      ]
    "hermes" =>
      [
        "HERMES_CONFIG_DIR", "HERMES_WEB_PORT", "OPENAI_API_KEY", "ANTHROPIC_API_KEY",
      ]
    _ =>
      [
        "ANTHROPIC_API_KEY", "ANTHROPIC_BASE_URL", "CLAUDE_CONFIG_DIR", "CLAUDE_CODE_USE_BEDROCK",
      ]
  }
}

///|
async fn moon_delete_env_vars_json(body : Json?) -> Result[Json, String] {
  let backup = moon_env_backup_json(body)
  guard backup is Object(obj) else { return Err("invalid_backup") }
  guard obj.get("backupPath") is Some(String(backup_path)) else {
    return Err("missing_backup_path")
  }
  @fsx.make_directory(
    @pathx.dirname(backup_path),
    recursive=true,
    exists_ok=true,
  ) catch {
    error => return Err("创建备份目录失败: \{error}")
  }
  @fsx.write_to_file(backup_path, backup.stringify(indent=2) + "\n") catch {
    error => return Err("写入备份文件失败: \{error}")
  }
  for conflict in moon_env_conflicts_from_body(body) {
    match moon_delete_single_env_conflict(conflict) {
      Ok(_) => ()
      Err(error) =>
        return Err(
          "删除环境变量失败: \{error}. 备份已保存到: \{backup_path}",
        )
    }
  }
  Ok(backup)
}

///|
async fn moon_delete_single_env_conflict(
  conflict : Json,
) -> Result[Unit, String] {
  guard conflict is Object(obj) else { return Err("invalid_conflict") }
  let source_type = moon_env_conflict_string(obj, "sourceType")
  let var_name = moon_env_conflict_string(obj, "varName")
  let source_path = moon_env_conflict_string(obj, "sourcePath")
  match (source_type, var_name, source_path) {
    (Some("file"), Some(var_name), Some(source_path)) => {
      let file_path = moon_env_source_file_path(source_path)
      guard !file_path.is_empty() else {
        return Err("无效的文件路径格式")
      }
      let content = @fsx.read_file(file_path) catch {
        error => return Err("读取文件失败 \{file_path}: \{error}")
      }
      let kept : Array[String] = []
      for line_view in content.split("\n") {
        let line = line_view.to_owned()
        match moon_env_assignment_line(line.trim().to_owned()) {
          Some((name, _)) if name == var_name => ()
          _ => kept.push(line)
        }
      }
      @fsx.write_to_file(file_path, kept.join("\n")) catch {
        error => return Err("写入文件失败 \{file_path}: \{error}")
      }
      Ok(())
    }
    (Some("system"), _, _) | (Some("process"), _, _) => Ok(())
    (Some(other), _, _) => Err("未知的环境变量来源类型: \{other}")
    _ => Err("invalid_conflict")
  }
}

///|
async fn moon_restore_single_env_conflict(
  conflict : Json,
) -> Result[Unit, String] {
  guard conflict is Object(obj) else { return Err("invalid_conflict") }
  let source_type = moon_env_conflict_string(obj, "sourceType")
  let var_name = moon_env_conflict_string(obj, "varName")
  let var_value = moon_env_conflict_string(obj, "varValue")
  let source_path = moon_env_conflict_string(obj, "sourcePath")
  match (source_type, var_name, var_value, source_path) {
    (Some("file"), Some(var_name), Some(var_value), Some(source_path)) => {
      let file_path = moon_env_source_file_path(source_path)
      guard !file_path.is_empty() else {
        return Err("无效的文件路径格式")
      }
      let content = @fsx.read_file(file_path) catch {
        error => return Err("读取文件失败 \{file_path}: \{error}")
      }
      @fsx.write_to_file(
        file_path,
        content + "\nexport \{var_name}=\{var_value}",
      ) catch {
        error => return Err("写入文件失败 \{file_path}: \{error}")
      }
      Ok(())
    }
    (Some(other), _, _, _) =>
      Err("无法恢复类型为 \{other} 的环境变量")
    _ => Err("invalid_conflict")
  }
}

///|
fn moon_env_conflicts_from_body(body : Json?) -> Array[Json] {
  match body {
    Some(Array(items)) => items
    Some(Object(obj)) =>
      match obj.get("conflicts") {
        Some(Array(items)) => items
        _ => []
      }
    _ => []
  }
}

///|
fn moon_env_conflict_string(obj : Map[String, Json], key : String) -> String? {
  match obj.get(key) {
    Some(String(value)) => Some(value)
    _ => None
  }
}

///|
fn moon_env_assignment_line(line : String) -> (String, String)? {
  if line.is_empty() || line.has_prefix("#") {
    return None
  }
  let export_line = if line.has_prefix("export ") {
    line[7:].to_owned().trim().to_owned()
  } else {
    line
  }
  match export_line.find("=") {
    Some(index) => {
      let name = export_line[:index].to_owned().trim().to_owned()
      let value = export_line[index + 1:].to_owned().trim().to_owned()
      if name.is_empty() {
        None
      } else {
        Some((name, value))
      }
    }
    None => None
  }
}

///|
fn moon_env_name_matches_keywords(
  var_name : String,
  keywords : Array[String],
) -> Bool {
  let upper = var_name.to_upper()
  for keyword in keywords {
    if upper.contains(keyword) {
      return true
    }
  }
  false
}

///|
fn moon_trim_env_value_quotes(value : String) -> String {
  let trimmed = value.trim().to_owned()
  if trimmed.length() >= 2 {
    if (trimmed.has_prefix("\"") && trimmed.has_suffix("\"")) ||
      (trimmed.has_prefix("'") && trimmed.has_suffix("'")) {
      return trimmed[1:trimmed.length() - 1].to_owned()
    }
  }
  trimmed
}

///|
fn gemini_request_is_stream(endpoint : String, body : Json) -> Bool {
  moon_streaming_request(endpoint, body, {})
}

///|
fn moon_streaming_request(
  endpoint : String,
  body : Json,
  headers : Map[String, String],
) -> Bool {
  if json_bool_field(body, "stream").unwrap_or(false) {
    return true
  }
  if endpoint.contains("streamGenerateContent") || endpoint.contains("alt=sse") {
    return true
  }
  match headers.get("accept") {
    Some(accept) => accept.contains("text/event-stream")
    None => false
  }
}

///|
fn moon_unit_success_json() -> Json {
  Json::null()
}

///|
fn moon_update_tray_menu_result() -> Json {
  Json::boolean(false)
}

///|
priv struct ProxyProviderQuery {
  provider_id : String
  app_type : String
}

///|
fn json_non_empty_string_from_map(
  obj : Map[String, Json],
  key : String,
) -> String? {
  match json_string_from_map(obj, key) {
    Some(value) if !value.trim().is_empty() => Some(value)
    _ => None
  }
}

///|
fn suite_write_outcome_json() -> Json {
  let empty : Map[String, Json] = {}
  Json::object(empty)
}

///|
fn suite_object_payload(body : Json?, key : String) -> Json? {
  match body {
    Some(Object(obj)) =>
      match obj.get(key) {
        Some(Object(_)) if obj.length() == 1 => None
        _ => Some(Json::object(obj))
      }
    _ => None
  }
}

///|
fn suite_object_request(path : StringView, body : Json?, key : String) -> Json? {
  match suite_object_payload(body, key) {
    Some(payload) => return Some(payload)
    None => ()
  }
  match usage_query_value(path, key) {
    Some(raw) => {
      let parsed = @json.parse(raw) catch { _ => return None }
      match parsed {
        Object(obj) => Some(Json::object(obj))
        _ => None
      }
    }
    None => None
  }
}

///|
fn split_csv_json(value : String) -> Json {
  let items : Array[Json] = []
  for raw in value.split(",") {
    let trimmed = raw.trim().to_owned()
    if !trimmed.is_empty() {
      items.push(Json::string(trimmed))
    }
  }
  Json::array(items)
}

///|
const HERMES_DASHBOARD_COMMAND = "hermes dashboard"

///|
const HERMES_DASHBOARD_LOG_PATH = "/tmp/moongate_hermes_dashboard.log"

///|
fn normalize_claude_desktop_mode(mode : String) -> String? {
  match mode.trim().to_lower() {
    "direct" => Some("direct")
    "proxy" => Some("proxy")
    _ => None
  }
}

///|
fn split_endpoint_urls(value : String) -> Array[String] {
  let urls : Array[String] = []
  for raw in value.split(",") {
    urls.push(raw.to_owned())
  }
  urls
}

///|
const MOONGATE_ENDPOINT_DEFAULT_TIMEOUT_SECS = 8

///|
const MOONGATE_ENDPOINT_MIN_TIMEOUT_SECS = 2

///|
const MOONGATE_ENDPOINT_MAX_TIMEOUT_SECS = 30

///|
fn moon_endpoint_invalid_url_error(url : String) -> String {
  if url.has_prefix("http://") || url.has_prefix("https://") {
    if moon_endpoint_url_missing_host(url) {
      "URL 无效: empty host"
    } else {
      "URL 无效"
    }
  } else {
    "URL 无效: relative URL without a base"
  }
}

///|
fn moon_endpoint_url_missing_host(url : String) -> Bool {
  match endpoint_url_host_start(url) {
    Some(start) => {
      if url.length() <= start {
        return true
      }
      let rest = url[start:]
      rest.has_prefix("/") || rest.has_prefix("?") || rest.has_prefix("#")
    }
    None => false
  }
}

///|
fn endpoint_url_host_start(url : String) -> Int? {
  if url.has_prefix("http://") {
    Some(7)
  } else if url.has_prefix("https://") {
    Some(8)
  } else {
    None
  }
}

///|
fn parse_bool_value(value : String) -> Bool? {
  match value.trim().to_owned().to_lower() {
    "true" | "1" | "yes" | "on" => Some(true)
    "false" | "0" | "no" | "off" => Some(false)
    _ => None
  }
}

///|
fn parse_non_negative_decimal_to_double(value : String) -> Double? {
  let trimmed = value.trim().to_owned()
  if trimmed.is_empty() {
    return None
  }
  let mut seen_dot = false
  let mut seen_digit = false
  for ch in trimmed {
    if ch == '.' {
      if seen_dot {
        return None
      }
      seen_dot = true
    } else {
      let code = ch.to_int()
      if code < '0'.to_int() || code > '9'.to_int() {
        return None
      }
      seen_digit = true
    }
  }
  if !seen_digit {
    return None
  }
  Some(parse_decimal_scaled(trimmed, 1_000_000L).to_double() / 1_000_000.0)
}

///|
fn claude_desktop_model_list_json(
  provider? : ProviderRouteTarget? = None,
) -> Json {
  let routes = match provider {
    Some(provider) => provider.resolved_claude_desktop_model_routes()
    None => []
  }
  if !routes.is_empty() {
    return claude_desktop_route_model_list_json(routes)
  }
  let data = [
    claude_desktop_model_json("claude-sonnet-4-6", supports_1m=true),
    claude_desktop_model_json("claude-opus-4-8", supports_1m=true),
    claude_desktop_model_json("claude-haiku-4-5", supports_1m=true),
  ]
  {
    "data": Json::array(data),
    "has_more": Json::boolean(false),
    "first_id": Json::string("claude-sonnet-4-6"),
    "last_id": Json::string("claude-haiku-4-5"),
  }.to_json()
}

///|
fn claude_desktop_route_model_list_json(
  routes : Array[(String, String, String?, Bool)],
) -> Json {
  let data = routes.map(fn(route) {
    let (route_id, _, label_override, supports_1m) = route
    claude_desktop_model_json(route_id, label_override~, supports_1m~)
  })
  let first_id = match routes.get(0) {
    Some((route_id, _, _, _)) => Json::string(route_id)
    None => Json::null()
  }
  let last_id = if routes.is_empty() {
    Json::null()
  } else {
    match routes.get(routes.length() - 1) {
      Some((route_id, _, _, _)) => Json::string(route_id)
      None => Json::null()
    }
  }
  {
    "data": Json::array(data),
    "has_more": Json::boolean(false),
    "first_id": first_id,
    "last_id": last_id,
  }.to_json()
}

///|
fn claude_desktop_model_json(
  id : String,
  label_override? : String? = None,
  supports_1m? : Bool = true,
) -> Json {
  let obj : Map[String, Json] = {
    "type": Json::string("model"),
    "id": Json::string(id),
    "created_at": Json::string("2024-01-01T00:00:00Z"),
  }
  match label_override {
    Some(label) => obj["labelOverride"] = Json::string(label)
    None => ()
  }
  if supports_1m {
    obj["supports1m"] = Json::boolean(true)
  }
  Json::object(obj)
}

///|
fn normalize_claude_desktop_request_model(
  request : AnthropicRequest,
  app_type : String,
) -> AnthropicRequest {
  if app_type != "claude-desktop" {
    return request
  }
  let model = normalize_claude_desktop_model_id(request.model)
  if model == request.model {
    request
  } else {
    {
      model,
      max_tokens: request.max_tokens,
      messages: request.messages,
      system: request.system,
      tools: request.tools,
      stream: request.stream,
    }
  }
}

///|
fn normalize_claude_desktop_model_id(model : String) -> String {
  let trimmed = model.trim().to_owned()
  strip_one_m_suffix_for_route_lookup(trimmed)
}

///|
fn strip_one_m_suffix_for_route_lookup(model : String) -> String {
  let trimmed = model.trim().to_owned()
  let marker = "[1m]"
  if trimmed.to_lower().has_suffix(marker) {
    trimmed[:trimmed.length() - marker.length()].trim().to_owned()
  } else {
    trimmed
  }
}

///|
fn is_claude_safe_model_id(model : String) -> Bool {
  let normalized = model.trim().to_lower()
  if normalized.contains("[1m]") {
    return false
  }
  let tail = if normalized.has_prefix("anthropic/claude-") {
    normalized["anthropic/claude-".length():]
  } else if normalized.has_prefix("claude-") {
    normalized["claude-".length():]
  } else {
    return false
  }
  ["sonnet-", "opus-", "haiku-"].any(prefix => {
    tail.has_prefix(prefix) && tail.length() > prefix.length()
  })
}

///|
fn ProviderRouteTarget::resolved_claude_desktop_model_routes(
  self : ProviderRouteTarget,
) -> Array[(String, String, String?, Bool)] {
  let routes : Array[(String, String, String?, Bool)] = []
  if self.claude_desktop_model_routes.is_empty() {
    return routes
  }
  let reserved_route_ids : Array[String] = []
  for route_id in self.claude_desktop_model_routes.keys() {
    let trimmed = route_id.trim().to_owned()
    if is_claude_safe_model_id(trimmed) {
      reserved_route_ids.push(trimmed)
    }
  }
  let route_ids : Array[String] = []
  for route_id in self.claude_desktop_model_routes.keys() {
    route_ids.push(route_id)
  }
  route_ids.sort()
  for route_id in route_ids {
    let trimmed_route_id = route_id.trim().to_owned()
    guard self.claude_desktop_model_routes.get(route_id) is Some(route) else {
      continue
    }
    let upstream_model = route.model.trim().to_owned()
    if trimmed_route_id.is_empty() || upstream_model.is_empty() {
      continue
    }
    let route_id_is_safe = is_claude_safe_model_id(trimmed_route_id)
    let resolved_route_id = if route_id_is_safe {
      trimmed_route_id
    } else {
      next_claude_desktop_catalog_safe_route_id(routes, reserved_route_ids)
    }
    let label_override = match route.label_override {
      Some(label) => {
        let label = label.trim().to_owned()
        if label.is_empty() {
          None
        } else {
          Some(label)
        }
      }
      None => if route_id_is_safe { None } else { Some(upstream_model) }
    }
    routes.push(
      (
        resolved_route_id,
        upstream_model,
        label_override,
        route.supports_1m.unwrap_or(false),
      ),
    )
  }
  routes.sort_by(fn(left, right) {
    let (left_route_id, _, _, _) = left
    let (right_route_id, _, _, _) = right
    left_route_id.compare(right_route_id)
  })
  let deduped : Array[(String, String, String?, Bool)] = []
  for route in routes {
    let (route_id, _, _, _) = route
    match deduped.last() {
      Some((previous_route_id, _, _, _)) if previous_route_id == route_id => ()
      _ => deduped.push(route)
    }
  }
  deduped
}

///|
fn next_claude_desktop_catalog_safe_route_id(
  existing : Array[(String, String, String?, Bool)],
  reserved : Array[String],
) -> String {
  for route_id in claude_desktop_default_proxy_route_ids() {
    if !reserved.contains(route_id) &&
      !existing.any(fn(route) {
        let (existing_route_id, _, _, _) = route
        existing_route_id == route_id
      }) {
      return route_id
    }
  }
  for index in 2..<10000 {
    let route_id = "claude-sonnet-4-6-r\{index}"
    if !reserved.contains(route_id) &&
      !existing.any(fn(route) {
        let (existing_route_id, _, _, _) = route
        existing_route_id == route_id
      }) {
      return route_id
    }
  }
  "claude-sonnet-4-6-r10000"
}

///|
fn ProviderRouteTarget::claude_desktop_route_upstream_model(
  self : ProviderRouteTarget,
  request_model : String,
) -> String? {
  let requested = strip_one_m_suffix_for_route_lookup(request_model)
  if requested.is_empty() || self.claude_desktop_model_routes.is_empty() {
    return None
  }
  let routes = self.resolved_claude_desktop_model_routes()
  for route in routes {
    let (route_id, upstream_model, _, _) = route
    if route_id == requested {
      return Some(upstream_model)
    }
  }
  None
}