///|
/// 一致性套件。每个套件对应 CLDR 官方测试数据中的一类(见 `DATA-SOURCES.md`)。
pub(all) enum Suite {
  Decimal
  Datetime
  MessageFormat2
} derive(Debug, Eq)

///|
/// 数字格式化样式,对应 `decimals.tsv` 的 `number_format` 列。
pub(all) enum NumberStyle {
  DecimalStyle
  PercentStyle
  ScientificStyle
  CurrencyStyle
} derive(Debug, Eq)

///|
/// 紧凑格式长度,对应 `decimals.tsv` 的 `format_length` 列(空值记为 `Standard`)。
pub(all) enum NumberLength {
  Standard
  Short
  Long
} derive(Debug, Eq)

///|
/// 失败归因分类。
///
/// 这是本项目相对「简单字符串 diff」的核心增量:报告要给出可行动的结论,
/// 而不是只丢一句 expected != actual(见 `DESIGN.md`)。
pub(all) enum FailReason {
  GroupingMismatch
  DigitSystemMismatch
  RoundingMismatch
  PluralCategoryMismatch
  BidiControlMissing
  CompactFormMismatch
  PatternSyntaxUnsupported
  /// **数值本身**不同(不是格式层面):例如百分比计算溢出、符号反了、量级错。
  /// 与"格式差异"分开,是因为修复方向完全不同——这是**算错了**,不是**排版错了**。
  ValueMismatch
  Other
} derive(Debug, Eq)

///|
/// 单条用例的执行结果。
///
/// `Unsupported` 与 `Fail` 严格区分:被测实现没有该能力不算失败,
/// 单独统计,避免用缺失能力拉低一致率。
pub(all) enum CaseOutcome {
  Pass
  Fail(reason~ : FailReason, expected~ : String, actual~ : String)
  /// 被测实现抛出异常。与 `Fail` 分开记录:**抛异常与答错是两类不同的缺陷**。
  Errored(detail~ : String)
  Unsupported(feature~ : String)
  NotInstalled
} derive(Debug, Eq)

///|
/// 适配器可声明的能力。
///
/// 粒度按"**一个库可能只做其中一部分**"来切:
/// - 日期骨架查询与日期格式化分开声明;
/// - 数字套件内部再分 `Decimal`(普通/百分比)、`DecimalScientific`、`DecimalCompact`。
///
/// 这样"没有这个能力"会记为 `unsupported`(不进一致率分母),
/// 而不是被算成"做错了"——见 `可比性映射.md` 的实测依据
/// (例如 `zlhahaha/moonlocale` 没有 scientific 与 compact 数字 API)。
pub(all) enum AdapterCapability {
  Decimal
  DecimalScientific
  DecimalCompact
  DatetimeSkeleton
  DatetimeFormat
  MessageFormat2
} derive(Debug, Eq)

///|
/// 所有可声明的能力,顺序稳定。
pub fn AdapterCapability::all() -> Array[AdapterCapability] {
  [
    Decimal,
    DecimalScientific,
    DecimalCompact,
    DatetimeSkeleton,
    DatetimeFormat,
    MessageFormat2,
  ]
}

///|
/// 能力在 CLI 和报告中的稳定标识。
pub fn AdapterCapability::label(self : AdapterCapability) -> String {
  match self {
    Decimal => "decimal"
    DecimalScientific => "decimal-scientific"
    DecimalCompact => "decimal-compact"
    DatetimeSkeleton => "datetime-skeleton"
    DatetimeFormat => "datetime-format"
    MessageFormat2 => "message-format-2"
  }
}

///|
/// 适配器声明的一组能力。构造时按首次出现顺序去重。
pub struct AdapterCapabilities {
  capabilities : Array[AdapterCapability]
} derive(Debug, Eq)

///|
pub fn AdapterCapabilities::new(
  capabilities : Array[AdapterCapability],
) -> AdapterCapabilities {
  let unique : Array[AdapterCapability] = []
  for capability in capabilities {
    if !unique.contains(capability) {
      unique.push(capability)
    }
  }
  { capabilities: unique, }
}

