///|
/// Named policy template for examples and quick starts.
pub(all) struct PolicyTemplate {
  name : String
  title : String
  use_case : String
  header : String
  notes : String
} derive(Eq, Debug)

///|
fn make_policy_template(
  name : String,
  title : String,
  use_case : String,
  header : String,
  notes : String,
) -> PolicyTemplate {
  { name, title, use_case, header, notes }
}

///|
pub fn PolicyTemplate::parse(
  self : PolicyTemplate,
) -> Result[Policy, ParseError] {
  parse_policy(self.header[:])
}

///|
pub fn PolicyTemplate::score(self : PolicyTemplate) -> PolicyScore? {
  match self.parse() {
    Ok(policy) => Some(policy.score())
    Err(_) => None
  }
}

///|
pub fn PolicyTemplate::summary(self : PolicyTemplate) -> String {
  self.name + " " + self.title + " " + self.use_case
}

///|
pub fn PolicyTemplate::audit_text_report(self : PolicyTemplate) -> String {
  match self.parse() {
    Ok(policy) => self.summary() + "\n" + policy.audit_text_report()
    Err(_) => self.summary() + "\ninvalid template"
  }
}

///|
pub fn strict_static_site_template() -> PolicyTemplate {
  make_policy_template(
    "strict-static-site", "Strict static site", "HTML generated site with local scripts and images.",
    "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; form-action 'self'; upgrade-insecure-requests",
    "Good baseline for documentation sites that do not use third-party script.",
  )
}

///|
pub fn nonce_spa_template() -> PolicyTemplate {
  make_policy_template(
    "nonce-spa", "Nonce based single page application", "Frontend app with a server generated nonce.",
    "default-src 'self'; script-src 'self' 'nonce-demo' 'strict-dynamic'; style-src 'self'; img-src 'self' https: data:; connect-src 'self' https:; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; form-action 'self'; upgrade-insecure-requests",
    "Replace nonce-demo with a per-response unpredictable nonce.",
  )
}

///|
pub fn wasm_app_template() -> PolicyTemplate {
  make_policy_template(
    "wasm-app", "WebAssembly application", "MoonBit WebAssembly app with controlled dynamic compilation needs.",
    "default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; connect-src 'self' https:; img-src 'self' data:; style-src 'self'; worker-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; upgrade-insecure-requests",
    "Use wasm-unsafe-eval only when the runtime really requires dynamic wasm compilation.",
  )
}

///|
pub fn api_console_template() -> PolicyTemplate {
  make_policy_template(
    "api-console", "API console", "Interactive API documentation with remote API endpoints.",
    "default-src 'self'; script-src 'self' 'nonce-demo'; style-src 'self'; img-src 'self' data: https:; connect-src 'self' https://api.service.local; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; form-action 'self'; report-to csp-endpoint; upgrade-insecure-requests",
    "Keep connect-src endpoints explicit for each deployment.",
  )
}

///|
pub fn admin_panel_template() -> PolicyTemplate {
  make_policy_template(
    "admin-panel", "Admin panel", "Sensitive business dashboard with no embedding.",
    "default-src 'self'; script-src 'self' 'nonce-demo'; style-src 'self'; img-src 'self' data:; connect-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'; form-action 'self'; require-trusted-types-for 'script'; trusted-types default; upgrade-insecure-requests",
    "A strict baseline for pages with sensitive administrative actions.",
  )
}

///|
pub fn markdown_preview_template() -> PolicyTemplate {
  make_policy_template(
    "markdown-preview", "Markdown preview", "Preview surface that renders user content.",
    "default-src 'none'; img-src 'self' https: data:; style-src 'self'; script-src 'self' 'nonce-demo'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'; form-action 'none'; sandbox allow-scripts allow-same-origin",
    "Rendering user content should prefer a sandboxed origin.",
  )
}

