///|
/// Security baseline profiles for common deployment shapes.
///
/// The generic audit report tells you what is wrong. These profiles tell you
/// what a sensible target configuration looks like for different scenarios.
pub(all) enum RequirementMode {
  Exact
  Contains
  ContainsAny
  Prefix
  AnyOf
  Absent
} derive(Eq, Debug)

///|
pub(all) enum ProfileKind {
  StaticSite
  SpaApp
  ApiService
  AdminConsole
  DocsSite
  EmbeddedWidget
  InternalDashboard
  FileDownload
  WasmEdge
  PublicPortal
  LoginFlow
  MediaCdn
} derive(Eq, Debug)

///|
pub(all) enum ProfileStatus {
  Satisfied
  Missing
  Weak
  Conflict
  Optional
} derive(Eq, Debug)

///|
pub(all) struct HeaderRequirement {
  name : String
  expected : String
  mode : RequirementMode
  required : Bool
  severity : Severity
  rationale : String
} derive(Eq, Debug)

///|
pub(all) struct SecurityProfile {
  kind : ProfileKind
  key : String
  title : String
  audience : String
  summary : String
  requirements : Array[HeaderRequirement]
} derive(Eq, Debug)

///|
pub(all) struct ProfileFinding {
  name : String
  status : ProfileStatus
  actual : String?
  expected : String
  severity : Severity
  rationale : String
} derive(Eq, Debug)

///|
pub(all) struct ProfileAssessment {
  profile_key : String
  score : Int
  matched : Int
  missing : Int
  weak : Int
  conflict : Int
  optional : Int
  findings : Array[ProfileFinding]
} derive(Eq, Debug)

///|
pub fn RequirementMode::label(self : RequirementMode) -> String {
  match self {
    Exact => "exact"
    Contains => "contains"
    ContainsAny => "contains-any"
    Prefix => "prefix"
    AnyOf => "any-of"
    Absent => "absent"
  }
}

///|
pub fn ProfileStatus::label(self : ProfileStatus) -> String {
  match self {
    Satisfied => "satisfied"
    Missing => "missing"
    Weak => "weak"
    Conflict => "conflict"
    Optional => "optional"
  }
}

///|
pub fn ProfileKind::label(self : ProfileKind) -> String {
  match self {
    StaticSite => "static-site"
    SpaApp => "spa-app"
    ApiService => "api-service"
    AdminConsole => "admin-console"
    DocsSite => "docs-site"
    EmbeddedWidget => "embedded-widget"
    InternalDashboard => "internal-dashboard"
    FileDownload => "file-download"
    WasmEdge => "wasm-edge"
    PublicPortal => "public-portal"
    LoginFlow => "login-flow"
    MediaCdn => "media-cdn"
  }
}

///|
pub fn requirement(
  name : StringView,
  expected : StringView,
  mode? : RequirementMode = Exact,
  required? : Bool = true,
  severity? : Severity = Medium,
  rationale? : StringView = "",
) -> HeaderRequirement {
  {
    name: name.to_owned(),
    expected: expected.to_owned(),
    mode,
    required,
    severity,
    rationale: rationale.to_owned(),
  }
}

///|
pub fn profile(
  kind : ProfileKind,
  key : StringView,
  title : StringView,
  audience : StringView,
  summary : StringView,
  requirements : Array[HeaderRequirement],
) -> SecurityProfile {
  {
    kind,
    key: key.to_owned(),
    title: title.to_owned(),
    audience: audience.to_owned(),
    summary: summary.to_owned(),
    requirements,
  }
}

///|
pub fn requirement_matches(
  req : HeaderRequirement,
  actual : StringView,
) -> Bool {
  match req.mode {
    Exact => actual.trim().to_lower().to_owned() == req.expected.to_lower()
    Contains => contains_expected_clauses(actual, req.expected)
    ContainsAny => contains_any_clause(actual, req.expected)
    Prefix =>
      actual.trim().to_lower().to_owned().has_prefix(req.expected.to_lower())
    AnyOf => any_of_matches(actual, req.expected)
    Absent => actual.is_empty()
  }
}

///|
/// A header requirement may describe several CSP or HSTS clauses separated by
/// semicolons. Match each clause independently so unrelated clauses can be
/// inserted without making an otherwise valid policy look weak.
fn contains_expected_clauses(actual : StringView, expected : String) -> Bool {
  let lowered_actual = actual.trim().to_lower().to_owned()
  expected
  .split(";")
  .all(part => {
    let clause = part.trim().to_lower().to_owned()
    clause.is_empty() || lowered_actual.contains(clause)
  })
}

