"""core/evolution 子包的数据类型定义。

自进化循环中 gate、diagnose、evolve、validate 共用的 dataclass。
所有输出类型默认 frozen=True（一次性构造、不可变），唯一例外是
EvolutionRecord（构建过程中需要多次修改状态）。

不依赖 app/ 或 adapters/。
"""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any

# =========================================================================
# 1. Gate 决策类型
# =========================================================================


@dataclass(frozen=True)
class GateParams:
    """CE-Gate 判据阈值组（从实验配置构造）。

    属性:
        e_confirm: CONFIRMED 接受的 e 值门槛（1/alpha，20 对应 alpha=5%）。
        e_provisional: 题尽暂定接受门槛，同时是 futility 出口的代数界。
        w_net_min: 题尽暂定接受要求的最小净胜 W-L。
        delta_min: 接受要求的最小点估计效应量 (W-L)/n_used。
        lambda_dir: Wald 方向游走的拒绝阈值（负数）。
        e_rollback: 试用期结算的对称回滚 e 值门槛（1/alpha'，10 对应 10%）。
    """

    e_confirm: float
    e_provisional: float
    w_net_min: int
    delta_min: float
    lambda_dir: float
    e_rollback: float


@dataclass(frozen=True)
class GateVerdict:
    """一次块间判定的完整结果（判定 + 全部诊断量）。

    属性:
        decision: 判定结果，取值为 continue / accept_confirmed /
            reject_directional / reject_futility / accept_provisional /
            reject_inertia 之一。
        e_value: 当前 e 值。
        wald_lambda: 当前 Wald 方向游走值。
        delta_hat: 点估计效应量 (W-L)/n_used；n_used=0 时为 0。
        delta_shrunk: 收缩点估计 (W-L)/(n_used+4)，仅观测用。
    """

    decision: str
    e_value: float
    wald_lambda: float
    delta_hat: float
    delta_shrunk: float


# =========================================================================
# 2. 诊断类型
# =========================================================================


@dataclass(frozen=True)
class SpanMetrics:
    """单次工具调用的输出质量指标。

    属性:
        step: 工具调用所在的步骤编号。
        tool_name: 本次调用使用的工具名称。
        extraction_completeness: 信息提取完整度。
        hallucination_rate: 幻觉内容占比。
        missed_info_tags: 未提取信息的标签列表。
        hallucination_tags: 幻觉内容的标签列表。
    """

    step: int
    tool_name: str
    extraction_completeness: float
    hallucination_rate: float
    missed_info_tags: list[str] = field(default_factory=list)
    hallucination_tags: list[str] = field(default_factory=list)


@dataclass(frozen=True)
class SkillStepAdherence:
    """单个 skill step 的遵循判定。

    属性:
        step_label: 被判定的步骤标签。
        adhered: 该步骤是否被遵循。
        description: 对遵循情况的文字说明。
    """

    step_label: str
    adhered: bool
    description: str


@dataclass(frozen=True)
class QuestionMetrics:
    """单题的完整指标，即 Stage 1 输出。

    包含 7 个规则指标和 5 类 judge 指标（span / missed / adherence /
    bias / sufficiency）。frozen=True 保证构造后不可变。

    属性:
        question_id: 题目唯一标识。
        video_id: 对应视频唯一标识。
        task_type: 题目任务类型。
        correct: 该题最终是否答对。
        format_compliance: 输出格式遵循程度。
        budget_usage: 预算使用比例。
        confidence_calibration: 置信度校准结论。
        repeat_visit_rate: 重复访问节点的比例。
        search_keyword_repetition: 搜索关键词重复率。
        level_jump_pattern: 层级跳转模式描述。
        tool_usage: 各工具的调用次数统计。
        span_metrics: 该题全部工具调用的片段级质量指标。
        missed_nodes: 该题遗漏的节点列表。
        skill_adherence: 该题对 skill 步骤的遵循情况。
        confirmation_bias: 是否出现确认偏误。None 表示 judge 不可用。
        evidence_sufficient: 当前证据是否充足。None 表示 judge 不可用。
        degraded: 是否为降级指标（judge 解析失败时生成）。
    """

    question_id: str
    video_id: str
    task_type: str
    correct: bool
    format_compliance: float
    budget_usage: float
    confidence_calibration: str
    repeat_visit_rate: float
    search_keyword_repetition: float
    level_jump_pattern: str
    tool_usage: dict[str, int]
    span_metrics: list[SpanMetrics]
    missed_nodes: list[str]
    skill_adherence: list[SkillStepAdherence]
    confirmation_bias: bool | None
    evidence_sufficient: bool | None
    degraded: bool = False