///|
pub fn embedded_widget_template() -> PolicyTemplate {
  make_policy_template(
    "embedded-widget", "Embedded widget", "Widget intentionally loaded inside partner sites.",
    "default-src 'self'; script-src 'self' 'nonce-demo'; style-src 'self'; img-src 'self' https: data:; connect-src 'self' https:; object-src 'none'; base-uri 'none'; frame-ancestors https://partner.service.local; form-action 'none'; upgrade-insecure-requests",
    "frame-ancestors should list only approved embedding origins.",
  )
}

///|
pub fn locked_down_document_template() -> PolicyTemplate {
  make_policy_template(
    "locked-down-document", "Locked down document", "Read-only document with no script execution.",
    "default-src 'none'; img-src 'self' data:; style-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'; form-action 'none'; sandbox",
    "Useful for generated reports or static pages that do not need script.",
  )
}

///|
pub fn report_only_migration_template() -> PolicyTemplate {
  make_policy_template(
    "report-only-migration", "Report-only migration", "Migration stage where violations are observed before enforcement.",
    "default-src 'self'; script-src 'self' 'nonce-demo'; style-src 'self' 'unsafe-inline'; img-src 'self' https: data:; connect-src 'self' https:; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; report-to csp-endpoint; upgrade-insecure-requests",
    "Use as a starting point, then remove unsafe-inline after telemetry stabilizes.",
  )
}

///|
pub fn legacy_cdn_template() -> PolicyTemplate {
  make_policy_template(
    "legacy-cdn", "Legacy CDN migration", "Application still loading selected assets from a CDN.",
    "default-src 'self'; script-src 'self' https://cdn.service.local; style-src 'self' https://cdn.service.local; img-src 'self' https://cdn.service.local data:; font-src 'self' https://cdn.service.local; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; upgrade-insecure-requests",
    "Prefer explicit CDN hosts over scheme-wide https: allowlists.",
  )
}

///|
pub fn strict_api_docs_template() -> PolicyTemplate {
  make_policy_template(
    "strict-api-docs", "Strict API docs", "Documentation with API examples but no live remote execution.",
    "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; connect-src 'none'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; form-action 'none'; upgrade-insecure-requests",
    "connect-src none prevents accidental live calls from static docs.",
  )
}

///|
pub fn service_worker_template() -> PolicyTemplate {
  make_policy_template(
    "service-worker-app", "Service worker app", "Offline-capable app with a service worker.",
    "default-src 'self'; script-src 'self' 'nonce-demo'; worker-src 'self'; connect-src 'self' https:; img-src 'self' data:; style-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; upgrade-insecure-requests",
    "worker-src should remain explicit for service worker heavy apps.",
  )
}

///|
pub fn media_library_template() -> PolicyTemplate {
  make_policy_template(
    "media-library", "Media library", "App that streams images, audio, and video from controlled origins.",
    "default-src 'self'; script-src 'self' 'nonce-demo'; style-src 'self'; img-src 'self' https://media.service.local data:; media-src 'self' https://media.service.local; connect-src 'self' https://api.service.local; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; upgrade-insecure-requests",
    "Keep media and API origins separate for easier review.",
  )
}

///|
pub fn upload_portal_template() -> PolicyTemplate {
  make_policy_template(
    "upload-portal", "Upload portal", "Authenticated upload page with previews.",
    "default-src 'self'; script-src 'self' 'nonce-demo'; style-src 'self'; img-src 'self' blob: data:; connect-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'; form-action 'self'; upgrade-insecure-requests",
    "Blob previews are common for upload flows but should stay out of script-src.",
  )
}

///|
pub fn payment_checkout_template() -> PolicyTemplate {
  make_policy_template(
    "payment-checkout", "Payment checkout", "Checkout page with one approved payment frame.",
    "default-src 'self'; script-src 'self' 'nonce-demo' https://pay.service.local; style-src 'self'; img-src 'self' https: data:; frame-src https://pay.service.local; connect-src 'self' https://pay.service.local; object-src 'none'; base-uri 'none'; frame-ancestors 'none'; form-action 'self' https://pay.service.local; upgrade-insecure-requests",
    "Payment integrations should be isolated and explicitly allowed.",
  )
}