///|
fn contains_any_clause(actual : StringView, expected : String) -> Bool {
  let lowered_actual = actual.trim().to_lower().to_owned()
  expected
  .split("|")
  .any(part => {
    let clause = part.trim().to_lower().to_owned()
    !clause.is_empty() && lowered_actual.contains(clause)
  })
}

///|
fn any_of_matches(actual : StringView, expected : String) -> Bool {
  let lowered = actual.trim().to_lower().to_owned()
  for candidate in expected.split("|") {
    if lowered == candidate.trim().to_lower().to_owned() {
      return true
    }
  }
  false
}

///|
pub fn SecurityProfile::required_count(self : SecurityProfile) -> Int {
  self.requirements.count_if(req => req.required)
}

///|
pub fn SecurityProfile::optional_count(self : SecurityProfile) -> Int {
  self.requirements.count_if(req => !req.required)
}

///|
pub fn SecurityProfile::find_requirement(
  self : SecurityProfile,
  name : StringView,
) -> HeaderRequirement? {
  let expected = normalize_header_name(name)
  self.requirements.iter().find_first(req => req.name == expected)
}

///|
pub fn SecurityProfile::required_headers(
  self : SecurityProfile,
) -> Array[String] {
  self.requirements.filter(req => req.required).map(req => req.name)
}

///|
pub fn SecurityProfile::optional_headers(
  self : SecurityProfile,
) -> Array[String] {
  self.requirements.filter(req => !req.required).map(req => req.name)
}

///|
pub fn SecurityProfile::to_markdown(self : SecurityProfile) -> String {
  let lines : Array[String] = []
  lines.push("## " + self.title)
  lines.push("")
  lines.push("- Key: `" + self.key + "`")
  lines.push("- Audience: " + self.audience)
  lines.push("- Summary: " + self.summary)
  lines.push("- Required headers: " + self.required_count().to_string())
  lines.push("- Optional headers: " + self.optional_count().to_string())
  lines.push("")
  lines.push("| Header | Mode | Required | Severity | Rationale |")
  lines.push("| --- | --- | --- | --- | --- |")
  for req in self.requirements {
    lines.push(
      "| `" +
      req.name +
      "` | " +
      req.mode.label() +
      " | " +
      bool_text(req.required) +
      " | " +
      req.severity.label() +
      " | " +
      req.rationale.replace_all(old="|", new="\\|") +
      " |",
    )
  }
  lines.join("\n")
}

///|
fn bool_text(flag : Bool) -> String {
  if flag {
    "yes"
  } else {
    "no"
  }
}

///|
pub fn SecurityProfile::score_headers(
  self : SecurityProfile,
  headers : HeaderSet,
) -> ProfileAssessment {
  let findings : Array[ProfileFinding] = []
  let mut matched = 0
  let mut missing = 0
  let mut weak = 0
  let mut conflict = 0
  let mut optional = 0
  for req in self.requirements {
    let actual = headers.get(req.name)
    match actual {
      Some(value) =>
        if req.mode == Absent {
          conflict += 1
          findings.push({
            name: req.name,
            status: Conflict,
            actual,
            expected: req.expected,
            severity: req.severity,
            rationale: req.rationale,
          })
        } else if requirement_matches(req, value) {
          matched += 1
          findings.push({
            name: req.name,
            status: Satisfied,
            actual,
            expected: req.expected,
            severity: req.severity,
            rationale: req.rationale,
          })
        } else {
          weak += 1
          findings.push({
            name: req.name,
            status: Weak,
            actual,
            expected: req.expected,
            severity: req.severity,
            rationale: req.rationale,
          })
        }
      None =>
        if req.required {
          missing += 1
          findings.push({
            name: req.name,
            status: Missing,
            actual: None,
            expected: req.expected,
            severity: req.severity,
            rationale: req.rationale,
          })
        } else {
          optional += 1
          findings.push({
            name: req.name,
            status: Optional,
            actual: None,
            expected: req.expected,
            severity: req.severity,
            rationale: req.rationale,
          })
        }
    }
  }
  let score = score_profile_result(matched, missing, weak, conflict, optional)
  {
    profile_key: self.key,
    score,
    matched,
    missing,
    weak,
    conflict,
    optional,
    findings,
  }
}

