///|
fn add_unique_source(sources : Array[Source], raw : String) -> Unit {
  let normalized = normalize_token(raw)
  for source in sources {
    if normalize_token(source.raw) == normalized {
      return
    }
  }
  sources.push(classify_source(raw, 0))
}

///|
fn request_directive_name(kind : ResourceKind) -> String {
  match kind {
    Script => "script-src"
    Style => "style-src"
    Image => "img-src"
    Font => "font-src"
    Connect => "connect-src"
    Frame => "frame-src"
    Media => "media-src"
    Worker => "worker-src"
    Object => "object-src"
    Manifest => "manifest-src"
    Child => "child-src"
    Base => "base-uri"
    Form => "form-action"
    Navigate => "navigate-to"
    Prefetch => "prefetch-src"
    Other => "default-src"
  }
}

///|
fn source_for_request(req : ResourceRequest) -> String {
  if req.is_inline && req.nonce.length() > 0 {
    "'nonce-" + req.nonce + "'"
  } else if extract_origin(req.url).length() > 0 {
    extract_origin(req.url)
  } else {
    "'self'"
  }
}

///|
fn find_bucket(buckets : Array[Directive], name : String) -> Int {
  let mut i = 0
  while i < buckets.length() {
    if buckets[i].name == name {
      return i
    }
    i = i + 1
  }
  -1
}

///|
/// Build a minimal allow-list policy from observed resource requests.
pub fn plan_from_requests(requests : Array[ResourceRequest]) -> MigrationPlan {
  let directives : Array[Directive] = []
  let findings : Array[Finding] = []
  for req in requests {
    let name = request_directive_name(req.kind)
    let raw = source_for_request(req)
    let index = find_bucket(directives, name)
    if index < 0 {
      let sources : Array[Source] = []
      add_unique_source(sources, raw)
      directives.push(make_directive(name, sources, "", 0))
    } else {
      add_unique_source(directives[index].sources, raw)
    }
  }
  let policy = { directives, diagnostics: [] }
  { directives, findings, normalized: normalize(policy) }
}