"""Proximity risk pattern detector.

Uses Rust (petgraph) for BFS distance calculation - no NetworkX dependency.
"""

from typing import Dict, List, Any

from loguru import logger

from chainswarm_analyzers_baseline.graph.builder import GraphData
from chainswarm_analyzers_baseline.patterns.base_detector import (
    BasePatternDetector,
    PatternType,
    DetectionMethod,
    Severity,
    generate_pattern_hash,
    generate_pattern_id,
)
from chainswarm_analyzers_baseline.backends.dispatcher import AlgorithmDispatcher


class ProximityDetector(BasePatternDetector):
    """Detect proximity to risk addresses using Rust backend."""

    @property
    def pattern_type(self) -> str:
        return PatternType.PROXIMITY_RISK

    def detect(
        self,
        graph_data: GraphData,
        address_labels: Dict[str, Dict[str, Any]],
        window_days: int,
        processing_date: str
    ) -> List[Dict[str, Any]]:
        self._address_labels_cache = address_labels
        self._graph_data = graph_data

        graph = graph_data.graph

        if graph.node_count() == 0:
            return []

        patterns_by_hash = {}

        max_distance = self._get_config_value('proximity_analysis', 'max_distance', 3)
        distance_decay_factor = self._get_config_value(
            'proximity_analysis', 'distance_decay_factor', 1.0
        )

        # Get all addresses
        all_addresses = graph.nodes()
        risk_addresses = self._get_fraudulent_addresses(all_addresses)

        if not risk_addresses:
            risk_addresses = self._identify_risk_addresses(graph_data)

        if not risk_addresses:
            return []

        logger.debug(f"Analyzing proximity to {len(risk_addresses)} risk addresses")

        # Create dispatcher for BFS
        dispatcher = AlgorithmDispatcher(graph)

        # Use Rust parallel multi-source BFS for efficiency
        proximity_results = dispatcher.calculate_risk_proximity(
            risk_addresses,
            max_distance=max_distance,
            decay_factor=distance_decay_factor
        )

        # Group results by (risk_addr, address) pairs
        for address, risk_score, risk_addr, distance in proximity_results:
            if address == risk_addr or distance == 0:
                continue

            pattern_hash = generate_pattern_hash(
                PatternType.PROXIMITY_RISK,
                [risk_addr, address]
            )

            if pattern_hash in patterns_by_hash:
                continue

            address_volume = self._calculate_address_volume(graph_data, address)
            flag_type = self._get_flag_type(risk_addr)
            severity = self._determine_proximity_severity(
                distance, flag_type, address_volume
            )

            address_roles = {
                risk_addr: 'risk_source',
                address: 'suspect'
            }

            pattern_id = generate_pattern_id(
                PatternType.PROXIMITY_RISK, pattern_hash
            )

            pattern = {
                'pattern_id': pattern_id,
                'pattern_type': PatternType.PROXIMITY_RISK,
                'pattern_hash': pattern_hash,
                'addresses_involved': sorted([risk_addr, address]),
                'address_roles': address_roles,
                'transaction_ids': [],
                'total_amount_usd': address_volume,
                'detection_method': DetectionMethod.PROXIMITY_ANALYSIS,
                'confidence_score': self._calculate_proximity_confidence(
                    distance, address_volume
                ),
                'severity': severity,
                'evidence': {
                    'hop_distance': distance,
                    'flagged_address': risk_addr,
                    'flag_type': flag_type,
                    'risk_multiplier': risk_score,
                },
                'window_days': window_days,
                'processing_date': processing_date,
                'network': self.network or '',
            }

            patterns_by_hash[pattern_hash] = pattern

        logger.info(f"Detected {len(patterns_by_hash)} proximity risk patterns")
        return list(patterns_by_hash.values())

    def _get_fraudulent_addresses(
        self,
        addresses: List[str]
    ) -> List[str]:
        """Get addresses labeled as fraudulent."""
        return [addr for addr in addresses if self._is_fraudulent_address(addr)]

    def _identify_risk_addresses(
        self,
        graph_data: GraphData
    ) -> List[str]:
        """Identify high-risk addresses based on volume and degree."""
        risk_addresses = []

        high_volume_threshold = self._get_config_value(
            'risk_identification', 'high_volume_threshold', 100000
        )
        high_degree_threshold = self._get_config_value(
            'risk_identification', 'high_degree_threshold', 50
        )

        graph = graph_data.graph

        # Get volume map from Rust (efficient)
        volume_map = graph.volume_map()
        degree_map = graph.degree_map()

        for node in graph.nodes():
            in_vol, out_vol = volume_map.get(node, (0.0, 0.0))
            total_volume = in_vol + out_vol

            in_deg, out_deg = degree_map.get(node, (0, 0))
            degree = in_deg + out_deg

            if total_volume > high_volume_threshold and degree > high_degree_threshold:
                risk_addresses.append(node)

        return risk_addresses

    def _calculate_address_volume(
        self,
        graph_data: GraphData,
        address: str
    ) -> float:
        """Calculate total volume for an address."""
        in_vol = graph_data.graph.in_volume(address)
        out_vol = graph_data.graph.out_volume(address)
        return in_vol + out_vol

    def _get_flag_type(
        self,
        address: str
    ) -> str:
        """Get the type of risk flag for an address."""
        label_info = self._address_labels_cache.get(address, {})
        address_type = label_info.get('address_type', 'UNKNOWN')

        flag_type_map = {
            'MIXER': 'MIXER',
            'SCAM': 'SCAM',
            'DARK_MARKET': 'DARK_MARKET',
            'SANCTIONED': 'SANCTIONED',
        }

        return flag_type_map.get(address_type, 'HIGH_RISK')

    def _determine_proximity_severity(
        self,
        distance: int,
        flag_type: str,
        volume: float
    ) -> str:
        """Determine severity based on proximity characteristics."""
        if flag_type == 'SANCTIONED':
            return Severity.CRITICAL

        if distance == 1:
            if flag_type in ['MIXER', 'DARK_MARKET', 'SCAM']:
                return Severity.CRITICAL
            return Severity.HIGH

        if distance == 2:
            if volume > 100000:
                return Severity.HIGH
            if flag_type in ['MIXER', 'DARK_MARKET']:
                return Severity.HIGH
            return Severity.MEDIUM

        if volume > 100000:
            return Severity.MEDIUM

        return Severity.LOW

    def _calculate_proximity_confidence(
        self,
        distance: int,
        volume: float
    ) -> float:
        """Calculate confidence score for proximity pattern."""
        confidence = 1.0 - (distance * 0.2)

        if volume > 100000:
            confidence += 0.1
        if volume > 1000000:
            confidence += 0.1

        return min(max(confidence, 0.1), 1.0)
