"""E2E: PolicyEvaluator against real V1Node objects from the cluster."""

import pytest
from kubernetes import client

from apollo.spoke.node_lifecycle._policy_evaluator import PolicyEvaluator
from apollo.spoke.node_lifecycle.models.policy import (
    ConditionMatch,
    DrainConfig,
    NodeTerminationPolicy,
    PolicySelector,
    PolicySpec,
)

pytestmark = pytest.mark.usefixtures("cluster_reachable")


def _make_policy(
    name: str,
    reason: str = "test",
    priority: int = 10,
    max_age_hours: float | None = None,
    conditions: list[ConditionMatch] | None = None,
    node_selector: dict[str, str] | None = None,
    dry_run: bool = False,
    enabled: bool = True,
) -> NodeTerminationPolicy:
    """Helper: build a NodeTerminationPolicy in-memory."""
    return NodeTerminationPolicy(
        name=name,
        spec=PolicySpec(
            reason=reason,
            priority=priority,
            enabled=enabled,
            dry_run=dry_run,
            selector=PolicySelector(
                max_age_hours=max_age_hours,
                conditions=conditions or [],
                node_selector=node_selector or {},
            ),
            drain=DrainConfig(),
        ),
    )


class TestPolicyEvaluation:
    """Run the PolicyEvaluator against real V1Node objects from the cluster."""

    def test_low_age_threshold_matches_real_node(self, k8s_clients):
        """An age policy with a tiny threshold should match all real nodes."""
        evaluator = PolicyEvaluator()
        core = k8s_clients["core"]
        nodes = core.list_node().items

        policy = _make_policy(
            name="e2e-low-age",
            reason="Node too old",
            max_age_hours=0.001,  # ~4 seconds
        )

        matches = evaluator.evaluate([policy], nodes)
        assert len(matches) > 0
        assert all(m.policy_name == "e2e-low-age" for m in matches)

    def test_huge_age_threshold_matches_nothing(self, k8s_clients):
        """An age policy with a very high threshold should not match."""
        evaluator = PolicyEvaluator()
        core = k8s_clients["core"]
        nodes = core.list_node().items

        policy = _make_policy(
            name="e2e-huge-age",
            reason="Node impossibly old",
            max_age_hours=999999,
        )

        matches = evaluator.evaluate([policy], nodes)
        assert len(matches) == 0

    def test_disk_pressure_condition_no_match(self, k8s_clients):
        """A healthy k3d node should NOT have DiskPressure=True."""
        evaluator = PolicyEvaluator()
        core = k8s_clients["core"]
        nodes = core.list_node().items

        policy = _make_policy(
            name="e2e-disk-pressure",
            reason="Disk pressure",
            conditions=[ConditionMatch(type="DiskPressure", status="True")],
        )

        matches = evaluator.evaluate([policy], nodes)
        assert len(matches) == 0

    def test_node_selector_with_real_label(self, k8s_clients):
        """All k3d nodes should have kubernetes.io/os=linux."""
        evaluator = PolicyEvaluator()
        core = k8s_clients["core"]
        nodes = core.list_node().items

        policy = _make_policy(
            name="e2e-os-selector",
            reason="Linux nodes only",
            max_age_hours=0.001,
            node_selector={"kubernetes.io/os": "linux"},
        )

        matches = evaluator.evaluate([policy], nodes)
        assert len(matches) > 0

    def test_already_labeled_node_is_skipped(self, k8s_clients, node_manager, node_name):
        """A node already labeled for termination should be skipped by the evaluator."""
        evaluator = PolicyEvaluator()
        core = k8s_clients["core"]

        # Label one node for termination
        node_manager.label_for_termination(
            node_name, reason="pre-labeled", policy_name="existing"
        )

        nodes = core.list_node().items
        policy = _make_policy(
            name="e2e-skip-labeled",
            reason="Should skip labeled",
            max_age_hours=0.001,
        )

        matches = evaluator.evaluate([policy], nodes)
        matched_names = {m.node_name for m in matches}
        assert node_name not in matched_names

    def test_priority_ordering(self, k8s_clients):
        """Higher priority policy wins when multiple policies could match the same node."""
        evaluator = PolicyEvaluator()
        core = k8s_clients["core"]
        nodes = core.list_node().items

        low_pri = _make_policy(
            name="e2e-low-pri",
            reason="low priority",
            priority=10,
            max_age_hours=0.001,
        )
        high_pri = _make_policy(
            name="e2e-high-pri",
            reason="high priority",
            priority=100,
            max_age_hours=0.001,
        )

        # Pass low-pri first to ensure sorting works
        matches = evaluator.evaluate([low_pri, high_pri], nodes)
        assert len(matches) > 0
        # All matches should be from the high-priority policy
        for m in matches:
            assert m.policy_name == "e2e-high-pri"
            assert m.priority == 100
