"""
脱敏引擎核心

LocalDesensitizationEngine: 本地脱敏引擎主入口
协调四维度处理器、模型客户端和字典管理器
"""

import re
import logging
from dataclasses import dataclass, field
from typing import Optional, Dict, Any, List
from pathlib import Path

from .processors.pii_processor import PIIDesensitizer
from .processors.geo_processor import GeoDesensitizer
from .processors.biz_processor import BizDesensitizer
from .processors.financial_processor import FinancialDesensitizer
from .dict_manager import DictManager
from .validator import BlackWhiteListChecker
from .model_client import DesensitizeModelClient
from foundation.infrastructure.config.config import config_handler

logger = logging.getLogger(__name__)


@dataclass
class DesensitizedResult:
    """脱敏处理结果"""
    content: str  # 脱敏后的文本内容
    task_id: str  # 任务ID
    dict_hash: str  # 脱敏字典哈希
    statistics: Dict[str, int] = field(default_factory=dict)  # 统计信息
    is_valid: bool = True  # 是否通过黑白名单校验
    violations: List[Dict] = field(default_factory=list)  # 违规项列表


class EntityRegistry:
    """实体注册表 - 跨维度追踪实体映射关系"""

    def __init__(self):
        self.persons: Dict[str, str] = {}  # 姓名 -> 角色占位符
        self.entities: Dict[str, str] = {}  # 实体名 -> 序列化占位符
        self.companies: Dict[str, str] = {}  # 公司名 -> 标准角色
        self.coordinates: Dict[str, Any] = {  # 坐标映射
            "base_point": None,  # 基准点（最小桩号）
            "mappings": {}  # 原始 -> 相对
        }
        self.amounts: List[str] = []  # 金额列表

        # 计数器用于生成序列号
        self._person_counter = 0
        self._entity_counter = 0

    def register_person(self, name: str, role: str = "人员") -> str:
        """注册人员，返回角色占位符"""
        if name in self.persons:
            return self.persons[name]

        self._person_counter += 1
        placeholder = f"[{role}{self._get_suffix(self._person_counter)}]"
        self.persons[name] = placeholder
        return placeholder

    def register_entity(self, name: str, entity_type: str = "实体") -> str:
        """注册工程实体（桥/隧/河等），返回序列化占位符"""
        if name in self.entities:
            return self.entities[name]

        self._entity_counter += 1
        type_map = {
            "桥": "号特大桥",
            "隧道": "号隧道",
            "河": "号河流",
            "涵洞": "号涵洞",
            "立交": "号立交"
        }
        suffix = type_map.get(entity_type, f"号{entity_type}")
        placeholder = f"[{self._entity_counter}{suffix}]"
        self.entities[name] = placeholder
        return placeholder

    def register_company(self, name: str, company_type: str = "单位") -> str:
        """注册企业/单位，返回标准角色词汇"""
        if name in self.companies:
            return self.companies[name]

        type_map = {
            "建设": "建设单位",
            "总包": "总承包单位",
            "监理": "监理单位",
            "设计": "设计单位",
            "劳务": "劳务分包单位",
            "数字化": "数字化实施方"
        }
        placeholder = f"[{type_map.get(company_type, company_type)}]"
        self.companies[name] = placeholder
        return placeholder

    def set_coordinate_base(self, base_point: str):
        """设置坐标相对化基准点"""
        self.coordinates["base_point"] = base_point
        self.coordinates["relative_zero"] = "K0+000"

    def register_coordinate(self, original: str, relative: str):
        """注册坐标映射关系"""
        self.coordinates["mappings"][original] = relative

    def export_mapping(self) -> Dict[str, Any]:
        """导出完整映射字典"""
        return {
            "version": "1.0",
            "persons": [
                {"original": k, "masked": v, "role": v[1:-1].rstrip("ABCDEFGHIJ")}
                for k, v in self.persons.items()
            ],
            "entities": [
                {"original": k, "masked": v}
                for k, v in self.entities.items()
            ],
            "companies": [
                {"original": k, "masked": v}
                for k, v in self.companies.items()
            ],
            "coordinates": self.coordinates,
            "amount_count": len(self.amounts)
        }

    def get_statistics(self) -> Dict[str, int]:
        """获取统计信息"""
        return {
            "pii_count": len(self.persons) + len(self.amounts),
            "geo_count": len(self.entities) + len(self.coordinates.get("mappings", {})),
            "biz_count": len(self.companies),
            "financial_count": len(self.amounts)
        }

    @staticmethod
    def _get_suffix(n: int) -> str:
        """获取序号后缀 A, B, C..."""
        return chr(ord('A') + n - 1) if n <= 26 else f"{n}"