///|
fn score_profile_result(
  matched : Int,
  missing : Int,
  weak : Int,
  conflict : Int,
  optional : Int,
) -> Int {
  let mut score = 100
  score -= missing * 14
  score -= weak * 9
  score -= conflict * 18
  score -= optional * 1
  score += matched / 2
  if score < 0 {
    0
  } else {
    score
  }
}

///|
pub fn ProfileAssessment::summary(self : ProfileAssessment) -> String {
  "matched=" +
  self.matched.to_string() +
  ", missing=" +
  self.missing.to_string() +
  ", weak=" +
  self.weak.to_string() +
  ", conflict=" +
  self.conflict.to_string()
}

///|
/// Returns true when all required profile checks are satisfied.
pub fn ProfileAssessment::meets_required(self : ProfileAssessment) -> Bool {
  self.missing == 0 && self.weak == 0 && self.conflict == 0
}

///|
pub fn ProfileAssessment::to_markdown(self : ProfileAssessment) -> String {
  let lines : Array[String] = []
  lines.push("# Profile Assessment")
  lines.push("")
  lines.push("- Profile: `" + self.profile_key + "`")
  lines.push("- Score: " + self.score.to_string())
  lines.push("- Summary: " + self.summary())
  lines.push("")
  lines.push("| Header | Status | Expected | Actual | Severity |")
  lines.push("| --- | --- | --- | --- | --- |")
  for finding in self.findings {
    lines.push(
      "| `" +
      finding.name +
      "` | " +
      finding.status.label() +
      " | " +
      finding.expected.replace_all(old="|", new="\\|") +
      " | " +
      option_text(finding.actual) +
      " | " +
      finding.severity.label() +
      " |",
    )
  }
  lines.join("\n")
}

///|
fn option_text(value : String?) -> String {
  match value {
    Some(v) => v.replace_all(old="|", new="\\|")
    None => "-"
  }
}

///|
pub fn profile_lookup(key : StringView) -> SecurityProfile? {
  let expected = key.to_lower().to_owned()
  for p in profile_catalog() {
    if p.key == expected {
      return Some(p)
    }
  }
  None
}

///|
pub fn profile_catalog() -> Array[SecurityProfile] {
  [
    static_site_profile(),
    spa_app_profile(),
    api_service_profile(),
    admin_console_profile(),
    docs_site_profile(),
    embedded_widget_profile(),
    internal_dashboard_profile(),
    file_download_profile(),
    wasm_edge_profile(),
    public_portal_profile(),
    login_flow_profile(),
    media_cdn_profile(),
  ]
}

///|
pub fn profile_keys() -> Array[String] {
  profile_catalog().map(p => p.key)
}

///|
pub fn profile_titles() -> Array[String] {
  profile_catalog().map(p => p.title)
}

///|
pub fn profile_count() -> Int {
  profile_catalog().length()
}

///|
pub fn profile_catalog_markdown() -> String {
  let lines : Array[String] = []
  lines.push("# Security Profiles")
  lines.push("")
  lines.push("- Count: " + profile_count().to_string())
  lines.push("")
  for p in profile_catalog() {
    lines.push("- `" + p.key + "` - " + p.summary)
  }
  lines.join("\n")
}

///|
pub fn static_site_profile() -> SecurityProfile {
  profile(
    StaticSite,
    "static-site",
    "Static Site",
    "marketing pages, docs, and pre-rendered sites",
    "A baseline for static web delivery with no privileged browser APIs.",
    [
      requirement(
        "content-security-policy",
        "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'",
        mode=Contains,
        severity=High,
        rationale="Static sites should keep every script and object source explicit.",
      ),
      requirement(
        "strict-transport-security",
        "max-age=31536000",
        mode=Contains,
        severity=Medium,
        rationale="Static sites should force HTTPS for a long window.",
      ),
      requirement(
        "x-content-type-options",
        "nosniff",
        mode=Exact,
        severity=Medium,
        rationale="Content sniffing is an avoidable source of script injection.",
      ),
      requirement(
        "referrer-policy",
        "strict-origin-when-cross-origin|no-referrer",
        mode=AnyOf,
        severity=Low,
        rationale="Shared content should not leak full path details to other origins.",
      ),
      requirement(
        "permissions-policy",
        "camera=()",
        mode=Contains,
        required=false,
        severity=Low,
        rationale="Static sites rarely need device sensors or capture APIs.",
      ),
      requirement(
        "cross-origin-opener-policy",
        "same-origin",
        mode=AnyOf,
        required=false,
        severity=Low,
        rationale="Isolation is helpful but not always necessary for public pages.",
      ),
      requirement(
        "cross-origin-resource-policy",
        "same-origin|same-site",
        mode=AnyOf,
        required=false,
        severity=Low,
        rationale="Static assets can often be tightened to reduce embedding risk.",
      ),
      requirement(
        "x-frame-options",
        "deny|sameorigin",
        mode=AnyOf,
        required=false,
        severity=Low,
        rationale="Clickjacking protection should come from CSP or XFO.",
      ),
    ],
  )
}