///|
pub fn analytics_limited_template() -> PolicyTemplate {
  make_policy_template(
    "analytics-limited", "Limited analytics", "Site with one analytics endpoint and no third-party script execution.",
    "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data: https://analytics.service.local; connect-src 'self' https://analytics.service.local; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; upgrade-insecure-requests",
    "Prefer server-side analytics or explicit endpoints over broad script-src hosts.",
  )
}

///|
pub fn local_dev_template() -> PolicyTemplate {
  make_policy_template(
    "local-dev", "Local development", "Development-only policy with local endpoints.",
    "default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; connect-src 'self' http://localhost:* ws://localhost:*; object-src 'none'; base-uri 'self'; frame-ancestors 'none'",
    "This template intentionally contains development risks and should not be used in production.",
  )
}

///|
pub fn production_spa_template() -> PolicyTemplate {
  make_policy_template(
    "production-spa", "Production SPA", "Production single page app with strict script trust.",
    "default-src 'self'; script-src 'self' 'nonce-demo' 'strict-dynamic'; style-src 'self'; img-src 'self' https: data:; connect-src 'self' https://api.service.local; font-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'; form-action 'self'; require-trusted-types-for 'script'; trusted-types default; upgrade-insecure-requests",
    "A strong baseline for modern apps that can deploy nonces.",
  )
}

///|
pub fn locked_iframe_template() -> PolicyTemplate {
  make_policy_template(
    "locked-iframe", "Locked iframe", "Isolated iframe used for previews or external content wrappers.",
    "default-src 'none'; script-src 'none'; style-src 'self'; img-src 'self' data:; object-src 'none'; base-uri 'none'; frame-ancestors 'self'; form-action 'none'; sandbox",
    "Useful when the content should not run active script.",
  )
}

///|
pub fn no_script_report_template() -> PolicyTemplate {
  make_policy_template(
    "no-script-report", "No script report", "Static generated report with images and styles only.",
    "default-src 'none'; script-src 'none'; style-src 'self'; img-src 'self' data:; font-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'; form-action 'none'",
    "A good high-security option for exported reports.",
  )
}

///|
pub fn trusted_types_app_template() -> PolicyTemplate {
  make_policy_template(
    "trusted-types-app", "Trusted Types app", "Script-heavy web app with DOM injection sink protection.",
    "default-src 'self'; script-src 'self' 'nonce-demo' 'strict-dynamic'; style-src 'self'; img-src 'self' data: https:; connect-src 'self' https://api.service.local; object-src 'none'; base-uri 'none'; frame-ancestors 'none'; require-trusted-types-for 'script'; trusted-types default app-html sanitizer; upgrade-insecure-requests",
    "Trusted Types policy names should match audited application code.",
  )
}

///|
pub fn worker_compute_template() -> PolicyTemplate {
  make_policy_template(
    "worker-compute", "Worker compute", "Browser compute task that isolates work into workers.",
    "default-src 'self'; script-src 'self' 'nonce-demo'; worker-src 'self' blob:; connect-src 'self'; img-src 'self' data:; style-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'; form-action 'none'; upgrade-insecure-requests",
    "blob: in worker-src may be acceptable when workers are generated locally.",
  )
}

///|
pub fn offline_docs_template() -> PolicyTemplate {
  make_policy_template(
    "offline-docs", "Offline docs", "Documentation bundle that should work without network access.",
    "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'none'; media-src 'none'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'; form-action 'none'",
    "The explicit connect-src none makes offline behavior visible.",
  )
}

