///|
/// Terminal result of RFC 9111 storage eligibility evaluation.
pub(all) enum StorageVerdict {
  Storable
  NotStorable
} derive(Debug, Eq)

///|
/// Storage decision plus field handling required before persistence.
pub(all) struct StorageDecision {
  verdict : StorageVerdict
  fields_to_remove : Array[String]
  fields_requiring_validation : Array[String]
  trace : Array[TraceStep]
  diagnostics : Array[Diagnostic]
} derive(Debug, Eq)

///|
pub fn StorageDecision::is_storable(self : StorageDecision) -> Bool {
  self.verdict == Storable
}

///|
/// Evaluate whether a response may be stored by the selected cache mode.
/// This implementation deliberately supports representation storage for GET;
/// HEAD is handled later as metadata refresh and unsafe methods are rejected.
pub fn evaluate_storage(
  request : RequestMetadata,
  response : ResponseMetadata,
  mode : CacheMode,
  now : Timestamp,
) -> StorageDecision {
  let trace : Array[TraceStep] = []
  let diagnostics = request.headers.diagnostics()
  for item in response.headers.diagnostics() {
    diagnostics.push(item)
  }
  if has_error(diagnostics) {
    return storage_reject(
      "STORAGE_INVALID_FIELDS", "message contains an unsafe or invalid HTTP field",
      "RFC 9111 3", trace, diagnostics,
    )
  }
  if request.http_method != "GET" {
    return storage_reject(
      "STORAGE_METHOD_UNSUPPORTED", "only complete GET responses are stored by this engine",
      "RFC 9111 3", trace, diagnostics,
    )
  }
  if response.status < 200 || response.status > 599 {
    return storage_reject(
      "STORAGE_STATUS_NOT_FINAL", "response status is not a final HTTP status", "RFC 9111 3",
      trace, diagnostics,
    )
  }
  if response.status == 206 || response.status == 304 {
    return storage_reject(
      "STORAGE_PARTIAL_OR_VALIDATION_RESPONSE", "partial and validation responses require a pre-existing stored response",
      "RFC 9111 3.3, 4.3.4", trace, diagnostics,
    )
  }
  let request_control = parse_cache_control(request.headers)
  let response_control = parse_cache_control(response.headers)
  append_diagnostics(diagnostics, request_control.diagnostics)
  append_diagnostics(diagnostics, response_control.diagnostics)
  if request_control.contains("no-store") {
    return storage_reject(
      "STORAGE_REQUEST_NO_STORE", "request Cache-Control contains no-store", "RFC 9111 5.2.1.5",
      trace, diagnostics,
    )
  }
  if response_control.contains("no-store") {
    return storage_reject(
      "STORAGE_RESPONSE_NO_STORE", "response Cache-Control contains no-store", "RFC 9111 5.2.2.5",
      trace, diagnostics,
    )
  }
  if response_control.contains("must-understand") &&
    !is_understood_status(response.status) {
    return storage_reject(
      "STORAGE_STATUS_NOT_UNDERSTOOD", "must-understand forbids storing an unsupported status code",
      "RFC 9111 5.2.2.3", trace, diagnostics,
    )
  }
  let private_fields = response_control.field_names("private")
  if mode == SharedCache &&
    response_control.contains("private") &&
    private_fields.length() == 0 {
    return storage_reject(
      "STORAGE_SHARED_PRIVATE", "an unqualified private directive forbids shared storage",
      "RFC 9111 5.2.2.7", trace, diagnostics,
    )
  }
  if mode == SharedCache &&
    request.headers.contains("authorization") &&
    !response_control.contains("must-revalidate") &&
    !response_control.contains("public") &&
    response_control.delta("s-maxage") is DeltaMissing {
    return storage_reject(
      "STORAGE_AUTHORIZATION_RESTRICTED", "authenticated request lacks a directive allowing shared storage",
      "RFC 9111 3.5", trace, diagnostics,
    )
  }
  let has_explicit_permission = response_control.contains("public") ||
    (mode == PrivateCache && response_control.contains("private")) ||
    response.headers.contains("expires") ||
    is_valid_delta(response_control.delta("max-age")) ||
    (mode == SharedCache && is_valid_delta(response_control.delta("s-maxage")))
  if !has_explicit_permission && !is_heuristically_cacheable(response.status) {
    return storage_reject(
      "STORAGE_NO_EXPLICIT_OR_DEFAULT_PERMISSION", "response has neither explicit cache permission nor a heuristically cacheable status",
      "RFC 9111 3", trace, diagnostics,
    )
  }
  trace.push(
    step(
      "STORAGE_ALLOWED", "response satisfies RFC storage preconditions", "RFC 9111 3",
    ),
  )
  let validation_fields = response_control.field_names("no-cache")
  if private_fields.length() > 0 && mode == SharedCache {
    trace.push(
      step(
        "STORAGE_REMOVE_PRIVATE_FIELDS", "qualified private fields must be removed before shared storage",
        "RFC 9111 5.2.2.7",
      ),
    )
  }
  if validation_fields.length() > 0 {
    trace.push(
      step(
        "STORAGE_FIELD_VALIDATION_REQUIRED", "qualified no-cache fields require validation before reuse",
        "RFC 9111 5.2.2.4",
      ),
    )
  }
  ignore(now)
  {
    verdict: Storable,
    fields_to_remove: if mode == SharedCache {
      private_fields
    } else {
      []
    },
    fields_requiring_validation: validation_fields,
    trace,
    diagnostics,
  }
}

///|
fn storage_reject(
  code : String,
  message : String,
  section : String,
  trace : Array[TraceStep],
  diagnostics : Array[Diagnostic],
) -> StorageDecision {
  trace.push(step(code, message, section))
  {
    verdict: NotStorable,
    fields_to_remove: [],
    fields_requiring_validation: [],
    trace,
    diagnostics,
  }
}

///|
fn step(code : String, message : String, rfc_section : String) -> TraceStep {
  { code, message, rfc_section, }
}

///|
fn has_error(diagnostics : Array[Diagnostic]) -> Bool {
  for item in diagnostics {
    if item.level == Error {
      return true
    }
  }
  false
}

///|
fn append_diagnostics(
  target : Array[Diagnostic],
  source : Array[Diagnostic],
) -> Unit {
  for item in source {
    target.push(item)
  }
}

///|
fn is_valid_delta(value : DeltaDirective) -> Bool {
  value is DeltaValid(_)
}

///|
fn is_understood_status(status : Int) -> Bool {
  match status {
    200
    | 201
    | 202
    | 203
    | 204
    | 205
    | 206
    | 300
    | 301
    | 302
    | 303
    | 304
    | 305
    | 307
    | 308
    | 400
    | 401
    | 402
    | 403
    | 404
    | 405
    | 406
    | 407
    | 408
    | 409
    | 410
    | 411
    | 412
    | 413
    | 414
    | 415
    | 416
    | 417
    | 421
    | 422
    | 423
    | 424
    | 425
    | 426
    | 428
    | 429
    | 431
    | 451
    | 500
    | 501
    | 502
    | 503
    | 504
    | 505
    | 506
    | 507
    | 508
    | 510
    | 511 => true
    _ => false
  }
}

///|
fn is_heuristically_cacheable(status : Int) -> Bool {
  match status {
    200 | 203 | 204 | 300 | 301 | 308 | 404 | 405 | 410 | 414 | 501 => true
    _ => false
  }
}