///|
pub fn spa_app_profile() -> SecurityProfile {
  profile(
    SpaApp,
    "spa-app",
    "Single-Page App",
    "client-heavy applications with dynamic script loading",
    "A profile for app shells that still need tight script governance.",
    [
      requirement(
        "content-security-policy",
        "default-src 'self'; script-src 'self'",
        mode=Contains,
        severity=High,
        rationale="App shells should pin script loading to trusted origins.",
      ),
      requirement(
        "content-security-policy",
        "frame-ancestors 'none'",
        mode=Contains,
        severity=Medium,
        rationale="Client applications are common clickjacking targets.",
      ),
      requirement(
        "strict-transport-security",
        "max-age=31536000",
        mode=Contains,
        severity=Medium,
        rationale="SPA deployment benefits from a long HTTPS baseline.",
      ),
      requirement(
        "x-content-type-options",
        "nosniff",
        mode=Exact,
        severity=Medium,
        rationale="Script and module delivery should not rely on MIME guessing.",
      ),
      requirement(
        "referrer-policy",
        "strict-origin-when-cross-origin",
        mode=Exact,
        severity=Low,
        rationale="SPAs often fetch many cross-origin APIs and assets.",
      ),
      requirement(
        "permissions-policy",
        "geolocation=()",
        mode=Contains,
        required=false,
        severity=Low,
        rationale="Application shells usually do not need privileged APIs at load time.",
      ),
      requirement(
        "cross-origin-opener-policy",
        "same-origin",
        mode=AnyOf,
        required=false,
        severity=Low,
        rationale="Isolation is valuable if the app uses high-value auth flows.",
      ),
      requirement(
        "cross-origin-resource-policy",
        "same-origin",
        mode=AnyOf,
        required=false,
        severity=Low,
        rationale="SPA assets are usually best kept within the same origin set.",
      ),
    ],
  )
}

///|
pub fn api_service_profile() -> SecurityProfile {
  profile(
    ApiService,
    "api-service",
    "API Service",
    "JSON and machine-facing services",
    "A profile for services that emit JSON, tokens, or binary data.",
    [
      requirement(
        "content-security-policy",
        "default-src 'none'",
        mode=Contains,
        severity=High,
        rationale="APIs should not expose browser execution surfaces by default.",
      ),
      requirement(
        "strict-transport-security",
        "max-age=31536000",
        mode=Contains,
        severity=High,
        rationale="Machine clients should still only talk to HTTPS endpoints.",
      ),
      requirement(
        "x-content-type-options",
        "nosniff",
        mode=Exact,
        severity=Medium,
        rationale="JSON endpoints should not be sniffed as executable content.",
      ),
      requirement(
        "referrer-policy",
        "no-referrer|strict-origin-when-cross-origin",
        mode=AnyOf,
        severity=Low,
        rationale="APIs should avoid leaking identifying request paths.",
      ),
      requirement(
        "access-control-allow-origin",
        "*",
        mode=Exact,
        required=false,
        severity=Low,
        rationale="Cross-origin access must be explicitly intended and reviewed.",
      ),
      requirement(
        "access-control-allow-credentials",
        "true",
        mode=Exact,
        required=false,
        severity=Low,
        rationale="Credentialed API access should be narrowly controlled.",
      ),
      requirement(
        "cross-origin-resource-policy",
        "same-origin",
        mode=AnyOf,
        required=false,
        severity=Low,
        rationale="APIs usually benefit from not being embeddable cross-origin.",
      ),
      requirement(
        "permissions-policy",
        "camera=()",
        mode=Contains,
        required=false,
        severity=Low,
        rationale="Machine APIs should not imply browser capture permissions.",
      ),
    ],
  )
}

