"""KPI bottleneck and performance analyzer."""

from __future__ import annotations

from dataclasses import dataclass
from typing import Any

from .tree import KPITree, KPINode
from .calculator import KPICalculator


@dataclass
class BottleneckInfo:
    """Information about a KPI bottleneck."""

    node_id: str
    node_name: str
    actual: float | None
    target: float | None
    achievement_rate: float | None
    gap: float | None
    impact_score: float  # 0-10 scale
    parent_contribution: float  # How much this affects parent
    depth: int

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary."""
        return {
            "node_id": self.node_id,
            "node_name": self.node_name,
            "actual": self.actual,
            "target": self.target,
            "achievement_rate": self.achievement_rate,
            "gap": self.gap,
            "impact_score": self.impact_score,
            "parent_contribution": self.parent_contribution,
            "depth": self.depth,
        }


@dataclass
class PerformanceInfo:
    """Performance information for a node."""

    node_id: str
    node_name: str
    category: str
    actual: float | None
    target: float | None
    achievement_rate: float | None
    trend: str  # "improving", "stable", "declining"
    unit: str

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary."""
        return {
            "node_id": self.node_id,
            "node_name": self.node_name,
            "category": self.category,
            "actual": self.actual,
            "target": self.target,
            "achievement_rate": self.achievement_rate,
            "trend": self.trend,
            "unit": self.unit,
        }


class KPIAnalyzer:
    """Analyzer for KPI performance and bottlenecks."""

    def __init__(self, tree: KPITree, calculator: KPICalculator | None = None):
        """Initialize analyzer.

        Args:
            tree: KPI tree to analyze.
            calculator: Optional calculator instance.
        """
        self.tree = tree
        self.calculator = calculator or KPICalculator(tree)

    def find_bottlenecks(self, threshold: float = 100.0) -> list[BottleneckInfo]:
        """Find KPIs that are bottlenecking performance.

        Args:
            threshold: Achievement rate threshold (default 100%).
                       Nodes below this are considered bottlenecks.

        Returns:
            List of bottleneck information, sorted by impact.
        """
        bottlenecks: list[BottleneckInfo] = []

        for node in self.tree.nodes.values():
            # Skip if no target
            if node.target is None or node.value is None:
                continue

            achievement = node.achievement_rate
            if achievement is None:
                continue

            # Check if below threshold
            if achievement < threshold:
                impact = self._calculate_impact_score(node)
                contribution = self._calculate_parent_contribution(node)

                bottlenecks.append(
                    BottleneckInfo(
                        node_id=node.id,
                        node_name=node.name,
                        actual=node.value,
                        target=node.target,
                        achievement_rate=achievement,
                        gap=node.gap,
                        impact_score=impact,
                        parent_contribution=contribution,
                        depth=node.depth,
                    )
                )

        # Sort by impact score (highest first)
        bottlenecks.sort(key=lambda b: b.impact_score, reverse=True)

        return bottlenecks

    def _calculate_impact_score(self, node: KPINode) -> float:
        """Calculate impact score for a node (0-10 scale).

        Impact is based on:
        - How far below target
        - Depth in tree (shallower = more impact)
        - Category weight
        """
        if node.achievement_rate is None:
            return 0.0

        # Base score from achievement gap
        gap_ratio = max(0, 1 - (node.achievement_rate / 100))
        base_score = gap_ratio * 5  # Max 5 from gap

        # Depth modifier (shallower nodes have more impact)
        depth_modifier = max(0, 3 - node.depth) / 3 * 3  # Max 3 from depth

        # Category weight (finance and sales are weighted higher)
        category_weights = {
            "finance": 1.0,
            "sales": 0.9,
            "marketing": 0.7,
            "customer": 0.7,
            "product": 0.6,
            "hr": 0.5,
            "operation": 0.5,
        }
        category_weight = category_weights.get(node.category.value, 0.5)
        category_modifier = category_weight * 2  # Max 2 from category

        total = base_score + depth_modifier + category_modifier
        return min(10.0, total)

    def _calculate_parent_contribution(self, node: KPINode) -> float:
        """Calculate how much this node contributes to parent's performance.

        Uses simulation to determine the effect of achieving target.
        """
        if node.parent is None or node.target is None:
            return 0.0

        parent = node.parent
        if parent.value is None or parent.value == 0:
            return 0.0

        # Simulate achieving target
        original_value = node.value
        node.value = node.target

        # Recalculate parent
        new_results = self.calculator.simulate({node.id: node.target})

        # Restore
        node.value = original_value

        # Calculate contribution
        new_parent_value = new_results.get(parent.id, parent.value)
        if parent.value == 0:
            return 0.0

        contribution = (new_parent_value - parent.value) / parent.value * 100
        return contribution

    def get_performance_summary(self) -> list[PerformanceInfo]:
        """Get performance summary for all nodes.

        Returns:
            List of performance information for each node.
        """
        performances: list[PerformanceInfo] = []

        for node in self.tree.nodes.values():
            performances.append(
                PerformanceInfo(
                    node_id=node.id,
                    node_name=node.name,
                    category=node.category.value,
                    actual=node.value,
                    target=node.target,
                    achievement_rate=node.achievement_rate,
                    trend=self._calculate_trend(node),
                    unit=node.unit,
                )
            )

        return performances

    def _calculate_trend(self, node: KPINode) -> str:
        """Calculate trend for a node.

        Note: This is a placeholder. In production, this would
        analyze historical data.
        """
        # TODO: Implement actual trend calculation with historical data
        if node.achievement_rate is None:
            return "stable"

        if node.achievement_rate >= 110:
            return "improving"
        elif node.achievement_rate < 90:
            return "declining"
        else:
            return "stable"

    def get_kgi_summary(self) -> dict[str, Any]:
        """Get KGI summary information.

        Returns:
            Dictionary with KGI summary.
        """
        kgi = self.tree.root

        return {
            "name": kgi.name,
            "actual": kgi.value,
            "target": kgi.target,
            "achievement_rate": kgi.achievement_rate,
            "gap": kgi.gap,
            "unit": kgi.unit,
        }

    def get_category_summary(self) -> dict[str, dict[str, Any]]:
        """Get performance summary by category.

        Returns:
            Dictionary mapping category to summary stats.
        """
        categories: dict[str, list[KPINode]] = {}

        for node in self.tree.nodes.values():
            cat = node.category.value
            if cat not in categories:
                categories[cat] = []
            categories[cat].append(node)

        summary: dict[str, dict[str, Any]] = {}

        for cat, nodes in categories.items():
            achievement_rates = [
                n.achievement_rate for n in nodes if n.achievement_rate is not None
            ]

            summary[cat] = {
                "node_count": len(nodes),
                "avg_achievement": (
                    sum(achievement_rates) / len(achievement_rates)
                    if achievement_rates
                    else None
                ),
                "min_achievement": min(achievement_rates) if achievement_rates else None,
                "max_achievement": max(achievement_rates) if achievement_rates else None,
                "bottleneck_count": len(
                    [r for r in achievement_rates if r < 100]
                ),
            }

        return summary