@dataclass(frozen=True)
class ErrorAttribution:
    """D1 错误归因。

    属性:
        question_id: 发生错误归因的题目唯一标识。
        error_type: 错误的主要类别。
        reasoning_failure_type: 推理失败类型；若不适用则为 None。
        cause_category: C3 病因：'defect'/'lapse'；正确题/INFRA/未判为 None。
        lapse_note: LAPSE 提醒文本（供 appendix 路由）；非 LAPSE 为 None。
    """

    question_id: str
    error_type: str
    reasoning_failure_type: str | None
    cause_category: str | None = None
    lapse_note: str | None = None


@dataclass(frozen=True)
class CaseSample:
    """单个案例样本，进化模块的最小输入单元。

    属性:
        question_id: 题目唯一标识。
        video_id: 对应视频唯一标识。
        task_type: 题目任务类型。
        question: 题目文本。
        options: 选项列表。
        answer: 正确答案。
        prediction: Agent 预测答案。
        correct: 是否答对。
        error_type: 错误类型；正确题为 None。
        selection_reason: 被选为案例的原因说明。
        metrics: QuestionMetrics 的关键字段子集。
        trace: 完整推理轨迹，不截断。
    """

    question_id: str
    video_id: str
    task_type: str
    question: str
    options: list[str]
    answer: str
    prediction: str | None
    correct: bool
    error_type: str | None
    selection_reason: str
    metrics: dict[str, Any]
    trace: list[dict[str, Any]]


@dataclass(frozen=True)
class SkillCasePack:
    """单个 task_type 的案例包，服务于 Skill 进化。

    属性:
        task_type: 题目任务类型。
        target_file: 对应 skill 文件名，如 'temporal-reasoning.md'。
        stats: 从 D3/D4 提取的该题型统计。
        failure_cases: 失败案例列表。
        success_cases: 成功案例列表。
        lapse_notes: C3 LAPSE 提醒文本列表（路由进 appendix 受保护区）。
    """

    task_type: str
    target_file: str
    stats: dict[str, Any]
    failure_cases: list[CaseSample] = field(default_factory=list)
    success_cases: list[CaseSample] = field(default_factory=list)
    lapse_notes: list[str] = field(default_factory=list)


@dataclass(frozen=True)
class SystemCasePack:
    """跨题型行为模式案例包，服务于 System Prompt 进化。

    属性:
        stats: 从 D5 提取的行为模式统计。
        failure_cases: 失败案例列表。
        success_cases: 成功案例列表。
    """

    stats: dict[str, Any]
    failure_cases: list[CaseSample] = field(default_factory=list)
    success_cases: list[CaseSample] = field(default_factory=list)


@dataclass(frozen=True)
class ToolCasePack:
    """单个 tool_name 的案例包，服务于 Tool Prompt 进化。

    属性:
        tool_name: 工具名称。
        target_files: 对应 prompt 文件名列表。
        stats: 从 D2 提取的工具质量统计。
        failure_spans: 失败 span 案例列表。
        success_spans: 成功 span 案例列表。
    """

    tool_name: str
    target_files: list[str]
    stats: dict[str, Any]
    failure_spans: list[dict[str, Any]] = field(default_factory=list)
    success_spans: list[dict[str, Any]] = field(default_factory=list)