class LocalDesensitizationEngine:
    """本地脱敏引擎主入口

    支持两阶段配置：
    - DEPLOY_PHASE=test: 使用外部API测试（仅测试数据集）
    - DEPLOY_PHASE=local: 使用本地模型（生产环境）

    支持两方案切换：
    - scheme_a: 规则引擎主导 + 模型语义兜底
    - scheme_b: 模型全量语义驱动 + 规则后校验
    """

    def __init__(self):
        self.cfg = config_handler.get_section("desensitize")
        self.deploy_phase = self.cfg.get("DEPLOY_PHASE", "test")
        self.scheme = self.cfg.get("DESENSITIZE_SCHEME", "scheme_a")
        self.level = self.cfg.get("DEFAULT_LEVEL", "standard")

        # 初始化处理器
        self.pii_processor = PIIDesensitizer()
        self.geo_processor = GeoDesensitizer()
        self.biz_processor = BizDesensitizer()
        self.financial_processor = FinancialDesensitizer()

        # 初始化模型客户端（可选，scheme_b或scheme_a补充阶段使用）
        self.model_client: Optional[DesensitizeModelClient] = None
        if self._need_model():
            self.model_client = DesensitizeModelClient()

        # 初始化校验器和管理器
        self.validator = BlackWhiteListChecker()
        self.dict_manager = DictManager()

        logger.info(f"[DesensitizeEngine] Initialized: phase={self.deploy_phase}, "
                   f"scheme={self.scheme}, level={self.level}")

    def _need_model(self) -> bool:
        """判断当前配置是否需要调用大模型"""
        if self.deploy_phase == "test":
            return True  # 测试阶段必须调用模型
        if self.scheme == "scheme_b":
            return True  # 方案B需要模型全量推理
        return False  # scheme_a 可能不需要（纯规则引擎）

    async def process(self, content: str, task_id: str) -> DesensitizedResult:
        """执行完整脱敏流程

        Args:
            content: 原始文档内容
            task_id: 任务唯一标识

        Returns:
            DesensitizedResult: 脱敏结果
        """
        logger.info(f"[DesensitizeEngine] Processing task {task_id}, "
                   f"content_length={len(content)}")

        # 创建实体注册表（跨维度共享）
        registry = EntityRegistry()

        try:
            if self.scheme == "scheme_a":
                result_content = await self._process_scheme_a(content, registry)
            else:  # scheme_b
                result_content = await self._process_scheme_b(content, registry)

            # 黑白名单最终校验（强制，不可跳过）
            validation = self.validator.validate(result_content)

            if not validation.is_valid:
                logger.error(f"[DesensitizeEngine] Validation failed for {task_id}: "
                           f"{len(validation.violations)} violations")
                # 根据配置决定是阻断还是仅告警
                if self.cfg.get("VALIDATION_FAILURE_ACTION", "block") == "block":
                    return DesensitizedResult(
                        content=result_content,
                        task_id=task_id,
                        dict_hash="",
                        statistics=registry.get_statistics(),
                        is_valid=False,
                        violations=validation.violations
                    )

            # 保存脱敏字典
            mapping_dict = registry.export_mapping()
            dict_hash = await self.dict_manager.save(task_id, mapping_dict)

            return DesensitizedResult(
                content=result_content,
                task_id=task_id,
                dict_hash=dict_hash,
                statistics=registry.get_statistics(),
                is_valid=True,
                violations=[]
            )

        except Exception as e:
            logger.exception(f"[DesensitizeEngine] Processing failed for {task_id}")
            raise

    async def _process_scheme_a(self, content: str, registry: EntityRegistry) -> str:
        """方案A：规则引擎主导 + 模型语义兜底"""
        # Step 1: 规则引擎全量扫描
        content = self.pii_processor.process(content, registry)
        content = self.geo_processor.process(content, registry)
        content = self.biz_processor.process(content, registry)
        content = self.financial_processor.process(content, registry)

        # Step 2: 模型补充识别（如果配置了模型客户端）
        if self.model_client and self.model_client.selective:
            # 提取低置信度片段（这里简化处理，实际可基于规则置信度）
            low_confidence_segments = self._extract_uncertain_segments(content)
            if low_confidence_segments:
                logger.debug(f"[SchemeA] Model supplement for {len(low_confidence_segments)} segments")
                # 可选：调用模型对低置信度片段做补充识别
                # content = await self.model_client.desensitize_text(content, self.level)

        return content

    async def _process_scheme_b(self, content: str, registry: EntityRegistry) -> str:
        """方案B：模型全量语义驱动 + 规则后校验"""
        if not self.model_client:
            raise RuntimeError("Scheme B requires model client but none available")

        # Step 1: 模型全量语义脱敏
        content = await self.model_client.desensitize_text(content, self.level)

        # Step 2: 规则引擎补充校验（精确格式兜底）
        # 确保结构化字段（如精确桩号、手机号格式）完全脱敏
        content = self.pii_processor.process(content, registry, strict_mode=True)
        content = self.geo_processor.process(content, registry, strict_mode=True)

        return content

    def _extract_uncertain_segments(self, content: str) -> List[str]:
        """提取可能未脱敏的低置信度片段（用于模型补充识别）"""
        # 简化实现：检查是否还有潜在的敏感模式残留
        uncertain = []

        # 检查是否有可能是人名但未被处理的片段（如"负责人：XXX"后的XXX）
        name_patterns = [
            r'负责人[：:]\s*([\u4e00-\u9fa5]{2,4})',
            r'项目经理[：:]\s*([\u4e00-\u9fa5]{2,4})',
            r'安全员[：:]\s*([\u4e00-\u9fa5]{2,4})',
        ]

        for pattern in name_patterns:
            matches = re.findall(pattern, content)
            uncertain.extend(matches)

        return uncertain
