"""Tests for infra_diagram.models."""

from __future__ import annotations

from infra_diagram.models import (
    NODE_ICONS,
    InfraDiagram,
    InfraEdge,
    InfraGroup,
    InfraNode,
    NodeType,
    SourceType,
)

# ─── NodeType ────────────────────────────────────────────────────────

class TestNodeType:
    def test_compute_types(self):
        assert NodeType.COMPUTE.value == "compute"
        assert NodeType.CONTAINER.value == "container"
        assert NodeType.SERVERLESS.value == "serverless"
        assert NodeType.VM.value == "vm"

    def test_storage_types(self):
        assert NodeType.STORAGE.value == "storage"
        assert NodeType.DATABASE.value == "database"
        assert NodeType.CACHE.value == "cache"
        assert NodeType.QUEUE.value == "queue"

    def test_network_types(self):
        assert NodeType.NETWORK.value == "network"
        assert NodeType.LOAD_BALANCER.value == "load_balancer"
        assert NodeType.CDN.value == "cdn"
        assert NodeType.DNS.value == "dns"
        assert NodeType.FIREWALL.value == "firewall"
        assert NodeType.VPN.value == "vpn"
        assert NodeType.GATEWAY.value == "gateway"
        assert NodeType.SUBNET.value == "subnet"

    def test_security_types(self):
        assert NodeType.IAM.value == "iam"
        assert NodeType.SECRET.value == "secret"
        assert NodeType.CERTIFICATE.value == "certificate"

    def test_monitoring_types(self):
        assert NodeType.MONITORING.value == "monitoring"
        assert NodeType.LOGGING.value == "logging"

    def test_other_types(self):
        assert NodeType.SERVICE.value == "service"
        assert NodeType.VOLUME.value == "volume"
        assert NodeType.CONFIG.value == "config"
        assert NodeType.REGISTRY.value == "registry"
        assert NodeType.INGRESS.value == "ingress"
        assert NodeType.NAMESPACE.value == "namespace"
        assert NodeType.DEPLOYMENT.value == "deployment"
        assert NodeType.POD.value == "pod"
        assert NodeType.UNKNOWN.value == "unknown"

    def test_str_enum(self):
        """NodeType is a str enum."""
        assert isinstance(NodeType.COMPUTE, str)
        assert NodeType.COMPUTE == "compute"


# ─── SourceType ──────────────────────────────────────────────────────

class TestSourceType:
    def test_values(self):
        assert SourceType.TERRAFORM.value == "terraform"
        assert SourceType.KUBERNETES.value == "kubernetes"
        assert SourceType.DOCKER_COMPOSE.value == "docker-compose"
        assert SourceType.UNKNOWN.value == "unknown"


# ─── NODE_ICONS ──────────────────────────────────────────────────────

class TestNodeIcons:
    def test_all_types_have_icons(self):
        for nt in NodeType:
            assert nt in NODE_ICONS, f"Missing icon for {nt}"

    def test_icon_types(self):
        for nt, icon in NODE_ICONS.items():
            assert isinstance(icon, str)
            assert len(icon) > 0


# ─── InfraNode ───────────────────────────────────────────────────────

class TestInfraNode:
    def test_basic_creation(self):
        node = InfraNode(id="vpc_main", name="main", node_type=NodeType.NETWORK)
        assert node.id == "vpc_main"

    def test_full_creation(self):
        node = InfraNode(
            id="aws_instance.web",
            name="web",
            node_type=NodeType.VM,
            provider="aws",
            source_file="main.tf",
            properties={"ami": "ami-123"},
        )
        assert node.name == "web"
        assert node.node_type == NodeType.VM
        assert node.provider == "aws"
        assert node.properties["ami"] == "ami-123"

    def test_icon_property(self):
        node = InfraNode(id="x", name="x", node_type=NodeType.DATABASE)
        assert node.icon == NODE_ICONS[NodeType.DATABASE]

    def test_icon_default(self):
        node = InfraNode(id="x", name="x", node_type=NodeType.UNKNOWN)
        assert node.icon == "❓"

    def test_label_property(self):
        node = InfraNode(id="x", name="my-server", node_type=NodeType.VM)
        assert "my-server" in node.label
        assert node.icon in node.label

    def test_safe_id(self):
        node = InfraNode(id="a-b.c/d", name="x", node_type=NodeType.UNKNOWN)
        assert node.safe_id == "a_b_c_d"

    def test_safe_id_no_special_chars(self):
        node = InfraNode(id="simple", name="x", node_type=NodeType.UNKNOWN)
        assert node.safe_id == "simple"

    def test_default_values(self):
        node = InfraNode(id="x", name="x", node_type=NodeType.UNKNOWN)
        assert node.provider == ""
        assert node.source_file == ""
        assert node.properties == {}
        assert node.children == []