///|
pub fn AdapterCapabilities::all() -> AdapterCapabilities {
  AdapterCapabilities::new(AdapterCapability::all())
}

///|
pub fn AdapterCapabilities::contains(
  self : AdapterCapabilities,
  capability : AdapterCapability,
) -> Bool {
  self.capabilities.contains(capability)
}

///|
/// 日期格式化的输入参数,不包含 CLDR 的期望结果和来源行号。
pub(all) struct DatetimeFormatInput {
  locale : String
  calendar : String
  input : String
  semantic_skeleton : String?
  classical_skeleton : String?
  semantic_skeleton_length : String?
  zone_style : String?
  time_length : String?
  date_length : String?
  hour_cycle : String?
  year_style : String?
  date_time_format_type : String?
} derive(Debug, Eq)

///|
/// MF2 格式化的输入参数,不包含 CLDR 的期望输出或期望错误。
pub(all) struct MessageFormat2Input {
  locale : String
  source : String
  bidi_isolation : String?
  tags : Array[String]
  params_json : String?
} derive(Debug, Eq)

///|
/// MF2 适配器的实际结果。错误按 CLDR 测试数据中的类别返回。
pub(all) enum MessageFormat2Result {
  Formatted(String)
  Errors(Array[String])
} derive(Debug, Eq)

///|
/// 数字格式化适配器。
///
/// `value` 用 `String` 而非数值类型传入:生态内各 i18n 包的数值类型并不统一,
/// 用字符串可以避免浮点转换引入额外舍入误差(见 `DESIGN.md` D2)。
pub(open) trait NumberAdapter {
  fn name(Self) -> String
  fn format_number(
    Self,
    locale : String,
    value : String,
    style~ : NumberStyle,
    length~ : NumberLength,
  ) -> String raise
}

///|
/// 日期格式化适配器。
pub(open) trait DatetimeAdapter {
  fn name(Self) -> String
  fn format_skeleton(
    Self,
    locale : String,
    calendar : String,
    skeleton : String,
  ) -> String raise
  fn format_datetime(Self, input : DatetimeFormatInput) -> String raise
}

///|
/// MessageFormat 2 适配器。
pub(open) trait MessageFormat2Adapter {
  fn name(Self) -> String
  fn format_message(Self, input : MessageFormat2Input) -> MessageFormat2Result raise
}

///|
/// 全部套件,顺序即报告中的展示顺序。
pub fn Suite::all() -> Array[Suite] {
  [Decimal, Datetime, MessageFormat2]
}

///|
/// 套件在 CLI 与报告中的稳定标识(不要随展示文案改动)。
pub fn Suite::label(self : Suite) -> String {
  match self {
    Decimal => "decimal"
    Datetime => "datetime"
    MessageFormat2 => "message-format-2"
  }
}

///|
/// 结果的稳定标签(报告与 CLI 共用)。
pub fn CaseOutcome::label(self : CaseOutcome) -> String {
  match self {
    Pass => "pass"
    Fail(_) => "fail"
    Errored(_) => "errored"
    Unsupported(_) => "unsupported"
    NotInstalled => "not-installed"
  }
}

///|
/// 是否属于**真正的一致性失败**(答错或抛异常)。
/// `unsupported` 与 `not-installed` 不算失败——没有能力不等于做错了(见 `DESIGN.md` D3)。
pub fn CaseOutcome::is_failure(self : CaseOutcome) -> Bool {
  match self {
    Fail(_) => true
    Errored(_) => true
    _ => false
  }
}

///|
/// 失败原因的稳定标识;不是 `Fail` 时返回 `None`。
pub fn CaseOutcome::reason_label(self : CaseOutcome) -> String? {
  match self {
    Fail(reason~, ..) => Some(reason.label())
    _ => None
  }
}