///|
pub fn admin_console_profile() -> SecurityProfile {
  profile(
    AdminConsole,
    "admin-console",
    "Admin Console",
    "operator and configuration dashboards",
    "A tighter profile for privileged interfaces and operational backends.",
    [
      requirement(
        "content-security-policy",
        "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'",
        mode=Contains,
        severity=High,
        rationale="Administrative screens should minimize all third-party execution.",
      ),
      requirement(
        "strict-transport-security",
        "max-age=31536000; includesubdomains",
        mode=Contains,
        severity=High,
        rationale="Operators deserve the strongest HTTPS baseline available.",
      ),
      requirement(
        "x-content-type-options",
        "nosniff",
        mode=Exact,
        severity=Medium,
        rationale="Admin consoles often render mixed data and attachments.",
      ),
      requirement(
        "x-frame-options",
        "deny",
        mode=AnyOf,
        severity=Medium,
        rationale="Administrative UI should not be embedded elsewhere.",
      ),
      requirement(
        "referrer-policy",
        "no-referrer",
        mode=AnyOf,
        severity=Low,
        rationale="Operational URLs should stay private when operators switch tabs.",
      ),
      requirement(
        "permissions-policy",
        "geolocation=()",
        mode=Contains,
        required=false,
        severity=Low,
        rationale="Operator dashboards rarely need sensor or capture APIs.",
      ),
      requirement(
        "cross-origin-opener-policy",
        "same-origin",
        mode=AnyOf,
        required=false,
        severity=Low,
        rationale="Isolation strengthens stateful login and tab separation.",
      ),
      requirement(
        "cross-origin-resource-policy",
        "same-origin",
        mode=AnyOf,
        required=false,
        severity=Low,
        rationale="Console assets are usually not meant for other origins.",
      ),
    ],
  )
}

///|
pub fn docs_site_profile() -> SecurityProfile {
  profile(
    DocsSite,
    "docs-site",
    "Documentation Site",
    "developer docs and reference portals",
    "A profile for documentation surfaces that need clarity without looseness.",
    [
      requirement(
        "content-security-policy",
        "default-src 'self'; object-src 'none'",
        mode=Contains,
        severity=High,
        rationale="Docs pages can still be abused for script or embed injection.",
      ),
      requirement(
        "strict-transport-security",
        "max-age=31536000",
        mode=Contains,
        severity=Medium,
        rationale="Documentation should be safely cached behind HTTPS.",
      ),
      requirement(
        "x-content-type-options",
        "nosniff",
        mode=Exact,
        severity=Medium,
        rationale="Docs often serve code samples and raw assets.",
      ),
      requirement(
        "referrer-policy",
        "strict-origin-when-cross-origin",
        mode=Exact,
        severity=Low,
        rationale="Documentation links often cross domains and external sites.",
      ),
      requirement(
        "permissions-policy",
        "camera=()",
        mode=Contains,
        required=false,
        severity=Low,
        rationale="Docs pages should not hint at interactive device access.",
      ),
      requirement(
        "x-frame-options",
        "sameorigin",
        mode=AnyOf,
        required=false,
        severity=Low,
        rationale="Embedding is okay only when explicitly intended.",
      ),
      requirement(
        "cross-origin-opener-policy",
        "same-origin-allow-popups|same-origin",
        mode=AnyOf,
        required=false,
        severity=Low,
        rationale="Docs may open external links but should stay isolated.",
      ),
      requirement(
        "cross-origin-resource-policy",
        "same-site",
        mode=AnyOf,
        required=false,
        severity=Low,
        rationale="Reference sites can often keep assets site-local.",
      ),
    ],
  )
}

///|
pub fn embedded_widget_profile() -> SecurityProfile {
  profile(
    EmbeddedWidget,
    "embedded-widget",
    "Embedded Widget",
    "widgets designed to be embedded in other sites",
    "A profile for third-party widgets with controlled embedding behavior.",
    [
      requirement(
        "content-security-policy",
        "frame-ancestors",
        mode=Contains,
        severity=High,
        rationale="Embedded widgets must declare who can embed them.",
      ),
      requirement(
        "content-security-policy",
        "script-src 'self'",
        mode=Contains,
        severity=High,
        rationale="Widget code should load scripts from predictable origins.",
      ),
      requirement(
        "strict-transport-security",
        "max-age=31536000",
        mode=Contains,
        severity=Medium,
        rationale="Third-party embedding still depends on HTTPS integrity.",
      ),
      requirement(
        "x-content-type-options",
        "nosniff",
        mode=Exact,
        severity=Medium,
        rationale="Widgets often load fragments and typed content.",
      ),
      requirement(
        "referrer-policy",
        "strict-origin-when-cross-origin",
        mode=Exact,
        severity=Low,
        rationale="Embedded widgets should not leak tenant-specific paths.",
      ),
      requirement(
        "permissions-policy",
        "camera=()",
        mode=Contains,
        required=false,
        severity=Low,
        rationale="Embedded widgets should not request sensor access by default.",
      ),
      requirement(
        "cross-origin-opener-policy",
        "unsafe-none",
        mode=AnyOf,
        required=false,
        severity=Low,
        rationale="Embeddable content usually cannot demand full browsing isolation.",
      ),
      requirement(
        "cross-origin-resource-policy",
        "cross-origin",
        mode=AnyOf,
        required=false,
        severity=Low,
        rationale="Widgets often need controlled cross-origin resource delivery.",
      ),
    ],
  )
}

