///|
/// Importer for MoonPod (`yadidyc/moonpod@0.2.0`) JSON policies.
///
/// MoonPod is a transport-neutral policy core; MoonGate is the MCP enforcement
/// runtime. This adapter lets a MoonPod policy be reused at the MCP boundary.
/// It is a compatibility layer, not a fork and not a dependency: MoonGate keeps
/// its own evaluator, and the output is an ordinary MoonGate policy.
///
/// Supported subset:
///   schema_version, allowed_tools, read_roots, write_roots, protected_paths,
///   network_rules, approval_required_tools, max_calls, max_operation_bytes.
///
/// Unsupported fields fail closed with a field name instead of being ignored:
///   command_rules, resource_rules, tool_quotas, max_io_bytes.
pub fn import_moonpod(source : String) -> Json raise PolicyError {
  let root = match json_parse_opt(source) {
    Some(Object(fields)) => fields
    _ => raise PolicyError("moonpod: policy must be a JSON object")
  }
  let known : Array[String] = [
    "schema_version", "allowed_tools", "read_roots", "write_roots", "protected_paths",
    "network_rules", "command_rules", "resource_rules", "tool_quotas", "max_calls",
    "max_operation_bytes", "max_io_bytes", "approval_required_tools",
  ]
  for name, _ in root {
    if !known.contains(name) {
      raise PolicyError("moonpod: unknown field " + name)
    }
  }
  if pod_int(root, "schema_version", 0) != 1 {
    raise PolicyError("moonpod: only schema_version 1 is supported")
  }
  let allowed = pod_texts(root, "allowed_tools")
  let read_roots = pod_texts(root, "read_roots")
  let write_roots = pod_texts(root, "write_roots")
  let protected_paths = pod_texts(root, "protected_paths")
  let approvals = pod_texts(root, "approval_required_tools")
  let network = pod_array(root, "network_rules")
  let rules : Array[Json] = []

  if !pod_array(root, "command_rules").is_empty() {
    raise PolicyError(
      "moonpod: field command_rules is not supported by the adapter",
    )
  }
  if !pod_array(root, "resource_rules").is_empty() {
    raise PolicyError(
      "moonpod: field resource_rules is not supported by the adapter",
    )
  }
  if !pod_array(root, "tool_quotas").is_empty() {
    raise PolicyError(
      "moonpod: field tool_quotas is not supported by the adapter",
    )
  }
  if root.contains("max_io_bytes") {
    raise PolicyError(
      "moonpod: field max_io_bytes is not supported by the adapter",
    )
  }
  if allowed.contains("process.run") {
    raise PolicyError(
      "moonpod: process.run needs command_rules, which the adapter does not support",
    )
  }

  // Protected paths deny first: they override every allow, matching MoonPod.
  for i, path in protected_paths {
    rules.push(
      pod_rule(
        "pod-protected-\{i}",
        "*",
        "deny",
        [pod_path_check(path)],
        "protected path from MoonPod policy",
      ),
    )
  }
  // Approvals come before allows so an approved tool cannot slip through.
  for i, tool in approvals {
    rules.push(
      pod_rule(
        "pod-approval-\{i}",
        tool,
        "approval",
        [],
        "approval required by MoonPod policy",
      ),
    )
  }
  for i, root_path in read_roots {
    rules.push(
      pod_rule(
        "pod-read-\{i}",
        "fs.read",
        "allow",
        [pod_path_check(root_path)],
        "read root from MoonPod policy",
      ),
    )
  }
  if allowed.contains("fs.read") && read_roots.is_empty() {
    rules.push(pod_rule("pod-read-any", "fs.read", "allow", [], "allowed tool"))
  }
  for i, root_path in write_roots {
    rules.push(
      pod_rule(
        "pod-write-\{i}",
        "fs.write",
        "allow",
        [pod_path_check(root_path)],
        "write root from MoonPod policy",
      ),
    )
  }
  if allowed.contains("fs.write") && write_roots.is_empty() {
    rules.push(
      pod_rule("pod-write-any", "fs.write", "allow", [], "allowed tool"),
    )
  }
  for i, entry in network {
    let fields = match entry {
      Object(fields) => fields
      _ => raise PolicyError("moonpod: network_rules[\{i}] must be an object")
    }
    let host = pod_text(fields, "host")
    let ports = pod_int_array(fields, "allowed_ports")
    let globs : Array[Json] = []
    for port in ports {
      globs.push(json_str("*://" + host + ":" + port.to_string() + "/*"))
      if port == 80 || port == 443 {
        globs.push(json_str("*://" + host + "/*"))
      }
    }
    rules.push(
      pod_rule(
        "pod-net-\{i}",
        "net.*",
        "allow",
        [json_obj([("arg", json_str("url")), ("glob", json_arr(globs))])],
        "network rule from MoonPod policy",
      ),
    )
  }
  for tool in allowed {
    if tool == "fs.read" || tool == "fs.write" || tool == "net.connect" {
      continue
    }
    if tool.has_prefix("net.") {
      // Network tools are only reachable through network_rules.
      continue
    }
    rules.push(pod_rule("pod-allow-" + tool, tool, "allow", [], "allowed tool"))
  }

  let max_calls = pod_int(root, "max_calls", 100)
  let max_operation = pod_int(root, "max_operation_bytes", 1048576)
  if max_calls == 0 || max_operation == 0 {
    rules.push(
      pod_rule(
        "pod-zero-budget",
        "*",
        "deny",
        [],
        "MoonPod policy allows zero calls or zero bytes",
      ),
    )
  }
  let policy = json_obj([
    ("version", json_int(1)),
    ("name", json_str("imported-moonpod")),
    ("default_decision", json_str("deny")),
    ("rules", json_arr(rules)),
    (
      "budget",
      json_obj([
        ("max_calls", json_int(max_calls)),
        ("max_single_bytes", json_int(max_operation)),
      ]),
    ),
    ("hide_denied", json_bool(true)),
    ("mask_secrets", json_bool(true)),
  ])
  // Validate the generated document with the same parser the gateway uses.
  let _ = parse_policy(json_render(policy)) catch {
    PolicyError(message) =>
      raise PolicyError("moonpod: generated policy is invalid: " + message)
  }
  policy
}