@dataclass(frozen=True)
class DiagnosisResult:
    """完整诊断报告，即两阶段诊断管线的最终输出。

    属性:
        run_id: 本次诊断运行的唯一标识。
        filter_summary: 筛选条件与筛选结果摘要。
        error_attributions: 错误归因结果列表。
        attribution_distribution: 各归因类别的分布统计。
        reasoning_failure_types: 各推理失败类型的分布统计。
        tool_quality: 按工具聚合的质量分析结果。
        search_effectiveness: 搜索有效性的聚合统计。
        skill_compliance: 技能遵循情况的聚合统计。
        decision_patterns: 决策模式与行为模式摘要。
        skill_case_packs: 按题型组织的 Skill 进化案例包。
        system_case_pack: 跨题型行为模式案例包；无系统性问题时为 None。
        tool_case_packs: 按工具名组织的 Tool Prompt 进化案例包。
        infra_excluded_count: C3：被 stop_reason 排除的题数。
        infra_excluded_ratio: INFRA 占总题数比例。
        infra_question_ids: 被排除题 question_id 列表。
        defect_count: 进入诊断池错题中判为 DEFECT 的数量。
        lapse_count: 进入诊断池错题中判为 LAPSE 的数量。
        degraded_count: judge 解析失败而降级的题数。
        degraded_question_ids: 降级题的 question_id 列表。
    """

    run_id: str
    filter_summary: dict[str, Any] = field(default_factory=dict)
    error_attributions: list[ErrorAttribution] = field(default_factory=list)
    attribution_distribution: dict[str, int] = field(default_factory=dict)
    reasoning_failure_types: dict[str, int] = field(default_factory=dict)
    tool_quality: dict[str, dict[str, Any]] = field(default_factory=dict)
    search_effectiveness: dict[str, dict[str, Any]] = field(default_factory=dict)
    skill_compliance: dict[str, dict[str, Any]] = field(default_factory=dict)
    decision_patterns: dict[str, Any] = field(default_factory=dict)
    skill_case_packs: dict[str, SkillCasePack] = field(default_factory=dict)
    system_case_pack: SystemCasePack | None = None
    tool_case_packs: dict[str, ToolCasePack] = field(default_factory=dict)
    infra_excluded_count: int = 0
    infra_excluded_ratio: float = 0.0
    infra_question_ids: list[str] = field(default_factory=list)
    defect_count: int = 0
    lapse_count: int = 0
    degraded_count: int = 0
    degraded_question_ids: list[str] = field(default_factory=list)


@dataclass(frozen=True)
class DiagnosisSignalRow:
    """单题诊断信号行，即 baseline run 逐题诊断的持久化单元。

    供后续视频级切分选择器消费；由 (question_id, baseline_run_id,
    diag_fingerprint) 唯一确定，逐题 upsert 支持断点续跑。

    属性:
        question_id: 题目唯一标识。
        video_id: 对应视频唯一标识。
        baseline_run_id: 产出该信号的 baseline run 标识。
        diag_fingerprint: 诊断口径指纹，隔离不同诊断配置的信号。
        task_type: 题目任务类型。
        error_type: 错误类别（extraction/search/reasoning/mixed）；
            T0/uncertain 行为 None。
        cause_category: 病因类别（defect/lapse）；不适用为 None。
        tier: 诊断分层（T0/T1/T2/uncertain）。
        evolution_target: 进化目标（tool/skill/system）；
            error_type 为 None 时亦为 None。
        degraded: 是否为降级信号（judge 解析失败时生成）。
        infra: 是否为 INFRA 护栏排除行。
        session_id: 关联的会话标识；不适用为 None。
    """

    question_id: str
    video_id: str
    baseline_run_id: str
    diag_fingerprint: str
    task_type: str
    error_type: str | None
    cause_category: str | None
    tier: str
    evolution_target: str | None
    degraded: bool
    infra: bool
    session_id: str | None


# =========================================================================
# 3. 进化类型
# =========================================================================


@dataclass
class EvolutionRecord:
    """单个目标文件的一次进化记录。

    构建过程中需要多次修改状态（如 status、result_version），
    因此是唯一不使用 frozen=True 的类型。

    属性:
        target_file: 目标文件名，如 'temporal-reasoning.md'。
        target_type: 目标类型: 'skill' / 'system' / 'tool'。
        original_content: 改写前原文。
        evolved_content: 改写后内容；rejected 时与 original_content 相同。
        reason: 状态说明。
        status: 'accepted' / 'rejected' / 'skipped'。
        source_version: 改写前版本号，如 'v1'。
        result_version: 改写后版本号；rejected/skipped 时为 None。
        suggestions: LLM 输出的改动建议列表。
        attempts: 每次 LLM 调用的原始响应摘要。
        validation_errors: 验证失败的具体原因。
        edits: LLM 输出的补丁列表。
        apply_report: 补丁逐条应用状态。
        clip_info: 超预算裁剪信息。
    """

    target_file: str
    target_type: str
    original_content: str
    evolved_content: str
    reason: str
    status: str
    source_version: str
    result_version: str | None = None
    suggestions: list[dict[str, Any]] = field(default_factory=list)
    attempts: list[dict[str, Any]] = field(default_factory=list)
    validation_errors: list[str] = field(default_factory=list)
    edits: list[dict[str, Any]] = field(default_factory=list)
    apply_report: list[dict[str, Any]] = field(default_factory=list)
    clip_info: dict[str, Any] = field(default_factory=lambda: {"triggered": False, "clipped": 0})


