"""
T-04 · 合同规则引擎单元测试(投稿整改补强,SUBMISSION_HARDENING P1-01)

覆盖:
    ✓ 后端规则总数固定为 22(5 类:5+4+5+4+4)
    ✓ CONTRACT_TYPES 6 种合同类型映射
    ✓ 高风险文本检测
    ✓ 无风险文本 → 空结果
    ✓ 空文本 / 超长文本边界(不崩)

运行:
    python -m pytest tests/test_contract_rules.py -v
"""
from __future__ import annotations

import os
import sys
import unittest

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

from core.contract_rules import (
    ALL_RULES, CONTRACT_TYPES, analyze_contract, scan_contract_risks,
)


class TestContractRulesCount(unittest.TestCase):
    """规则数量与分类锁死(FORBIDDEN_CHANGES §13)。"""

    def test_total_rules_is_22(self):
        """后端规则总数固定 22 条。"""
        total = sum(len(rules) for _, rules in ALL_RULES)
        self.assertEqual(total, 22, f"期望 22 条,实际 {total}")

    def test_categories_is_5(self):
        """5 类风险。"""
        self.assertEqual(len(ALL_RULES), 5)

    def test_category_distribution(self):
        """5+4+5+4+4 分布(收入确认 5 / 售后回购 4 / 关联方 5 / 担保 4 / 租赁 4)。"""
        dist = {name: len(rules) for name, rules in ALL_RULES}
        self.assertEqual(dist, {
            "收入确认风险": 5,
            "售后回购/退货风险": 4,
            "关联方/资金占用风险": 5,
            "担保/或有负债风险": 4,
            "租赁识别风险": 4,
        })

    def test_contract_types_six(self):
        """6 种合同类型映射不可删(sales/purchase/lease/guarantee/related_party/mixed)。"""
        expected = {"sales", "purchase", "lease", "guarantee", "related_party", "mixed"}
        self.assertEqual(set(CONTRACT_TYPES.keys()), expected)


class TestContractRiskDetection(unittest.TestCase):
    """风险文本检测。"""

    def test_high_risk_text_detected(self):
        """含关联方资金占用 + 担保的高风险文本应被识别。"""
        text = (
            "甲方将向关联方提供 5000 万元借款,并由母公司提供连带责任担保。"
            "若关联方未按期偿还,乙方有权处置抵押物。本合同含售后回购条款。"
        )
        risks = scan_contract_risks(text)
        self.assertGreater(len(risks), 0, "高风险文本未检出任何风险")

    def test_no_risk_text_empty(self):
        """无风险文本 → 空结果。"""
        text = "本合同为普通商品买卖,甲方按约定时间交付货物,乙方按约定价格付款。"
        risks = scan_contract_risks(text)
        # 普通买卖不应触发高风险(可能触发低风险收入确认,但不应有高风险)
        high_risks = [r for r in risks if getattr(r, "level", "") == "高风险"]
        self.assertEqual(len(high_risks), 0, f"无风险文本检出高风险: {high_risks}")

    def test_empty_text_no_crash(self):
        """空文本不崩。"""
        risks = scan_contract_risks("")
        self.assertEqual(len(risks), 0)

    def test_long_text_no_crash(self):
        """超长文本(10000 字)不崩。"""
        text = "关联方资金占用。" * 2000
        risks = scan_contract_risks(text)
        self.assertIsNotNone(risks)

    def test_analyze_contract_returns_result(self):
        """analyze_contract 返回完整结构(risks + overall_level + contract_type)。"""
        text = "甲方为关联方提供担保,含售后回购条款。"
        result = analyze_contract(text)
        self.assertIsNotNone(result)
        # dataclass 转 dict
        from dataclasses import asdict
        d = asdict(result)
        self.assertIn("risks", d)
        self.assertIn("overall_level", d)
        self.assertIn("contract_type", d)


if __name__ == "__main__":
    unittest.main(verbosity=2)