"""
地理坐标与基建数据脱敏处理器

处理对象：桩号、高程、桥隧名称、地质数据、行政地名等
执行策略：相对化处理（隐去绝对位置，保留物理逻辑）
"""

import re
from typing import Any, Tuple, Optional
from .base_processor import BaseDesensitizer


class GeoDesensitizer(BaseDesensitizer):
    """地理坐标脱敏处理器"""

    def _compile_patterns(self):
        """编译地理坐标相关正则表达式"""
        # 绝对桩号格式：D1K86+279.91, K0+500, DK15+230.5 等
        self.patterns["coordinate"] = re.compile(
            r'([A-Z]?\d?[Kk])(\d{2,5})\+(\d{2,6}(?:\.\d+)?)'
        )

        # 实体名称（桥、隧道、涵洞、河流等）
        self.patterns["bridge"] = re.compile(
            r'([\u4e00-\u9fa5]{2,8}(?:特大桥|大桥))'
        )
        self.patterns["tunnel"] = re.compile(
            r'([\u4e00-\u9fa5]{2,8}隧道)'
        )
        self.patterns["culvert"] = re.compile(
            r'([\u4e00-\u9fa5]{2,6}涵洞)'
        )
        self.patterns["river"] = re.compile(
            r'([\u4e00-\u9fa5]{2,6}(?:河|江))'
        )

        # 高程数据
        self.patterns["elevation"] = re.compile(
            r'海拔\s*(\d{3,4}(?:\.\d+)?)\s*[m米]'
        )

        # 地质描述（保留等级，泛化描述）
        self.patterns["geology"] = re.compile(
            r'(V|IV|III|II|I)[级阶]围岩'
        )

        # 行政地名（省市区县）
        self.patterns["location"] = re.compile(
            r'(四川省|成都市|北京市|上海市|重庆市|天津市|[\u4e00-\u9fa5]{2,6}(?:省|市|区|县))'
        )

    def process(self, content: str, registry: Any, strict_mode: bool = False) -> str:
        """执行地理坐标脱敏"""
        # 1. 计算坐标基准点（最小桩号）
        base_point = self._find_base_point(content)
        if base_point:
            registry.set_coordinate_base(self._format_coordinate(base_point))

        # 2. 桩号相对化处理
        def replace_coordinate(match):
            prefix = match.group(1)  # 如 D1K, K
            km = int(match.group(2))  # 公里数
            meter = match.group(3)  # 米数

            original = match.group(0)

            if base_point:
                # 计算相对桩号
                relative_km, relative_meter = self._calculate_relative(
                    (prefix, km, float(meter) if '.' in meter else int(meter)),
                    base_point
                )
                relative = f"K{relative_km}+{int(relative_meter):03d}"
                registry.register_coordinate(original, relative)
                return relative
            else:
                # 无法确定基准，泛化处理
                registry.register_coordinate(original, f"[桩号脱敏]")
                return "[桩号脱敏]"

        content = self.patterns["coordinate"].sub(replace_coordinate, content)

        # 3. 工程实体序列化处理
        content = self.patterns["bridge"].sub(
            lambda m: registry.register_entity(m.group(1), "桥"), content
        )
        content = self.patterns["tunnel"].sub(
            lambda m: registry.register_entity(m.group(1), "隧道"), content
        )
        content = self.patterns["culvert"].sub(
            lambda m: registry.register_entity(m.group(1), "涵洞"), content
        )
        content = self.patterns["river"].sub(
            lambda m: registry.register_entity(m.group(1), "河"), content
        )

        # 4. 高程数据泛化
        def replace_elevation(match):
            elevation = float(match.group(1))
            # 根据海拔范围泛化地形描述
            if elevation > 3000:
                return "[高原高山区]"
            elif elevation > 1500:
                return "[中山区]"
            elif elevation > 500:
                return "[低山丘陵区]"
            else:
                return "[平原区]"

        content = self.patterns["elevation"].sub(replace_elevation, content)

        # 5. 地质等级保留（V级围岩等）
        # 地质等级属于技术参数，保留但泛化描述
        def replace_geology(match):
            level = match.group(1)
            return f"[{level}级围岩区]"

        if strict_mode:
            content = self.patterns["geology"].sub(replace_geology, content)

        # 6. 行政地名泛化
        content = self.patterns["location"].sub("[某地区]", content)

        return content

    def _find_base_point(self, content: str) -> Optional[Tuple[str, int, float]]:
        """从文本中提取最小桩号作为相对化基准点"""
        coordinates = []

        for match in self.patterns["coordinate"].finditer(content):
            prefix = match.group(1)
            km = int(match.group(2))
            meter_str = match.group(3)
            meter = float(meter_str) if '.' in meter_str else int(meter_str)
            coordinates.append((prefix, km, meter))

        if not coordinates:
            return None

        # 按公里数+米数排序，取最小值
        coordinates.sort(key=lambda x: (x[1], x[2]))
        return coordinates[0]

    def _format_coordinate(self, coord: Tuple[str, int, float]) -> str:
        """格式化坐标"""
        prefix, km, meter = coord
        if isinstance(meter, float):
            return f"{prefix}{km}+{meter:.2f}"
        return f"{prefix}{km}+{meter:03d}"

    def _calculate_relative(self, coord: Tuple[str, int, float],
                           base: Tuple[str, int, float]) -> Tuple[int, float]:
        """计算相对桩号

        Args:
            coord: 当前坐标 (prefix, km, meter)
            base: 基准坐标 (prefix, km, meter)

        Returns:
            (相对公里数, 相对米数)
        """
        coord_total_meters = coord[1] * 1000 + coord[2]
        base_total_meters = base[1] * 1000 + base[2]

        relative_meters = coord_total_meters - base_total_meters
        relative_km = int(relative_meters // 1000)
        relative_meter = int(relative_meters % 1000)

        return relative_km, relative_meter
