"""Tests for infra_diagram.parsers.kubernetes."""

from __future__ import annotations

import tempfile
from pathlib import Path

from infra_diagram.models import NodeType, SourceType
from infra_diagram.parsers.kubernetes import _K8S_KIND_MAP, parse_kubernetes

# ─── Kind map ────────────────────────────────────────────────────────

class TestKindMap:
    def test_deployment_mapped(self):
        assert _K8S_KIND_MAP["Deployment"] == NodeType.DEPLOYMENT

    def test_service_mapped(self):
        assert _K8S_KIND_MAP["Service"] == NodeType.SERVICE

    def test_ingress_mapped(self):
        assert _K8S_KIND_MAP["Ingress"] == NodeType.INGRESS

    def test_configmap_mapped(self):
        assert _K8S_KIND_MAP["ConfigMap"] == NodeType.CONFIG

    def test_secret_mapped(self):
        assert _K8S_KIND_MAP["Secret"] == NodeType.SECRET

    def test_statefulset_mapped(self):
        assert _K8S_KIND_MAP["StatefulSet"] == NodeType.DEPLOYMENT

    def test_pvc_mapped(self):
        assert _K8S_KIND_MAP["PersistentVolumeClaim"] == NodeType.VOLUME

    def test_network_policy_mapped(self):
        assert _K8S_KIND_MAP["NetworkPolicy"] == NodeType.FIREWALL

    def test_map_count(self):
        assert len(_K8S_KIND_MAP) >= 18


# ─── Parsing ─────────────────────────────────────────────────────────

SIMPLE_DEPLOYMENT = """apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
  namespace: default
spec:
  replicas: 2
  template:
    spec:
      containers:
      - name: app
        image: myapp:latest
        ports:
        - containerPort: 8080
"""

SIMPLE_SERVICE = """apiVersion: v1
kind: Service
metadata:
  name: web-svc
  namespace: default
spec:
  type: ClusterIP
  selector:
    app: web
  ports:
  - port: 80
    targetPort: 8080
"""

MULTI_DOC = SIMPLE_DEPLOYMENT + "---\n" + SIMPLE_SERVICE


class TestParseKubernetes:
    def _write_yaml(self, tmpdir: str, content: str, name: str = "app.yaml") -> str:
        fpath = Path(tmpdir) / name
        fpath.write_text(content, encoding="utf-8")
        return tmpdir

    def test_single_deployment(self):
        with tempfile.TemporaryDirectory() as d:
            self._write_yaml(d, SIMPLE_DEPLOYMENT)
            diagram = parse_kubernetes(d)
            assert diagram.source_type == SourceType.KUBERNETES
            assert diagram.node_count >= 1
            n = diagram.get_node("default/Deployment/web")
            assert n is not None
            assert n.node_type == NodeType.DEPLOYMENT
            assert n.provider == "kubernetes"

    def test_service_parsed(self):
        with tempfile.TemporaryDirectory() as d:
            self._write_yaml(d, SIMPLE_SERVICE)
            diagram = parse_kubernetes(d)
            n = diagram.get_node("default/Service/web-svc")
            assert n is not None
            assert n.node_type == NodeType.SERVICE
            assert n.properties.get("service_type") == "ClusterIP"

    def test_multi_document(self):
        with tempfile.TemporaryDirectory() as d:
            self._write_yaml(d, MULTI_DOC)
            diagram = parse_kubernetes(d)
            assert diagram.node_count >= 2

    def test_container_images_extracted(self):
        with tempfile.TemporaryDirectory() as d:
            self._write_yaml(d, SIMPLE_DEPLOYMENT)
            diagram = parse_kubernetes(d)
            n = diagram.get_node("default/Deployment/web")
            assert "myapp:latest" in n.properties.get("images", [])

    def test_service_ports_extracted(self):
        with tempfile.TemporaryDirectory() as d:
            self._write_yaml(d, SIMPLE_SERVICE)
            diagram = parse_kubernetes(d)
            n = diagram.get_node("default/Service/web-svc")
            assert n.properties.get("ports") is not None
            assert len(n.properties["ports"]) > 0

    def test_service_selector_extracted(self):
        with tempfile.TemporaryDirectory() as d:
            self._write_yaml(d, SIMPLE_SERVICE)
            diagram = parse_kubernetes(d)
            n = diagram.get_node("default/Service/web-svc")
            assert n.properties.get("selector") == {"app": "web"}

    def test_edge_service_to_deployment(self):
        with tempfile.TemporaryDirectory() as d:
            self._write_yaml(d, MULTI_DOC)
            diagram = parse_kubernetes(d)
            svc_edges = [e for e in diagram.edges if "Service" in e.source]
            assert len(svc_edges) > 0

    def test_namespace_grouping(self):
        with tempfile.TemporaryDirectory() as d:
            self._write_yaml(d, SIMPLE_DEPLOYMENT)
            diagram = parse_kubernetes(d)
            assert len(diagram.groups) > 0
            assert any("default" in g.name for g in diagram.groups)

    def test_ingress_parsed(self):
        ingress_yaml = """apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: web-ingress
  namespace: default
spec:
  rules:
  - host: example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-svc
            port:
              number: 80
"""
        with tempfile.TemporaryDirectory() as d:
            self._write_yaml(d, ingress_yaml)
            diagram = parse_kubernetes(d)
            n = diagram.get_node("default/Ingress/web-ingress")
            assert n is not None
            assert n.node_type == NodeType.INGRESS
            assert "example.com" in n.properties.get("hosts", [])

    def test_empty_directory(self):
        with tempfile.TemporaryDirectory() as d:
            diagram = parse_kubernetes(d)
            assert diagram.node_count == 0

    def test_single_file_mode(self):
        with tempfile.TemporaryDirectory() as d:
            fpath = Path(d) / "deploy.yaml"
            fpath.write_text(SIMPLE_DEPLOYMENT, encoding="utf-8")
            diagram = parse_kubernetes(str(fpath))
            assert diagram.node_count >= 1

    def test_invalid_yaml_skipped(self):
        bad_yaml = ": : invalid: [}"
        with tempfile.TemporaryDirectory() as d:
            self._write_yaml(d, bad_yaml)
            diagram = parse_kubernetes(d)
            # Should not crash
            assert diagram.node_count == 0

    def test_non_k8s_yaml_skipped(self):
        normal = "name: hello\nversion: 1.0\n"
        with tempfile.TemporaryDirectory() as d:
            self._write_yaml(d, normal)
            diagram = parse_kubernetes(d)
            assert diagram.node_count == 0

    def test_custom_namespace(self):
        yaml_content = """apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
  namespace: staging
spec:
  replicas: 1
  template:
    spec:
      containers:
      - name: app
        image: myapp:v2
"""
        with tempfile.TemporaryDirectory() as d:
            self._write_yaml(d, yaml_content)
            diagram = parse_kubernetes(d)
            n = diagram.get_node("staging/Deployment/app")
            assert n is not None
            assert n.properties["namespace"] == "staging"

    def test_source_files_tracked(self):
        with tempfile.TemporaryDirectory() as d:
            self._write_yaml(d, SIMPLE_DEPLOYMENT)
            diagram = parse_kubernetes(d)
            assert len(diagram.source_files) == 1

    def test_configmap_parsed(self):
        cm = """apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
  namespace: default
data:
  KEY: value
"""
        with tempfile.TemporaryDirectory() as d:
            self._write_yaml(d, cm)
            diagram = parse_kubernetes(d)
            n = diagram.get_node("default/ConfigMap/app-config")
            assert n is not None
            assert n.node_type == NodeType.CONFIG