# ─── InfraEdge ───────────────────────────────────────────────────────

class TestInfraEdge:
    def test_basic(self):
        edge = InfraEdge(source="a", target="b")
        assert edge.source == "a"
        assert edge.target == "b"
        assert edge.label == ""
        assert edge.style == ""

    def test_with_label(self):
        edge = InfraEdge(source="a", target="b", label="routes to")
        assert edge.label == "routes to"

    def test_with_style(self):
        edge = InfraEdge(source="a", target="b", style="dashed")
        assert edge.style == "dashed"


# ─── InfraGroup ──────────────────────────────────────────────────────

class TestInfraGroup:
    def test_basic(self):
        group = InfraGroup(id="grp1", name="My Group")
        assert group.id == "grp1"
        assert group.name == "My Group"
        assert group.node_ids == []

    def test_with_nodes(self):
        group = InfraGroup(id="g1", name="G", node_ids=["a", "b", "c"])
        assert len(group.node_ids) == 3


# ─── InfraDiagram ────────────────────────────────────────────────────

class TestInfraDiagram:
    def test_defaults(self):
        d = InfraDiagram()
        assert d.title == "Infrastructure Diagram"
        assert d.source_type == SourceType.UNKNOWN
        assert d.node_count == 0
        assert d.edge_count == 0
        assert d.nodes == []
        assert d.edges == []
        assert d.groups == []
        assert d.source_files == []

    def test_node_count(self):
        d = InfraDiagram()
        d.nodes.append(InfraNode(id="a", name="a", node_type=NodeType.VM))
        d.nodes.append(InfraNode(id="b", name="b", node_type=NodeType.VM))
        assert d.node_count == 2

    def test_edge_count(self):
        d = InfraDiagram()
        d.edges.append(InfraEdge(source="a", target="b"))
        assert d.edge_count == 1

    def test_add_node(self):
        d = InfraDiagram()
        n = InfraNode(id="a", name="a", node_type=NodeType.VM)
        d.add_node(n)
        assert d.node_count == 1

    def test_add_node_no_duplicate(self):
        d = InfraDiagram()
        n1 = InfraNode(id="a", name="a", node_type=NodeType.VM)
        n2 = InfraNode(id="a", name="a2", node_type=NodeType.VM)
        d.add_node(n1)
        d.add_node(n2)
        assert d.node_count == 1
        assert d.nodes[0].name == "a"

    def test_add_edge(self):
        d = InfraDiagram()
        d.add_edge(InfraEdge(source="a", target="b"))
        assert d.edge_count == 1

    def test_add_group(self):
        d = InfraDiagram()
        d.add_group(InfraGroup(id="g1", name="G"))
        assert len(d.groups) == 1

    def test_get_node_found(self):
        d = InfraDiagram()
        d.add_node(InfraNode(id="abc", name="x", node_type=NodeType.VM))
        assert d.get_node("abc") is not None

    def test_get_node_not_found(self):
        d = InfraDiagram()
        assert d.get_node("nonexistent") is None

    def test_custom_title(self):
        d = InfraDiagram(title="My Infra", source_type=SourceType.TERRAFORM)
        assert d.title == "My Infra"
        assert d.source_type == SourceType.TERRAFORM