@dataclass(frozen=True)
class RejectedEdit:
    """已在验证阶段证明无效的历史改法摘要。

    属性:
        target_file: 目标文件名，如 'temporal-reasoning.md'。
        target_type: 目标类型: 'skill' / 'system' / 'tool'。
        change_summary: 被验证为无效的改法摘要。
        delta: 该改法对应候选相对基线的准确率变化。
        source_version: 该改法来源的版本号，如 'v2'。
        epoch: 该改法所属的进化轮次。
        gate_w: CE-Gate 证据：配对翻转 W（基线错到候选对）。
        gate_l: CE-Gate 证据：配对翻转 L（基线对到候选错）。
        gate_e_value: CE-Gate 证据：终态 e 值。
        gate_delta_shrunk: CE-Gate 证据：收缩效应量（观测用）。
    """

    target_file: str
    target_type: str
    change_summary: str
    delta: float
    source_version: str
    epoch: int
    gate_w: int | None = None
    gate_l: int | None = None
    gate_e_value: float | None = None
    gate_delta_shrunk: float | None = None


@dataclass(frozen=True)
class EvolutionResult:
    """一次整体进化流程的汇总结果。

    由 app/harness/ 编排层组装。不含 skills_version / prompts_version
    （版本管理是 app/ 职责，不属于 core/ 决策内核）。

    属性:
        records: 所有目标的进化记录。
        accepted_count: 通过验证的改写数。
        rejected_count: 未通过验证的改写数。
        skipped_count: 因无失败案例而跳过的目标数。
    """

    records: list[EvolutionRecord] = field(default_factory=list)
    accepted_count: int = 0
    rejected_count: int = 0
    skipped_count: int = 0


# =========================================================================
# 4. 验证辅助类型
# =========================================================================


@dataclass(frozen=True)
class PairResult:
    """块验证配对比对结果。

    属性:
        w: 基线错、候选对的翻转数。
        l: 基线对、候选错的翻转数。
        observed: 每题的 (基线是否正确, 候选是否正确) 记录。
    """

    w: int
    l: int  # noqa: E741 — 数学记号 W/L（win/loss），与 gate.py 一致
    observed: dict[str, tuple[bool, bool]]


@dataclass(frozen=True)
class QuadrantClassification:
    """块验证四象限分类。

    属性:
        improvements: 基线错、候选对的题目 ID 列表。
        regressions: 基线对、候选错的题目 ID 列表。
        persistent_fails: 两臂均错的题目 ID 列表。
        stable_successes: 两臂均对的题目 ID 列表。
    """

    improvements: list[str]
    regressions: list[str]
    persistent_fails: list[str]
    stable_successes: list[str]


# =========================================================================
# 5. Prompt 模板束
# =========================================================================


@dataclass(frozen=True)
class DiagnosePrompts:
    """诊断管线所需的全部固定模板束。

    由调用方加载后以 frozen dataclass 传入，避免 core/ 依赖文件系统。

    属性:
        defect_vs_lapse: defect/lapse 病因判别模板。
        reasoning_sub: 推理失败子分类模板。
        span_eval_system: span 评估系统提示模板。
        span_eval_user: span 评估用户提示模板。
        missed_nodes: 遗漏节点检测模板。
        skill_adherence: 技能遵循判定模板。
        confirmation_bias: 确认偏误检测模板。
        evidence_sufficiency: 证据充足性判定模板。
    """

    defect_vs_lapse: str
    reasoning_sub: str
    span_eval_system: str
    span_eval_user: str
    missed_nodes: str
    skill_adherence: str
    confirmation_bias: str
    evidence_sufficiency: str


@dataclass(frozen=True)
class EvolvePrompts:
    """进化引擎所需的全部固定模板束。

    由调用方加载后以 frozen dataclass 传入，避免 core/ 依赖文件系统。

    属性:
        evolve_skill: Skill 进化提示模板。
        evolve_system: System Prompt 进化提示模板。
        evolve_tool: Tool Prompt 进化提示模板。
        evolve_rank: 编辑排序提示模板。
        consolidate_system: appendix 压缩系统提示。
    """

    evolve_skill: str
    evolve_system: str
    evolve_tool: str
    evolve_rank: str
    consolidate_system: str
