"""Tests for KPI tree."""

import pytest

from src.config import KPINodeConfig, NodeType, KPICategory
from src.kpi.tree import KPITree, KPINode


class TestKPINode:
    """Tests for KPINode."""

    def test_achievement_rate(self):
        """Test achievement rate calculation."""
        node = KPINode(id="test", name="Test", node_type=NodeType.KPI)
        node.value = 80
        node.target = 100

        assert node.achievement_rate == 80.0

    def test_achievement_rate_no_target(self):
        """Test achievement rate when no target."""
        node = KPINode(id="test", name="Test", node_type=NodeType.KPI)
        node.value = 80

        assert node.achievement_rate is None

    def test_achievement_rate_zero_target(self):
        """Test achievement rate with zero target."""
        node = KPINode(id="test", name="Test", node_type=NodeType.KPI)
        node.value = 80
        node.target = 0

        assert node.achievement_rate is None

    def test_gap(self):
        """Test gap calculation."""
        node = KPINode(id="test", name="Test", node_type=NodeType.KPI)
        node.value = 80
        node.target = 100

        assert node.gap == -20

    def test_is_leaf(self):
        """Test leaf node detection."""
        parent = KPINode(id="parent", name="Parent", node_type=NodeType.KPI)
        child = KPINode(id="child", name="Child", node_type=NodeType.INPUT)

        parent.add_child(child)

        assert parent.is_leaf is False
        assert child.is_leaf is True

    def test_depth(self):
        """Test depth calculation."""
        root = KPINode(id="root", name="Root", node_type=NodeType.KGI)
        level1 = KPINode(id="l1", name="L1", node_type=NodeType.KPI)
        level2 = KPINode(id="l2", name="L2", node_type=NodeType.INPUT)

        root.add_child(level1)
        level1.add_child(level2)

        assert root.depth == 0
        assert level1.depth == 1
        assert level2.depth == 2


class TestKPITree:
    """Tests for KPITree."""

    @pytest.fixture
    def tree_config(self):
        """Sample tree configuration."""
        return [
            KPINodeConfig(
                id="revenue",
                name="Revenue",
                type=NodeType.KGI,
                formula="{contracts} * {avg_price}",
                children=["contracts", "avg_price"],
            ),
            KPINodeConfig(
                id="contracts",
                name="Contracts",
                type=NodeType.KPI,
                formula="{leads} * {conversion}",
                children=["leads", "conversion"],
            ),
            KPINodeConfig(
                id="avg_price",
                name="Avg Price",
                type=NodeType.INPUT,
            ),
            KPINodeConfig(
                id="leads",
                name="Leads",
                type=NodeType.INPUT,
                category=KPICategory.MARKETING,
            ),
            KPINodeConfig(
                id="conversion",
                name="Conversion",
                type=NodeType.INPUT,
            ),
        ]

    def test_from_config(self, tree_config):
        """Test tree construction from config."""
        tree = KPITree.from_config(tree_config)

        assert tree.root.id == "revenue"
        assert len(tree.nodes) == 5

    def test_tree_structure(self, tree_config):
        """Test tree structure is correct."""
        tree = KPITree.from_config(tree_config)

        # Check root
        assert tree.root.node_type == NodeType.KGI
        assert len(tree.root.children) == 2

        # Check children
        child_ids = [c.id for c in tree.root.children]
        assert "contracts" in child_ids
        assert "avg_price" in child_ids

    def test_no_kgi_raises(self):
        """Test that missing KGI raises error."""
        config = [
            KPINodeConfig(id="test", name="Test", type=NodeType.KPI),
        ]

        with pytest.raises(ValueError, match="No KGI node found"):
            KPITree.from_config(config)

    def test_multiple_kgi_raises(self):
        """Test that multiple KGIs raise error."""
        config = [
            KPINodeConfig(id="kgi1", name="KGI1", type=NodeType.KGI),
            KPINodeConfig(id="kgi2", name="KGI2", type=NodeType.KGI),
        ]

        with pytest.raises(ValueError, match="Multiple KGI nodes found"):
            KPITree.from_config(config)

    def test_invalid_child_raises(self):
        """Test that invalid child reference raises error."""
        config = [
            KPINodeConfig(
                id="revenue",
                name="Revenue",
                type=NodeType.KGI,
                children=["nonexistent"],
            ),
        ]

        with pytest.raises(ValueError, match="not found"):
            KPITree.from_config(config)

    def test_set_values(self, tree_config):
        """Test setting node values."""
        tree = KPITree.from_config(tree_config)
        tree.set_values({"leads": 100, "conversion": 0.5})

        assert tree.nodes["leads"].value == 100
        assert tree.nodes["conversion"].value == 0.5

    def test_set_targets(self, tree_config):
        """Test setting node targets."""
        tree = KPITree.from_config(tree_config)
        tree.set_targets({"leads": 150, "revenue": 1000000})

        assert tree.nodes["leads"].target == 150
        assert tree.nodes["revenue"].target == 1000000

    def test_get_leaves(self, tree_config):
        """Test getting leaf nodes."""
        tree = KPITree.from_config(tree_config)
        leaves = tree.get_leaves()

        leaf_ids = [n.id for n in leaves]
        assert "avg_price" in leaf_ids
        assert "leads" in leaf_ids
        assert "conversion" in leaf_ids
        assert "revenue" not in leaf_ids

    def test_to_dict(self, tree_config):
        """Test conversion to dictionary."""
        tree = KPITree.from_config(tree_config)
        tree.set_values({"leads": 100})

        result = tree.to_dict()

        assert result["id"] == "revenue"
        assert "children" in result
        assert len(result["children"]) == 2

    def test_to_text(self, tree_config):
        """Test conversion to text."""
        tree = KPITree.from_config(tree_config)

        text = tree.to_text()

        assert "Revenue" in text
        assert "Contracts" in text
        assert "Formula:" in text

    def test_validate_orphan_detection(self):
        """Test orphan node detection."""
        config = [
            KPINodeConfig(
                id="revenue",
                name="Revenue",
                type=NodeType.KGI,
                children=["contracts"],
            ),
            KPINodeConfig(id="contracts", name="Contracts", type=NodeType.INPUT),
            KPINodeConfig(id="orphan", name="Orphan", type=NodeType.INPUT),
        ]

        tree = KPITree.from_config(config)
        errors = tree.validate()

        assert len(errors) == 1
        assert "orphan" in errors[0].lower()
