src/lightspeed/release/maturity_baseline.gleam

//// Framework maturity and community operations baseline contracts for M47.

import gleam/int
import gleam/list
import gleam/string
import lightspeed/release/policy

pub const snapshot_version = 1

/// Framework maturity stage for one release line.
pub type MaturityStage {
  Beta
  GA
}

/// Versioning and deprecation policy baseline.
pub type VersioningPolicy {
  VersioningPolicy(
    semver_required: Bool,
    breaking_change_requires_major: Bool,
    warning_releases_before_removal: Int,
    migration_notes_required: Bool,
    rfc_adr_required: Bool,
  )
}

/// Release hygiene gate.
pub type ReleaseHygieneGate {
  ReleaseHygieneGate(name: String, required: Bool, evidence: String)
}

/// API compatibility breakage check gate.
pub type ApiCompatibilityGate {
  ApiCompatibilityGate(name: String, expected: String, strict: Bool)
}

/// Supply-chain hygiene gate.
pub type SupplyChainGate {
  SupplyChainGate(name: String, required: Bool, evidence: String)
}

/// Contributor workflow baseline.
pub type ContributorWorkflow {
  ContributorWorkflow(
    name: String,
    stages: List(String),
    audit_artifact: String,
  )
}

/// One release-line maturity baseline.
pub type ReleaseLineBaseline {
  ReleaseLineBaseline(
    line: String,
    stage: MaturityStage,
    versioning: VersioningPolicy,
    release_hygiene: List(ReleaseHygieneGate),
    api_compatibility: List(ApiCompatibilityGate),
    supply_chain: List(SupplyChainGate),
    contributor_operations: List(ContributorWorkflow),
    report_artifact: String,
  )
}

/// GA `1.x` maturity baseline.
pub fn ga_1_x_baseline() -> ReleaseLineBaseline {
  let release_policy = policy.ga_1_0_0()
  let deprecation = policy.deprecation(release_policy)

  ReleaseLineBaseline(
    line: "1.x",
    stage: GA,
    versioning: VersioningPolicy(
      semver_required: True,
      breaking_change_requires_major: deprecation.removal_requires_major_bump,
      warning_releases_before_removal: deprecation.warning_releases_before_removal,
      migration_notes_required: deprecation.migration_notes_required,
      rfc_adr_required: deprecation.rfc_adr_required,
    ),
    release_hygiene: [
      ReleaseHygieneGate(
        name: "release_checklist_attested",
        required: True,
        evidence: "docs/release_checklist_1.0.0.md",
      ),
      ReleaseHygieneGate(
        name: "compatibility_matrix_frozen",
        required: True,
        evidence: "docs/compatibility_matrix_1.0.0.md",
      ),
      ReleaseHygieneGate(
        name: "fixture_gates_green",
        required: True,
        evidence: "make ci",
      ),
    ],
    api_compatibility: [
      ApiCompatibilityGate(
        name: "public_api_signature_diff",
        expected: "no_breaking_changes_without_major",
        strict: True,
      ),
      ApiCompatibilityGate(
        name: "protocol_version_guard",
        expected: "protocol_version_monotonic",
        strict: True,
      ),
      ApiCompatibilityGate(
        name: "patch_stream_guard",
        expected: "patch_stream_contract_stable",
        strict: True,
      ),
    ],
    supply_chain: [
      SupplyChainGate(
        name: "sbom_generated",
        required: True,
        evidence: "release_artifacts/sbom.spdx.json",
      ),
      SupplyChainGate(
        name: "license_audit_pass",
        required: True,
        evidence: "reports/license_audit_latest.md",
      ),
      SupplyChainGate(
        name: "critical_vuln_gate",
        required: True,
        evidence: "reports/vuln_scan_latest.md",
      ),
      SupplyChainGate(
        name: "artifact_provenance_attested",
        required: True,
        evidence: "release_artifacts/provenance.intoto.jsonl",
      ),
    ],
    contributor_operations: contributor_workflows(),
    report_artifact: "docs/reports/maturity_fixture_latest.md#release_line_1_x",
  )
}