///|
fn pod_rule(
  id : String,
  tool : String,
  decision : String,
  checks : Array[Json],
  reason : String,
) -> Json {
  let fields : Array[(String, Json)] = [
    ("id", json_str(id)),
    ("tool", json_str(tool)),
    ("decision", json_str(decision)),
  ]
  if !checks.is_empty() {
    fields.push(("when", json_arr(checks)))
  }
  if reason != "" {
    fields.push(("reason", json_str(reason)))
  }
  json_obj(fields)
}

///|
fn pod_path_check(root : String) -> Json {
  json_obj([
    ("arg", json_str("path")),
    ("normalize", json_str("path")),
    ("prefix", json_arr([json_str(root)])),
  ])
}

///|
fn pod_texts(
  fields : Map[String, Json],
  name : String,
) -> Array[String] raise PolicyError {
  match fields.get(name) {
    None => []
    Some(Array(items)) => {
      let out : Array[String] = []
      for item in items {
        match item {
          String(text) => out.push(text)
          _ => raise PolicyError("moonpod: \{name} must be an array of strings")
        }
      }
      out
    }
    Some(_) => raise PolicyError("moonpod: \{name} must be an array of strings")
  }
}

///|
fn pod_array(
  fields : Map[String, Json],
  name : String,
) -> Array[Json] raise PolicyError {
  match fields.get(name) {
    None => []
    Some(Array(items)) => items
    Some(_) => raise PolicyError("moonpod: \{name} must be an array")
  }
}

///|
fn pod_text(
  fields : Map[String, Json],
  name : String,
) -> String raise PolicyError {
  match fields.get(name) {
    Some(String(text)) => text
    _ => raise PolicyError("moonpod: \{name} must be a string")
  }
}

///|
fn pod_int(
  fields : Map[String, Json],
  name : String,
  fallback : Int32,
) -> Int32 raise PolicyError {
  match fields.get(name) {
    Some(Number(value, ..)) => value.to_int()
    None => fallback
    _ => raise PolicyError("moonpod: \{name} must be an integer")
  }
}

///|
fn pod_int_array(
  fields : Map[String, Json],
  name : String,
) -> Array[Int32] raise PolicyError {
  match fields.get(name) {
    Some(Array(items)) => {
      let out : Array[Int32] = []
      for item in items {
        match item {
          Number(value, ..) => out.push(value.to_int())
          _ =>
            raise PolicyError("moonpod: \{name} must be an array of integers")
        }
      }
      out
    }
    _ => raise PolicyError("moonpod: \{name} must be an array of integers")
  }
}