///|
pub fn internal_dashboard_profile() -> SecurityProfile {
  profile(
    InternalDashboard,
    "internal-dashboard",
    "Internal Dashboard",
    "staff-only analytics and operations surfaces",
    "A profile for internal dashboards with stronger privacy expectations.",
    [
      requirement(
        "content-security-policy",
        "default-src 'self'; object-src 'none'; base-uri 'self'",
        mode=Contains,
        severity=High,
        rationale="Internal dashboards should still treat scripts as untrusted input.",
      ),
      requirement(
        "strict-transport-security",
        "max-age=31536000; includesubdomains",
        mode=Contains,
        severity=High,
        rationale="Internal zones often span multiple subdomains and services.",
      ),
      requirement(
        "x-content-type-options",
        "nosniff",
        mode=Exact,
        severity=Medium,
        rationale="Dashboards tend to render mixed data attachments and inline tools.",
      ),
      requirement(
        "x-frame-options",
        "deny",
        mode=AnyOf,
        severity=Medium,
        rationale="Internal systems should resist clickjacking too.",
      ),
      requirement(
        "referrer-policy",
        "no-referrer",
        mode=AnyOf,
        severity=Low,
        rationale="Internal route names are often sensitive by themselves.",
      ),
      requirement(
        "permissions-policy",
        "camera=()",
        mode=Contains,
        required=false,
        severity=Low,
        rationale="Operations dashboards rarely use browser capture APIs.",
      ),
      requirement(
        "cross-origin-opener-policy",
        "same-origin",
        mode=AnyOf,
        required=false,
        severity=Low,
        rationale="Isolation is usually worth it for internal authenticated apps.",
      ),
      requirement(
        "cross-origin-resource-policy",
        "same-origin",
        mode=AnyOf,
        required=false,
        severity=Low,
        rationale="Internal resources should generally stay same-origin only.",
      ),
    ],
  )
}

///|
pub fn file_download_profile() -> SecurityProfile {
  profile(
    FileDownload,
    "file-download",
    "File Download Service",
    "downloads, release assets, and binary distribution endpoints",
    "A profile for download handlers that should behave like inert delivery.",
    [
      requirement(
        "content-security-policy",
        "default-src 'none'; frame-ancestors 'none'",
        mode=Contains,
        severity=High,
        rationale="Download endpoints should not advertise script execution surfaces.",
      ),
      requirement(
        "strict-transport-security",
        "max-age=31536000",
        mode=Contains,
        severity=High,
        rationale="Binary distribution should be protected by long-lived HTTPS.",
      ),
      requirement(
        "x-content-type-options",
        "nosniff",
        mode=Exact,
        severity=Medium,
        rationale="Downloads should preserve their declared MIME types.",
      ),
      requirement(
        "content-disposition",
        "attachment",
        mode=Contains,
        severity=Medium,
        required=false,
        rationale="File download responses should bias toward attachment semantics.",
      ),
      requirement(
        "referrer-policy",
        "no-referrer",
        mode=AnyOf,
        severity=Low,
        rationale="Binary release URLs can be sensitive identifiers.",
      ),
      requirement(
        "permissions-policy",
        "camera=()",
        mode=Contains,
        required=false,
        severity=Low,
        rationale="Download responses should not hint at privileged browser use.",
      ),
      requirement(
        "cross-origin-resource-policy",
        "same-site|same-origin",
        mode=AnyOf,
        required=false,
        severity=Low,
        rationale="Distribution hosts should control who can embed assets.",
      ),
      requirement(
        "cache-control",
        "no-transform",
        mode=Contains,
        required=false,
        severity=Low,
        rationale="Downloads should avoid unexpected intermediary rewrites.",
      ),
    ],
  )
}

