// 装配配置:计数器 + 窗口 + 输出预留 + 截断策略。
//
// 窗口模型:`window = reserve_output + input_limit`。
// 真实场景里「128k 窗口」不等于「128k 都能给输入」——要留出回复。
// 预留放不下时**显式报错**,而不是悄悄把可用空间算成负数。

///|
/// 预算装不下时对单段的处理方式。
pub(all) enum TruncateStrategy {
  /// 整段丢弃(0.3 的默认行为)。
  NoTruncate
  /// 保留段首、截掉段尾:适合 system 提示这类开头最重要的内容。
  KeepPrefix
  /// 保留段尾、截掉段首:适合历史/日志这类最近内容最重要的段。
  KeepSuffix
  /// 保留段首、把被截掉的尾部换成**确定性占位标记**,并标注省略了多少 token。
  ///
  /// 零依赖约束下这是「占位压缩」的全部含义:标记是固定文案(只带省略量),
  /// 不做语义摘要;标记本身的 token 数计入窗口。适合长日志/长工具输出——
  /// 模型能看到「这里有 N 个 token 被省略」,比整段消失更有信息量。
  Compress
} derive(Eq, Debug)

///|
/// 一个配额池:同名段共享 `limit` 个 token 的额度。
///
/// 语义要点(都写进报告,可核对):
/// * 配额只约束**可裁段**;`Pinned` 段不裁,若 Pinned 自身超过配额,
///   报告里 `used` 会如实大于 `quota`。
/// * 池的额度用不完时,可按 `pools` 数组的声明顺序借给超额的池;
///   借用/借出量逐池记账。
/// * 没有声明配额的池不参与配额(只受总预算约束)。
pub(all) struct PoolQuota {
  name : String
  limit : Int64
}

///|
pub fn PoolQuota::new(name : String, limit : Int64) -> PoolQuota {
  { name, limit }
}

///|
/// 一次装配的配置。
pub(all) struct FitConfig {
  counter : Counter
  window : Int64
  reserve_output : Int64
  strategy : TruncateStrategy
  /// 池配额,按声明顺序参与借用;空数组 = 不做池约束。
  pools : Array[PoolQuota]
}

///|
/// 默认配置:整窗可用、整段丢弃、无池配额。
pub fn FitConfig::new(counter : Counter, window : Int64) -> FitConfig {
  { counter, window, reserve_output: 0L, strategy: NoTruncate, pools: [] }
}

///|
/// 设置池配额(按数组顺序处理借用)。
pub fn FitConfig::with_pools(
  self : FitConfig,
  pools : Array[PoolQuota],
) -> FitConfig {
  { ..self, pools }
}

///|
/// 预留 `reserve_output` 个 token 给模型回复。
pub fn FitConfig::with_reserve(
  self : FitConfig,
  reserve_output : Int64,
) -> FitConfig {
  { ..self, reserve_output }
}

///|
/// 设置截断策略。
pub fn FitConfig::with_strategy(
  self : FitConfig,
  strategy : TruncateStrategy,
) -> FitConfig {
  { ..self, strategy }
}

///|
/// 输入可用预算:`window - reserve_output`。
pub fn FitConfig::input_limit(self : FitConfig) -> Int64 {
  self.window - self.reserve_output
}

///|
/// 装配错误(不是「装不下」,装不下会写进报告;这是配置本身不成立)。
pub suberror FitError {
  /// 输出预留吃掉了整个窗口:`reserve >= window`
  ReserveExceedsWindow(Int64, Int64)
}