///|
/// 单条用例在某个适配器上的执行结果,是报告的最小单元。
pub(all) struct CaseResult {
  suite : Suite
  /// 适配器的稳定 ID(不是展示名)。
  adapter_id : String
  /// 数据来源文件,例如 `decimals.tsv`、`syntax.json`。
  source : String
  /// 在来源中的位置,例如 `L87`、`#12`。
  location : String
  outcome : CaseOutcome
  /// 单用例耗时(毫秒)。
  elapsed_ms : Int
} derive(Debug, Eq)

///|
/// 一次运行的计数汇总。
pub(all) struct RunSummary {
  total : Int
  passed : Int
  failed : Int
  unsupported : Int
  not_installed : Int
  errored : Int
} derive(Debug, Eq)

///|
pub fn RunSummary::empty() -> RunSummary {
  {
    total: 0,
    passed: 0,
    failed: 0,
    unsupported: 0,
    not_installed: 0,
    errored: 0,
  }
}

///|
/// 统计一批结果。
pub fn RunSummary::from_results(results : Array[CaseResult]) -> RunSummary {
  let mut total = 0
  let mut passed = 0
  let mut failed = 0
  let mut unsupported = 0
  let mut not_installed = 0
  let mut errored = 0
  for result in results {
    total += 1
    match result.outcome {
      Pass => passed += 1
      Fail(_) => failed += 1
      Errored(_) => errored += 1
      Unsupported(_) => unsupported += 1
      NotInstalled => not_installed += 1
    }
  }
  { total, passed, failed, unsupported, not_installed, errored, }
}

///|
/// 真正的一致性失败数 = 答错 + 抛异常。
pub fn RunSummary::failures(self : RunSummary) -> Int {
  self.failed + self.errored
}

///|
/// 是否没有任何一致性失败。
pub fn RunSummary::is_clean(self : RunSummary) -> Bool {
  self.failures() == 0
}

///|
/// 不参与一致性统计的用例数(无能力 + 未选入)。
pub fn RunSummary::excluded(self : RunSummary) -> Int {
  self.unsupported + self.not_installed
}

///|
/// 一致率 = pass /(总数 − unsupported − not-installed)。
///
/// 分母排除"没这个能力"与"未安装":它们不是失败,也不该把一致率算低
/// (见 `DESIGN.md` D3)。评审看的就是这个数字。
pub fn RunSummary::pass_rate(self : RunSummary) -> Double {
  let judged = self.total - self.unsupported - self.not_installed
  if judged <= 0 {
    0.0
  } else {
    self.passed.to_double() / judged.to_double()
  }
}

///|
/// 一致率的展示形式(截断到整数百分比)。
pub fn RunSummary::pass_rate_text(self : RunSummary) -> String {
  "\{(self.pass_rate() * 100.0).to_int()}%"
}

///|
/// 一行文本,供 CLI 与日志展示。
pub fn RunSummary::render(self : RunSummary) -> String {
  "共 \{self.total} · pass \{self.passed} · fail \{self.failed} · errored \{self.errored} · unsupported \{self.unsupported} · not-installed \{self.not_installed}"
}

///|
/// 日期用例 → 适配器输入。**不含期望值与来源信息**,适配器在类型上就拿不到答案
/// (见 `DESIGN.md` D1「只做裁判,不做选手」)。
pub fn DatetimeCase::format_input(self : DatetimeCase) -> DatetimeFormatInput {
  {
    locale: self.locale,
    calendar: self.calendar,
    input: self.input,
    semantic_skeleton: self.semantic_skeleton,
    classical_skeleton: self.classical_skeleton,
    semantic_skeleton_length: self.semantic_skeleton_length,
    zone_style: self.zone_style,
    time_length: self.time_length,
    date_length: self.date_length,
    hour_cycle: self.hour_cycle,
    year_style: self.year_style,
    date_time_format_type: self.date_time_format_type,
  }
}

///|
/// MF2 用例 → 适配器输入。**不含期望输出与期望错误**。
pub fn MessageCase::format_input(self : MessageCase) -> MessageFormat2Input {
  {
    locale: self.locale,
    source: self.src,
    bidi_isolation: self.bidi_isolation,
    tags: self.tags,
    params_json: self.params_json,
  }
}