/// Beta `2.x-next` maturity baseline.
pub fn beta_2_x_next_baseline() -> ReleaseLineBaseline {
  ReleaseLineBaseline(
    line: "2.x-next",
    stage: Beta,
    versioning: VersioningPolicy(
      semver_required: True,
      breaking_change_requires_major: True,
      warning_releases_before_removal: 2,
      migration_notes_required: True,
      rfc_adr_required: True,
    ),
    release_hygiene: [
      ReleaseHygieneGate(
        name: "release_checklist_attested",
        required: True,
        evidence: "docs/release_checklist_1.0.0.md",
      ),
      ReleaseHygieneGate(
        name: "compatibility_matrix_updated",
        required: True,
        evidence: "docs/compatibility_matrix_1.0.0.md",
      ),
      ReleaseHygieneGate(
        name: "fixture_gates_green",
        required: True,
        evidence: "make ci",
      ),
    ],
    api_compatibility: [
      ApiCompatibilityGate(
        name: "public_api_signature_diff",
        expected: "breaking_changes_require_rfc_and_major",
        strict: True,
      ),
      ApiCompatibilityGate(
        name: "protocol_version_guard",
        expected: "protocol_version_monotonic",
        strict: True,
      ),
      ApiCompatibilityGate(
        name: "patch_stream_guard",
        expected: "patch_stream_contract_stable",
        strict: True,
      ),
    ],
    supply_chain: [
      SupplyChainGate(
        name: "sbom_generated",
        required: True,
        evidence: "release_artifacts/sbom.spdx.json",
      ),
      SupplyChainGate(
        name: "license_audit_pass",
        required: True,
        evidence: "reports/license_audit_latest.md",
      ),
      SupplyChainGate(
        name: "critical_vuln_gate",
        required: True,
        evidence: "reports/vuln_scan_latest.md",
      ),
      SupplyChainGate(
        name: "artifact_provenance_attested",
        required: True,
        evidence: "release_artifacts/provenance.intoto.jsonl",
      ),
    ],
    contributor_operations: contributor_workflows(),
    report_artifact: "docs/reports/maturity_fixture_latest.md#release_line_2_x_next",
  )
}

/// M47 release-line maturity baselines.
pub fn release_lines() -> List(ReleaseLineBaseline) {
  [ga_1_x_baseline(), beta_2_x_next_baseline()]
}

/// Contributor operations baseline.
pub fn contributor_workflows() -> List(ContributorWorkflow) {
  [
    ContributorWorkflow(
      name: "issue_triage",
      stages: [
        "intake",
        "severity_labeling",
        "owner_assignment",
        "milestone_routing",
      ],
      audit_artifact: "ops/community/issue_triage_audit.md",
    ),
    ContributorWorkflow(
      name: "review_workflow",
      stages: [
        "rfc_adr_link_check",
        "code_review",
        "fixture_gate_review",
        "approval",
      ],
      audit_artifact: "ops/community/review_audit.md",
    ),
    ContributorWorkflow(
      name: "release_workflow",
      stages: [
        "release_plan",
        "compatibility_check",
        "supply_chain_gate",
        "release_announcement",
      ],
      audit_artifact: "ops/community/release_audit.md",
    ),
  ]
}

/// Release-line accessor.
pub fn line(baseline: ReleaseLineBaseline) -> String {
  baseline.line
}

/// Contributor operations accessor.
pub fn contributor_operations(
  baseline: ReleaseLineBaseline,
) -> List(ContributorWorkflow) {
  baseline.contributor_operations
}

/// Report artifact accessor.
pub fn report_artifact(baseline: ReleaseLineBaseline) -> String {
  baseline.report_artifact
}

/// Stable maturity-stage label.
pub fn stage_label(stage: MaturityStage) -> String {
  case stage {
    Beta -> "beta"
    GA -> "ga"
  }
}

/// Stable versioning signature.
pub fn versioning_signature(policy: VersioningPolicy) -> String {
  "semver_required="
  <> bool_label(policy.semver_required)
  <> "|breaking_change_requires_major="
  <> bool_label(policy.breaking_change_requires_major)
  <> "|warning_releases_before_removal="
  <> int.to_string(policy.warning_releases_before_removal)
  <> "|migration_notes_required="
  <> bool_label(policy.migration_notes_required)
  <> "|rfc_adr_required="
  <> bool_label(policy.rfc_adr_required)
}

/// Stable release hygiene signature.
pub fn release_hygiene_signature(gates: List(ReleaseHygieneGate)) -> String {
  join_with(";", list.map(gates, release_hygiene_gate_signature))
}

/// Stable API compatibility gate signature.
pub fn api_compatibility_signature(
  gates: List(ApiCompatibilityGate),
) -> String {
  join_with(";", list.map(gates, api_gate_signature))
}

/// Stable supply-chain gate signature.
pub fn supply_chain_signature(gates: List(SupplyChainGate)) -> String {
  join_with(";", list.map(gates, supply_chain_gate_signature))
}

