"""Temporal monitoring for risk graph relationship changes."""

from __future__ import annotations

from collections import Counter, defaultdict
from dataclasses import dataclass, field
from typing import Any

from .models import RiskGraphEdge


@dataclass(slots=True)
class TemporalRiskGraphChange:
    change_type: str
    source_key: str
    target_key: str
    edge_type: str
    metadata: dict[str, Any] = field(default_factory=dict)

    def as_dict(self) -> dict[str, Any]:
        return {
            "change_type": self.change_type,
            "source_key": self.source_key,
            "target_key": self.target_key,
            "edge_type": self.edge_type,
            "metadata": dict(self.metadata),
        }


@dataclass(slots=True)
class TemporalRiskGraphSnapshotDiff:
    changes: list[TemporalRiskGraphChange]
    edge_type_delta: dict[str, int]

    def as_dict(self) -> dict[str, Any]:
        return {
            "changes": [item.as_dict() for item in self.changes],
            "edge_type_delta": dict(self.edge_type_delta),
        }


class TemporalRiskGraphMonitor:
    def monitor(
        self,
        previous_edges: list[RiskGraphEdge],
        current_edges: list[RiskGraphEdge],
        *,
        risk_node_keys: set[str] | None = None,
    ) -> TemporalRiskGraphSnapshotDiff:
        previous = {edge.edge_key(): edge for edge in previous_edges}
        current = {edge.edge_key(): edge for edge in current_edges}
        risk_keys = set(risk_node_keys or set())

        changes: list[TemporalRiskGraphChange] = []
        for key, edge in current.items():
            if key not in previous:
                changes.append(_change("relationship_added", edge))
                if edge.source_key in risk_keys or edge.target_key in risk_keys:
                    changes.append(_change("risk_propagation", edge))
        for key, edge in previous.items():
            if key not in current:
                changes.append(_change("relationship_removed", edge))

        changes.extend(_relationship_volume_changes(previous_edges, current_edges))
        changes.extend(_target_migrations(previous_edges, current_edges))
        return TemporalRiskGraphSnapshotDiff(
            changes=changes,
            edge_type_delta=_edge_type_delta(previous_edges, current_edges),
        )


def _change(change_type: str, edge: RiskGraphEdge, **metadata: Any) -> TemporalRiskGraphChange:
    return TemporalRiskGraphChange(
        change_type=change_type,
        source_key=edge.source_key,
        target_key=edge.target_key,
        edge_type=edge.edge_type,
        metadata=metadata,
    )


def _edge_type_delta(
    previous_edges: list[RiskGraphEdge],
    current_edges: list[RiskGraphEdge],
) -> dict[str, int]:
    previous_counts = Counter(edge.edge_type for edge in previous_edges)
    current_counts = Counter(edge.edge_type for edge in current_edges)
    edge_types = set(previous_counts) | set(current_counts)
    return {
        edge_type: current_counts.get(edge_type, 0) - previous_counts.get(edge_type, 0)
        for edge_type in sorted(edge_types)
    }


def _relationship_volume_changes(
    previous_edges: list[RiskGraphEdge],
    current_edges: list[RiskGraphEdge],
) -> list[TemporalRiskGraphChange]:
    changes: list[TemporalRiskGraphChange] = []
    previous_counts = Counter(edge.edge_type for edge in previous_edges)
    current_by_type: dict[str, list[RiskGraphEdge]] = defaultdict(list)
    for edge in current_edges:
        current_by_type[edge.edge_type].append(edge)
    for edge_type, current_group in current_by_type.items():
        previous_count = previous_counts.get(edge_type, 0)
        current_count = len(current_group)
        if current_count >= 3 and current_count >= max(1, previous_count) * 2:
            changes.append(
                _change(
                    "relationship_surge",
                    current_group[0],
                    previous_count=previous_count,
                    current_count=current_count,
                )
            )
    previous_by_type: dict[str, list[RiskGraphEdge]] = defaultdict(list)
    for edge in previous_edges:
        previous_by_type[edge.edge_type].append(edge)
    current_counts = Counter(edge.edge_type for edge in current_edges)
    for edge_type, previous_group in previous_by_type.items():
        if len(previous_group) >= 3 and current_counts.get(edge_type, 0) == 0:
            changes.append(
                _change(
                    "relationship_disappeared",
                    previous_group[0],
                    previous_count=len(previous_group),
                    current_count=0,
                )
            )
    return changes


def _target_migrations(
    previous_edges: list[RiskGraphEdge],
    current_edges: list[RiskGraphEdge],
) -> list[TemporalRiskGraphChange]:
    previous_targets: dict[tuple[str, str], set[str]] = defaultdict(set)
    current_targets: dict[tuple[str, str], set[str]] = defaultdict(set)
    for edge in previous_edges:
        previous_targets[(edge.source_key, edge.edge_type)].add(edge.target_key)
    for edge in current_edges:
        current_targets[(edge.source_key, edge.edge_type)].add(edge.target_key)

    changes: list[TemporalRiskGraphChange] = []
    for key, current_target_set in current_targets.items():
        previous_target_set = previous_targets.get(key, set())
        if previous_target_set and current_target_set != previous_target_set:
            source_key, edge_type = key
            target_key = sorted(current_target_set - previous_target_set or current_target_set)[0]
            changes.append(
                TemporalRiskGraphChange(
                    change_type="target_migration",
                    source_key=source_key,
                    target_key=target_key,
                    edge_type=edge_type,
                    metadata={
                        "previous_targets": sorted(previous_target_set),
                        "current_targets": sorted(current_target_set),
                    },
                )
            )
    return changes
