"""准确率指标：CER（字级）/ WER（词级）/ MER（中英混合）。

分词策略：
  - CER : 去空格后逐字符（中文每字、英文每字母各一 token）。
  - WER : 按空白切词（主要对英文有意义）。
  - MER : 中文按【字】、英文/数字按【词】，混合 token 序列算编辑距离——这是中英混说的 headline 指标。

聚合：错误率必须在语料级聚合（累加 S/D/I/N 后再求率），不能对每条样本的率求平均，
否则短句权重被放大、结果失真。
"""

from __future__ import annotations

import re
from dataclasses import dataclass

import jiwer

from .text_norm import normalize

_CJK = re.compile(r"[一-鿿㐀-䶿]")


@dataclass
class Counts:
    """一组编辑距离统计。N = 参考长度 = S + D + H。"""

    S: int = 0  # 替换
    D: int = 0  # 删除
    I: int = 0  # 插入
    N: int = 0  # 参考 token 数

    def __add__(self, o: "Counts") -> "Counts":
        return Counts(self.S + o.S, self.D + o.D, self.I + o.I, self.N + o.N)

    @property
    def rate(self) -> float:
        if self.N == 0:
            return 1.0 if (self.S + self.D + self.I) > 0 else 0.0
        return (self.S + self.D + self.I) / self.N


def char_tokens(text: str) -> list[str]:
    return [c for c in text if not c.isspace()]


def word_tokens(text: str) -> list[str]:
    return text.split()


def mixed_tokens(text: str) -> list[str]:
    """中文按字、其余按词。"""
    tokens: list[str] = []
    buf = ""
    for ch in text:
        if _CJK.match(ch):
            if buf.strip():
                tokens.extend(buf.split())
            buf = ""
            tokens.append(ch)
        else:
            buf += ch
    if buf.strip():
        tokens.extend(buf.split())
    return tokens


def _counts(ref_tokens: list[str], hyp_tokens: list[str]) -> Counts:
    if not ref_tokens:
        return Counts(S=0, D=0, I=len(hyp_tokens), N=0)
    out = jiwer.process_words(" ".join(ref_tokens), " ".join(hyp_tokens))
    return Counts(
        S=out.substitutions,
        D=out.deletions,
        I=out.insertions,
        N=out.substitutions + out.deletions + out.hits,
    )


def score_sample(ref_text: str, hyp_text: str) -> dict[str, Counts]:
    """对一条样本算 CER/WER/MER 的原始计数（供语料级聚合）。"""
    ref = normalize(ref_text)
    hyp = normalize(hyp_text)
    return {
        "cer": _counts(char_tokens(ref), char_tokens(hyp)),
        "wer": _counts(word_tokens(ref), word_tokens(hyp)),
        "mer": _counts(mixed_tokens(ref), mixed_tokens(hyp)),
    }


def empty_counts() -> dict[str, Counts]:
    return {"cer": Counts(), "wer": Counts(), "mer": Counts()}


def add_counts(acc: dict[str, Counts], one: dict[str, Counts]) -> dict[str, Counts]:
    for k in acc:
        acc[k] = acc[k] + one[k]
    return acc