///|
pub fn wasm_edge_profile() -> SecurityProfile {
  profile(
    WasmEdge,
    "wasm-edge",
    "Wasm Edge App",
    "WebAssembly edge or browser apps",
    "A profile for Wasm-heavy frontends and edge runtimes.",
    [
      requirement(
        "content-security-policy",
        "default-src 'self'; script-src 'self'; object-src 'none'",
        mode=Contains,
        severity=High,
        rationale="Wasm apps usually need a compact, explicit execution budget.",
      ),
      requirement(
        "strict-transport-security",
        "max-age=31536000",
        mode=Contains,
        severity=High,
        rationale="Edge code and browser code both benefit from strong HTTPS.",
      ),
      requirement(
        "x-content-type-options",
        "nosniff",
        mode=Exact,
        severity=Medium,
        rationale="Wasm delivery often includes multiple asset types.",
      ),
      requirement(
        "cross-origin-opener-policy",
        "same-origin",
        mode=AnyOf,
        severity=Low,
        required=false,
        rationale="SharedArrayBuffer and isolation-related features are easier this way.",
      ),
      requirement(
        "cross-origin-resource-policy",
        "same-origin",
        mode=AnyOf,
        severity=Low,
        required=false,
        rationale="Wasm bundles and memory helpers should remain tightly scoped.",
      ),
      requirement(
        "referrer-policy",
        "strict-origin-when-cross-origin",
        mode=Exact,
        severity=Low,
        rationale="Wasm apps frequently call out to analytics and service APIs.",
      ),
      requirement(
        "permissions-policy",
        "camera=()",
        mode=Contains,
        required=false,
        severity=Low,
        rationale="Wasm runtimes seldom need access to hardware sensors.",
      ),
      requirement(
        "x-frame-options",
        "deny",
        mode=AnyOf,
        required=false,
        severity=Low,
        rationale="Browser apps that render sensitive state should not be framed.",
      ),
    ],
  )
}

///|
pub fn public_portal_profile() -> SecurityProfile {
  profile(
    PublicPortal,
    "public-portal",
    "Public Portal",
    "customer-facing product portals",
    "A balanced profile for public product areas with account access.",
    [
      requirement(
        "content-security-policy",
        "default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'",
        mode=Contains,
        severity=High,
        rationale="Public portals need a conservative but usable baseline.",
      ),
      requirement(
        "strict-transport-security",
        "max-age=31536000; includesubdomains",
        mode=Contains,
        severity=High,
        rationale="Public portals usually span multiple branded subdomains.",
      ),
      requirement(
        "x-content-type-options",
        "nosniff",
        mode=Exact,
        severity=Medium,
        rationale="Portal pages often mix HTML, JSON, and downloadable files.",
      ),
      requirement(
        "referrer-policy",
        "strict-origin-when-cross-origin",
        mode=Exact,
        severity=Low,
        rationale="Customer journeys should not leak full URLs by default.",
      ),
      requirement(
        "permissions-policy",
        "geolocation=()",
        mode=Contains,
        required=false,
        severity=Low,
        rationale="Most portals do not need device location access.",
      ),
      requirement(
        "cross-origin-opener-policy",
        "same-origin-allow-popups|same-origin",
        mode=AnyOf,
        required=false,
        severity=Low,
        rationale="Portals sometimes open payment or support windows.",
      ),
      requirement(
        "cross-origin-resource-policy",
        "same-site",
        mode=AnyOf,
        required=false,
        severity=Low,
        rationale="Customer portals often rely on same-site assets and embeds.",
      ),
      requirement(
        "x-frame-options",
        "sameorigin",
        mode=AnyOf,
        required=false,
        severity=Low,
        rationale="Framing should be deliberate if enabled at all.",
      ),
    ],
  )
}