/// Stable contributor operations signature.
pub fn contributor_operations_signature(
  workflows: List(ContributorWorkflow),
) -> String {
  join_with(";", list.map(workflows, contributor_workflow_signature))
}

/// Stable release-line baseline signature.
pub fn signature(baseline: ReleaseLineBaseline) -> String {
  "line="
  <> baseline.line
  <> "|stage="
  <> stage_label(baseline.stage)
  <> "|versioning="
  <> versioning_signature(baseline.versioning)
  <> "|release_hygiene="
  <> release_hygiene_signature(baseline.release_hygiene)
  <> "|api_compatibility="
  <> api_compatibility_signature(baseline.api_compatibility)
  <> "|supply_chain="
  <> supply_chain_signature(baseline.supply_chain)
  <> "|contributor_operations="
  <> contributor_operations_signature(baseline.contributor_operations)
  <> "|artifact="
  <> baseline.report_artifact
}

/// Release-line artifact signatures for M47 reports.
pub fn release_line_artifact_signatures() -> List(String) {
  list.map(release_lines(), fn(item) {
    item.line <> ":" <> item.report_artifact
  })
}

/// Validate one release-line maturity baseline.
pub fn valid(baseline: ReleaseLineBaseline) -> Bool {
  baseline.line != ""
  && baseline.report_artifact != ""
  && versioning_policy_valid(baseline.versioning)
  && release_hygiene_valid(baseline.release_hygiene)
  && api_gates_valid(baseline.api_compatibility)
  && supply_chain_valid(baseline.supply_chain)
  && contributor_operations_valid(baseline.contributor_operations)
}

/// Check all release lines have explicit maturity policies.
pub fn maturity_policies_explicit() -> Bool {
  baselines_valid(release_lines()) && list.length(release_lines()) == 2
}

/// Check API breakage gates are strict and explicit.
pub fn api_breakage_gates_enforced() -> Bool {
  all_api_gates_strict(release_lines())
}

/// Check supply-chain hygiene gates are required across release lines.
pub fn supply_chain_hygiene_enforced() -> Bool {
  all_supply_chain_required(release_lines())
}

/// Check contributor operations are standardized.
pub fn contributor_operations_standardized() -> Bool {
  all_workflows_standardized(release_lines())
  && workflows_have_audit_artifacts(release_lines())
}

/// Deterministic M47 snapshot signature.
pub fn snapshot_signature() -> String {
  let line_signatures = list.map(release_lines(), signature)
  let artifacts = release_line_artifact_signatures()

  "m47.maturity.v"
  <> int.to_string(snapshot_version)
  <> "|lines="
  <> join_with(";", line_signatures)
  <> "|artifacts="
  <> join_with(";", artifacts)
}

fn versioning_policy_valid(policy: VersioningPolicy) -> Bool {
  policy.semver_required
  && policy.breaking_change_requires_major
  && policy.warning_releases_before_removal >= 2
  && policy.migration_notes_required
  && policy.rfc_adr_required
}

fn release_hygiene_valid(gates: List(ReleaseHygieneGate)) -> Bool {
  case gates {
    [] -> False
    [gate, ..rest] ->
      release_gate_valid(gate) && release_hygiene_valid_continue(rest)
  }
}

fn release_hygiene_valid_continue(gates: List(ReleaseHygieneGate)) -> Bool {
  case gates {
    [] -> True
    [gate, ..rest] ->
      release_gate_valid(gate) && release_hygiene_valid_continue(rest)
  }
}

fn release_gate_valid(gate: ReleaseHygieneGate) -> Bool {
  gate.name != "" && gate.required && gate.evidence != ""
}

fn api_gates_valid(gates: List(ApiCompatibilityGate)) -> Bool {
  case gates {
    [] -> False
    [gate, ..rest] -> api_gate_valid(gate) && api_gates_valid_continue(rest)
  }
}

fn api_gates_valid_continue(gates: List(ApiCompatibilityGate)) -> Bool {
  case gates {
    [] -> True
    [gate, ..rest] -> api_gate_valid(gate) && api_gates_valid_continue(rest)
  }
}

fn api_gate_valid(gate: ApiCompatibilityGate) -> Bool {
  gate.name != "" && gate.expected != "" && gate.strict
}

fn supply_chain_valid(gates: List(SupplyChainGate)) -> Bool {
  case gates {
    [] -> False
    [gate, ..rest] ->
      supply_chain_gate_valid(gate) && supply_chain_valid_continue(rest)
  }
}

