///|
/// Named decoder policies for common deployment environments.
pub(all) enum DecodeProfile {
  Embedded
  Interactive
  Service
  Archive
} derive(Eq, Debug)

///|
/// Named deterministic encoder trade-offs.
pub(all) enum EncodeProfile {
  Fast
  Balanced
  Compact
  SmallMemory
} derive(Eq, Debug)

///|
/// The resource category exceeded by a parsed delta.
pub(all) enum LimitKind {
  OutputBytes
  WindowBytes
  WindowCount
  InstructionCount
} derive(Eq, Debug)

///|
/// One preflight limit violation tied to a file or window offset.
pub(all) struct LimitViolation {
  kind : LimitKind
  offset : Int
  window_index : Int
  actual : Int
  limit : Int
} derive(Eq, Debug)

///|
/// Returns explicit limits for a named deployment profile.
pub fn decode_limits_for_profile(profile : DecodeProfile) -> DecodeLimits {
  match profile {
    Embedded =>
      {
        max_output_size: 4 * 1024 * 1024,
        max_window_size: 512 * 1024,
        max_windows: 128,
        max_instructions_per_window: 256 * 1024,
      }
    Interactive =>
      {
        max_output_size: 64 * 1024 * 1024,
        max_window_size: 8 * 1024 * 1024,
        max_windows: 1024,
        max_instructions_per_window: 2 * 1024 * 1024,
      }
    Service => default_decode_limits()
    Archive =>
      {
        max_output_size: MAX_PORTABLE_INT,
        max_window_size: 64 * 1024 * 1024,
        max_windows: 65536,
        max_instructions_per_window: 16 * 1024 * 1024,
      }
  }
}

///|
/// Returns deterministic options for a named encoder trade-off.
pub fn encode_options_for_profile(profile : EncodeProfile) -> EncodeOptions {
  match profile {
    Fast =>
      {
        window_size: 1024 * 1024,
        minimum_match: 12,
        max_candidate_chain: 8,
        run_threshold: 5,
        enable_target_matches: false,
      }
    Balanced => default_encode_options()
    Compact =>
      {
        window_size: 1024 * 1024,
        minimum_match: 4,
        max_candidate_chain: 256,
        run_threshold: 3,
        enable_target_matches: true,
      }
    SmallMemory =>
      {
        window_size: 64 * 1024,
        minimum_match: 8,
        max_candidate_chain: 16,
        run_threshold: 4,
        enable_target_matches: true,
      }
  }
}

///|
fn push_limit_violation(
  output : Array[LimitViolation],
  kind : LimitKind,
  offset : Int,
  window_index : Int,
  actual : Int,
  limit : Int,
) -> Unit {
  output.push({ kind, offset, window_index, actual, limit })
}

///|
/// Evaluates declared structure against limits without decoding target bytes.
pub fn evaluate_limits(
  summary : DeltaSummary,
  limits : DecodeLimits,
) -> Array[LimitViolation] {
  let violations : Array[LimitViolation] = []
  if summary.target_size > limits.max_output_size {
    push_limit_violation(
      violations,
      OutputBytes,
      summary.header_size,
      -1,
      summary.target_size,
      limits.max_output_size,
    )
  }
  if summary.window_count > limits.max_windows {
    push_limit_violation(
      violations,
      WindowCount,
      summary.header_size,
      -1,
      summary.window_count,
      limits.max_windows,
    )
  }
  for window in summary.windows {
    if window.target_size > limits.max_window_size {
      push_limit_violation(
        violations,
        WindowBytes,
        window.offset,
        window.index,
        window.target_size,
        limits.max_window_size,
      )
    }
    let instructions = window.add_count + window.run_count + window.copy_count
    if instructions > limits.max_instructions_per_window {
      push_limit_violation(
        violations,
        InstructionCount,
        window.offset,
        window.index,
        instructions,
        limits.max_instructions_per_window,
      )
    }
  }
  violations
}

///|
/// Returns true when every declared structural resource is within limits.
pub fn DecodeLimits::accepts(
  self : DecodeLimits,
  summary : DeltaSummary,
) -> Bool {
  evaluate_limits(summary, self).length() == 0
}

///|
/// Returns a stable command-line spelling for a limit category.
pub fn LimitKind::name(self : LimitKind) -> String {
  match self {
    OutputBytes => "output_bytes"
    WindowBytes => "window_bytes"
    WindowCount => "window_count"
    InstructionCount => "instruction_count"
  }
}

///|
/// Renders a preflight violation in one stable line.
pub fn LimitViolation::to_text(self : LimitViolation) -> String {
  let location = if self.window_index >= 0 {
    "window \{self.window_index}"
  } else {
    "file"
  }
  "\{self.kind.name()} exceeded at \{location} byte \{self.offset}: actual \{self.actual}, limit \{self.limit}"
}