///|
pub fn login_flow_profile() -> SecurityProfile {
  profile(
    LoginFlow,
    "login-flow",
    "Login Flow",
    "authentication and sign-in journeys",
    "A profile tuned for login forms, MFA prompts, and password resets.",
    [
      requirement(
        "content-security-policy",
        "default-src 'self'; script-src 'self'; form-action 'self'",
        mode=Contains,
        severity=High,
        rationale="Login flows should keep scripts and form posts on trusted origins.",
      ),
      requirement(
        "content-security-policy",
        "frame-ancestors 'none'",
        mode=Contains,
        severity=High,
        rationale="Authentication screens are attractive clickjacking targets.",
      ),
      requirement(
        "strict-transport-security",
        "max-age=31536000",
        mode=Contains,
        severity=High,
        rationale="Credential exchange should always stay on HTTPS.",
      ),
      requirement(
        "x-content-type-options",
        "nosniff",
        mode=Exact,
        severity=Medium,
        rationale="Login pages often mix JSON config and HTML templates.",
      ),
      requirement(
        "referrer-policy",
        "no-referrer",
        mode=AnyOf,
        severity=Low,
        rationale="Authentication URLs should not leak account state through referrers.",
      ),
      requirement(
        "permissions-policy",
        "camera=()|microphone=()",
        mode=ContainsAny,
        required=false,
        severity=Low,
        rationale="Only explicit MFA screens should ever need those capabilities.",
      ),
      requirement(
        "cross-origin-opener-policy",
        "same-origin",
        mode=AnyOf,
        required=false,
        severity=Low,
        rationale="Auth windows should isolate themselves from opener state.",
      ),
      requirement(
        "x-frame-options",
        "deny",
        mode=AnyOf,
        required=false,
        severity=Low,
        rationale="If CSP frame-ancestors exists, XFO can still reinforce the stance.",
      ),
    ],
  )
}

///|
pub fn media_cdn_profile() -> SecurityProfile {
  profile(
    MediaCdn,
    "media-cdn",
    "Media CDN",
    "asset and image delivery edges",
    "A profile for image, video, font, and binary asset distribution.",
    [
      requirement(
        "content-security-policy",
        "default-src 'none'",
        mode=Contains,
        severity=High,
        rationale="CDN asset endpoints should not imply generic browser execution.",
      ),
      requirement(
        "strict-transport-security",
        "max-age=31536000; includesubdomains",
        mode=Contains,
        severity=High,
        rationale="Media edges commonly front large public surfaces.",
      ),
      requirement(
        "x-content-type-options",
        "nosniff",
        mode=Exact,
        severity=Medium,
        rationale="Asset delivery must preserve declared MIME types.",
      ),
      requirement(
        "cache-control",
        "public",
        mode=Contains,
        severity=Medium,
        required=false,
        rationale="Media CDNs typically need explicit caching behavior.",
      ),
      requirement(
        "cross-origin-resource-policy",
        "cross-origin",
        mode=AnyOf,
        severity=Low,
        required=false,
        rationale="CDN assets are often consumed cross-origin by design.",
      ),
      requirement(
        "referrer-policy",
        "no-referrer",
        mode=AnyOf,
        severity=Low,
        rationale="Asset URLs can reveal bucket or tenant details.",
      ),
      requirement(
        "permissions-policy",
        "camera=()",
        mode=Contains,
        required=false,
        severity=Low,
        rationale="Static media delivery does not need browser sensor permissions.",
      ),
      requirement(
        "x-frame-options",
        "deny",
        mode=AnyOf,
        required=false,
        severity=Low,
        rationale="Assets do not gain anything from being framed as UI.",
      ),
    ],
  )
}

///|
pub fn profile_assessment_catalog(profile : SecurityProfile) -> Array[String] {
  profile.requirements.map(req => {
    req.name + ":" + req.mode.label() + ":" + bool_text(req.required)
  })
}

///|
pub fn profile_gap_headers(
  profile : SecurityProfile,
  headers : HeaderSet,
) -> Array[String] {
  let gaps : Array[String] = []
  let assessment = profile.score_headers(headers)
  for finding in assessment.findings {
    if finding.status != Satisfied {
      gaps.push(finding.name)
    }
  }
  gaps
}

///|
pub fn profile_is_strict(profile : SecurityProfile) -> Bool {
  profile.required_count() >= 6 && profile.optional_count() <= 4
}

///|
pub fn strict_profiles() -> Array[SecurityProfile] {
  profile_catalog().filter(profile_is_strict)
}

///|
pub fn profile_names_markdown() -> String {
  let lines : Array[String] = []
  lines.push("### Profile names")
  for name in profile_keys() {
    lines.push("- `" + name + "`")
  }
  lines.join("\n")
}

///|
pub fn profile_audiences_markdown() -> String {
  let lines : Array[String] = []
  lines.push("### Profile audiences")
  for p in profile_catalog() {
    lines.push("- " + p.title + ": " + p.audience)
  }
  lines.join("\n")
}

///|
pub fn profile_summaries_markdown() -> String {
  let lines : Array[String] = []
  lines.push("# Profile Summary")
  lines.push("")
  for p in profile_catalog() {
    lines.push("- " + p.key + " => " + p.summary)
  }
  lines.join("\n")
}