"""Kubernetes YAML parser — extract resources from K8s manifests."""

from __future__ import annotations

from pathlib import Path

import yaml

from ..models import InfraDiagram, InfraEdge, InfraGroup, InfraNode, NodeType, SourceType

_K8S_KIND_MAP: dict[str, NodeType] = {
    "Deployment": NodeType.DEPLOYMENT,
    "StatefulSet": NodeType.DEPLOYMENT,
    "DaemonSet": NodeType.DEPLOYMENT,
    "ReplicaSet": NodeType.DEPLOYMENT,
    "Pod": NodeType.POD,
    "Service": NodeType.SERVICE,
    "Ingress": NodeType.INGRESS,
    "ConfigMap": NodeType.CONFIG,
    "Secret": NodeType.SECRET,
    "PersistentVolumeClaim": NodeType.VOLUME,
    "PersistentVolume": NodeType.VOLUME,
    "Namespace": NodeType.NAMESPACE,
    "ServiceAccount": NodeType.IAM,
    "Role": NodeType.IAM,
    "RoleBinding": NodeType.IAM,
    "ClusterRole": NodeType.IAM,
    "ClusterRoleBinding": NodeType.IAM,
    "NetworkPolicy": NodeType.FIREWALL,
    "HorizontalPodAutoscaler": NodeType.MONITORING,
    "CronJob": NodeType.COMPUTE,
    "Job": NodeType.COMPUTE,
}


def parse_kubernetes(path: str) -> InfraDiagram:
    """Parse Kubernetes YAML files."""
    diagram = InfraDiagram(
        title="Kubernetes Architecture",
        source_type=SourceType.KUBERNETES,
    )

    p = Path(path)
    yaml_files = []
    if p.is_file():
        yaml_files = [p]
    elif p.is_dir():
        yaml_files = sorted(list(p.glob("**/*.yaml")) + list(p.glob("**/*.yml")))

    for yf in yaml_files:
        diagram.source_files.append(str(yf))
        _parse_k8s_file(yf, diagram)

    _detect_k8s_edges(diagram)
    _group_by_namespace(diagram)

    return diagram


def _parse_k8s_file(yaml_file: Path, diagram: InfraDiagram) -> None:
    """Parse a single K8s YAML file (may contain multiple docs)."""
    try:
        content = yaml_file.read_text(encoding="utf-8", errors="ignore")
    except Exception:
        return

    try:
        docs = list(yaml.safe_load_all(content))
    except yaml.YAMLError:
        return

    for doc in docs:
        if not isinstance(doc, dict):
            continue

        kind = doc.get("kind", "")
        if not kind:
            continue

        metadata = doc.get("metadata", {}) or {}
        name = metadata.get("name", "unnamed")
        namespace = metadata.get("namespace", "default")

        node_type = _K8S_KIND_MAP.get(kind, NodeType.UNKNOWN)
        node_id = f"{namespace}/{kind}/{name}"

        props = {"kind": kind, "namespace": namespace}

        # Extract container images from deployments
        spec = doc.get("spec", {}) or {}
        template_spec = (spec.get("template", {}) or {}).get("spec", {}) or {}
        containers = template_spec.get("containers", [])
        if containers:
            images = [c.get("image", "") for c in containers if c.get("image")]
            if images:
                props["images"] = images

        # Extract ports from services
        if kind == "Service":
            svc_spec = doc.get("spec", {}) or {}
            ports = svc_spec.get("ports", [])
            if ports:
                props["ports"] = [
                    f"{p.get('port', '?')}:{p.get('targetPort', '?')}/{p.get('protocol', 'TCP')}"
                    for p in ports
                ]
            svc_type = svc_spec.get("type", "ClusterIP")
            props["service_type"] = svc_type
            selector = svc_spec.get("selector", {})
            if selector:
                props["selector"] = selector

        # Extract rules from ingress
        if kind == "Ingress":
            ing_spec = doc.get("spec", {}) or {}
            rules = ing_spec.get("rules", [])
            hosts = [r.get("host", "*") for r in rules if r.get("host")]
            if hosts:
                props["hosts"] = hosts

        node = InfraNode(
            id=node_id,
            name=name,
            node_type=node_type,
            provider="kubernetes",
            source_file=str(yaml_file),
            properties=props,
        )
        diagram.add_node(node)


def _detect_k8s_edges(diagram: InfraDiagram) -> None:
    """Detect edges between K8s resources."""
    services = [n for n in diagram.nodes if n.properties.get("kind") == "Service"]
    deployments = [n for n in diagram.nodes if n.properties.get("kind") in ("Deployment", "StatefulSet", "DaemonSet")]
    ingresses = [n for n in diagram.nodes if n.properties.get("kind") == "Ingress"]

    # Service -> Deployment (via selector matching)
    for svc in services:
        selector = svc.properties.get("selector", {})
        if selector:
            for dep in deployments:
                # Simple match: assume service targets deployments in same namespace
                if svc.properties.get("namespace") == dep.properties.get("namespace"):
                    diagram.add_edge(InfraEdge(
                        source=svc.id, target=dep.id, label="selects",
                    ))

    # Ingress -> Service
    for ing in ingresses:
        for svc in services:
            if ing.properties.get("namespace") == svc.properties.get("namespace"):
                diagram.add_edge(InfraEdge(
                    source=ing.id, target=svc.id, label="routes",
                ))


def _group_by_namespace(diagram: InfraDiagram) -> None:
    """Group nodes by Kubernetes namespace."""
    namespaces: dict[str, list[str]] = {}
    for node in diagram.nodes:
        ns = node.properties.get("namespace", "default")
        if ns not in namespaces:
            namespaces[ns] = []
        namespaces[ns].append(node.id)

    for ns, ids in namespaces.items():
        diagram.add_group(InfraGroup(
            id=f"ns_{ns}",
            name=f"namespace: {ns}",
            node_ids=ids,
        ))
