///|
let application_capability_id = "app"

///|
fn validate_renderer_capability_targets(
  manifest : @manifest.AppManifest,
  command_capabilities : Array[AppCommandCapability],
  extension_capabilities : Array[ExtensionCapabilityBinding],
) -> Unit raise AppConfigurationError {
  let windows : Map[String, Unit] = { "main": () }
  for window in manifest.windows {
    windows[window.id] = ()
  }
  for capability in command_capabilities {
    validate_renderer_targets(
      capability.targets,
      windows,
      "application commands",
    )
  }
  for capability in extension_capabilities {
    validate_renderer_targets(
      capability.targets,
      windows,
      "extension " + capability.spec.id(),
    )
  }
}

///|
fn validate_renderer_targets(
  targets : Array[RendererTarget],
  windows : Map[String, Unit],
  owner : String,
) -> Unit raise AppConfigurationError {
  guard !targets.is_empty() else {
    raise InvalidRendererCapability(detail=owner + " has no renderer target")
  }
  let seen : Map[String, Unit] = Map([])
  for target in targets {
    guard target.window.trim().to_owned() != "" else {
      raise InvalidRendererCapability(
        detail=owner + " references an empty window id",
      )
    }
    guard windows.contains(target.window) else {
      raise InvalidRendererCapability(
        detail=owner + " references unknown window " + target.window,
      )
    }
    let source_key = if target.bundled { "bundled" } else { "entry" }
    let identity = target.window + "\u{0}" + source_key
    guard !seen.contains(identity) else {
      raise InvalidRendererCapability(
        detail=owner + " repeats renderer target " + target.window,
      )
    }
    seen[identity] = ()
  }
}

///|
fn resolve_extension_capabilities(
  capabilities : Array[ExtensionCapabilityBinding],
  base_path : String,
) -> Array[ResolvedExtensionCapability] raise AppConfigurationError {
  let resolved : Array[ResolvedExtensionCapability] = []
  for capability in capabilities {
    let id = capability.spec.id()
    let scope = capability.spec.resolve_permission(capability.scope, base_path) catch {
      error =>
        raise InvalidRendererCapability(
          detail="extension " +
            id +
            " rejected its capability scope: " +
            error.message(),
        )
    }
    guard !capability.spec.op_names().is_empty() else {
      raise InvalidRendererCapability(
        detail="extension " + id + " does not own any registered operation",
      )
    }
    resolved.push(ResolvedExtensionCapability::{
      spec: capability.spec,
      scope,
      targets: capability.targets.copy(),
    })
  }
  resolved
}

///|
fn resolve_window_permission_policy(
  window_id : String,
  policy : BridgePagePolicy,
  app_capabilities : Array[RegisteredAppCommandCapability],
  extension_capabilities : Array[ResolvedExtensionCapability],
) -> WindowPermissionPolicy raise AppConfigurationError {
  let grants : Array[ResolvedPermissionGrant] = []
  for capability in app_capabilities {
    let origins : Map[String, Unit] = Map([])
    for target in capability.targets {
      guard target.window == window_id else { continue }
      let source_origin = renderer_target_origin(target, policy)
      guard !origins.contains(source_origin) else {
        raise InvalidRendererCapability(
          detail="application commands repeat source " +
            source_origin +
            " in window " +
            window_id,
        )
      }
      origins[source_origin] = ()
      grants.push(ResolvedPermissionGrant::{
        source_origin,
        extension_id: application_capability_id,
        ops: capability.ops.copy(),
        scope: Json::empty_object(),
      })
    }
  }
  let extension_origins : Map[String, Unit] = Map([])
  for capability in extension_capabilities {
    for target in capability.targets {
      guard target.window == window_id else { continue }
      let source_origin = renderer_target_origin(target, policy)
      let identity = capability.spec.id() + "\u{0}" + source_origin
      guard !extension_origins.contains(identity) else {
        raise InvalidRendererCapability(
          detail="extension " +
            capability.spec.id() +
            " repeats source " +
            source_origin +
            " in window " +
            window_id,
        )
      }
      extension_origins[identity] = ()
      grants.push(ResolvedPermissionGrant::{
        source_origin,
        extension_id: capability.spec.id(),
        ops: capability.spec.op_names(),
        scope: capability.scope,
      })
    }
  }
  WindowPermissionPolicy::{ grants, }
}

///|
fn renderer_target_origin(
  target : RendererTarget,
  policy : BridgePagePolicy,
) -> String {
  if target.bundled {
    "app"
  } else {
    match policy.entry_origin {
      Some(origin) => origin
      None => "app"
    }
  }
}

///|
fn WindowPermissionPolicy::native_grants(
  self : WindowPermissionPolicy,
  command_extensions : Map[String, @proton_command.AppCommandExtensionSpec],
) -> Array[@native.BridgeGrantConfig] raise AppConfigurationError {
  let source_order : Array[String] = []
  for grant in self.grants {
    if !source_order.contains(grant.source_origin) {
      source_order.push(grant.source_origin)
    }
  }
  let native_grants : Array[@native.BridgeGrantConfig] = []
  for source_origin in source_order {
    let ops : Array[String] = []
    let extensions : Array[@native.BridgeExtensionConfig] = []
    let initialization_units : Array[@native.BridgeInitializationUnit] = []
    for grant in self.grants {
      guard grant.source_origin == source_origin else { continue }
      for op in grant.ops {
        ops.push(op)
      }
      guard grant.extension_id != application_capability_id else { continue }
      let frontend = bridge_frontend_config_for_extension(
        grant.extension_id,
        command_extensions[grant.extension_id],
      )
      for extension in frontend.extensions {
        extensions.push(extension)
      }
      for unit in frontend.initialization_units {
        initialization_units.push(unit)
      }
    }
    native_grants.push(
      @native.BridgeGrantConfig(
        source_origin,
        ops~,
        extensions~,
        initialization_units~,
      ),
    )
  }
  native_grants
}

///|
fn WindowPermissionPolicy::grant_for_request(
  self : WindowPermissionPolicy,
  source_origin : String,
  op : String,
) -> ResolvedPermissionGrant? {
  for grant in self.grants {
    if grant.source_origin == source_origin && grant.ops.contains(op) {
      return Some(grant)
    }
  }
  None
}

///|
fn WindowPermissionPolicy::grants_extension(
  self : WindowPermissionPolicy,
  extension_id : String,
) -> Bool {
  self.grants.any(grant => grant.extension_id == extension_id)
}