///|
pub fn all_policy_templates() -> Array[PolicyTemplate] {
  let templates : Array[PolicyTemplate] = []
  templates.push(strict_static_site_template())
  templates.push(nonce_spa_template())
  templates.push(wasm_app_template())
  templates.push(api_console_template())
  templates.push(admin_panel_template())
  templates.push(markdown_preview_template())
  templates.push(embedded_widget_template())
  templates.push(locked_down_document_template())
  templates.push(report_only_migration_template())
  templates.push(legacy_cdn_template())
  templates.push(strict_api_docs_template())
  templates.push(service_worker_template())
  templates.push(media_library_template())
  templates.push(upload_portal_template())
  templates.push(payment_checkout_template())
  templates.push(analytics_limited_template())
  templates.push(local_dev_template())
  templates.push(production_spa_template())
  templates.push(locked_iframe_template())
  templates.push(no_script_report_template())
  templates.push(trusted_types_app_template())
  templates.push(worker_compute_template())
  templates.push(offline_docs_template())
  templates
}

///|
pub fn policy_template_names() -> Array[String] {
  let names : Array[String] = []
  for template in all_policy_templates() {
    names.push(template.name)
  }
  names
}

///|
pub fn policy_template_by_name(name : StringView) -> PolicyTemplate? {
  let key = lower_ascii(name)
  for template in all_policy_templates() {
    if template.name == key {
      return Some(template)
    }
  }
  None
}

///|
pub fn valid_policy_templates() -> Array[PolicyTemplate] {
  let out : Array[PolicyTemplate] = []
  for template in all_policy_templates() {
    match template.parse() {
      Ok(_) => out.push(template)
      Err(_) => ()
    }
  }
  out
}

///|
pub fn template_scoreboard() -> String {
  let rows : Array[String] = []
  for template in all_policy_templates() {
    match template.score() {
      Some(score) => rows.push(template.name + " " + score.summary())
      None => rows.push(template.name + " invalid")
    }
  }
  rows.join("\n")
}

///|
pub fn compare_policy_templates(
  before_name : StringView,
  after_name : StringView,
) -> PolicyDiff? {
  match
    (policy_template_by_name(before_name), policy_template_by_name(after_name)) {
    (Some(before_template), Some(after_template)) =>
      match (before_template.parse(), after_template.parse()) {
        (Ok(before), Ok(after)) => Some(diff_policy(before, after))
        (_, _) => None
      }
    (_, _) => None
  }
}

///|
pub fn template_audit_reports() -> String {
  let reports : Array[String] = []
  for template in all_policy_templates() {
    reports.push(template.audit_text_report())
  }
  reports.join("\n\n")
}

///|
pub fn safest_policy_template() -> PolicyTemplate? {
  let templates = all_policy_templates()
  if templates.length() == 0 {
    return None
  }
  let mut best = templates[0]
  let mut best_score = -1
  for template in templates {
    match template.score() {
      Some(score) =>
        if score.final_score > best_score {
          best = template
          best_score = score.final_score
        }
      None => ()
    }
  }
  Some(best)
}

///|
pub fn riskiest_policy_template() -> PolicyTemplate? {
  let templates = all_policy_templates()
  if templates.length() == 0 {
    return None
  }
  let mut worst = templates[0]
  let mut worst_score = 101
  for template in templates {
    match template.score() {
      Some(score) =>
        if score.final_score < worst_score {
          worst = template
          worst_score = score.final_score
        }
      None => ()
    }
  }
  Some(worst)
}

///|
pub fn templates_with_high_findings() -> Array[PolicyTemplate] {
  let out : Array[PolicyTemplate] = []
  for template in all_policy_templates() {
    match template.score() {
      Some(score) => if score.findings.high > 0 { out.push(template) }
      None => ()
    }
  }
  out
}

///|
pub fn templates_without_high_findings() -> Array[PolicyTemplate] {
  let out : Array[PolicyTemplate] = []
  for template in all_policy_templates() {
    match template.score() {
      Some(score) => if score.findings.high == 0 { out.push(template) }
      None => ()
    }
  }
  out
}

///|
pub fn template_catalog_summary() -> String {
  "templates=\{all_policy_templates().length()} valid=\{valid_policy_templates().length()} clean=\{templates_without_high_findings().length()} risky=\{templates_with_high_findings().length()}"
}