fn supply_chain_valid_continue(gates: List(SupplyChainGate)) -> Bool {
  case gates {
    [] -> True
    [gate, ..rest] ->
      supply_chain_gate_valid(gate) && supply_chain_valid_continue(rest)
  }
}

fn supply_chain_gate_valid(gate: SupplyChainGate) -> Bool {
  gate.name != "" && gate.required && gate.evidence != ""
}

fn contributor_operations_valid(workflows: List(ContributorWorkflow)) -> Bool {
  case workflows {
    [] -> False
    [workflow, ..rest] ->
      contributor_workflow_valid(workflow)
      && contributor_operations_valid_continue(rest)
  }
}

fn contributor_operations_valid_continue(
  workflows: List(ContributorWorkflow),
) -> Bool {
  case workflows {
    [] -> True
    [workflow, ..rest] ->
      contributor_workflow_valid(workflow)
      && contributor_operations_valid_continue(rest)
  }
}

fn contributor_workflow_valid(workflow: ContributorWorkflow) -> Bool {
  workflow.name != "" && workflow.audit_artifact != "" && workflow.stages != []
}

fn baselines_valid(values: List(ReleaseLineBaseline)) -> Bool {
  case values {
    [] -> True
    [value, ..rest] -> valid(value) && baselines_valid(rest)
  }
}

fn all_api_gates_strict(values: List(ReleaseLineBaseline)) -> Bool {
  case values {
    [] -> True
    [value, ..rest] ->
      api_gates_all_strict(value.api_compatibility)
      && all_api_gates_strict(rest)
  }
}

fn api_gates_all_strict(values: List(ApiCompatibilityGate)) -> Bool {
  case values {
    [] -> True
    [value, ..rest] -> value.strict && api_gates_all_strict(rest)
  }
}

fn all_supply_chain_required(values: List(ReleaseLineBaseline)) -> Bool {
  case values {
    [] -> True
    [value, ..rest] ->
      supply_chain_all_required(value.supply_chain)
      && all_supply_chain_required(rest)
  }
}

fn supply_chain_all_required(values: List(SupplyChainGate)) -> Bool {
  case values {
    [] -> True
    [value, ..rest] -> value.required && supply_chain_all_required(rest)
  }
}

fn all_workflows_standardized(values: List(ReleaseLineBaseline)) -> Bool {
  case values {
    [] -> True
    [value, ..rest] ->
      workflow_names_match(value.contributor_operations)
      && all_workflows_standardized(rest)
  }
}

fn workflow_names_match(workflows: List(ContributorWorkflow)) -> Bool {
  let names = list.map(workflows, fn(workflow) { workflow.name })
  names == ["issue_triage", "review_workflow", "release_workflow"]
}

fn workflows_have_audit_artifacts(values: List(ReleaseLineBaseline)) -> Bool {
  case values {
    [] -> True
    [value, ..rest] ->
      all_workflow_audits_present(value.contributor_operations)
      && workflows_have_audit_artifacts(rest)
  }
}

fn all_workflow_audits_present(workflows: List(ContributorWorkflow)) -> Bool {
  case workflows {
    [] -> True
    [workflow, ..rest] ->
      string.starts_with(workflow.audit_artifact, "ops/community/")
      && all_workflow_audits_present(rest)
  }
}

fn release_hygiene_gate_signature(gate: ReleaseHygieneGate) -> String {
  gate.name
  <> ":required="
  <> bool_label(gate.required)
  <> ":evidence="
  <> gate.evidence
}

fn api_gate_signature(gate: ApiCompatibilityGate) -> String {
  gate.name
  <> ":expected="
  <> gate.expected
  <> ":strict="
  <> bool_label(gate.strict)
}

fn supply_chain_gate_signature(gate: SupplyChainGate) -> String {
  gate.name
  <> ":required="
  <> bool_label(gate.required)
  <> ":evidence="
  <> gate.evidence
}

fn contributor_workflow_signature(workflow: ContributorWorkflow) -> String {
  workflow.name
  <> ":stages="
  <> join_with(",", workflow.stages)
  <> ":audit="
  <> workflow.audit_artifact
}

fn bool_label(value: Bool) -> String {
  case value {
    True -> "true"
    False -> "false"
  }
}

fn join_with(separator: String, values: List(String)) -> String {
  case values {
    [] -> ""
    [value] -> value
    [value, ..rest] -> value <> separator <> join_with(separator, rest)
  }
}