"""Infrastructure resource builder for namespace provisioning.

Pure functions that transform NamespaceInfraSpec into Kubernetes resource dicts.
No API calls — pure data transformation, snapshot-testable.

Ported from Apollo's namespace_controller/_resource_builder.py (infrastructure
functions only). Labels resources with ``app.kubernetes.io/managed-by: citadel-operator``.
"""

from typing import Any

from citadel_operator.k3s.namespace_infra_models import (
    LimitRangeConfig,
    NamespaceInfraSpec,
    NetworkPolicyConfig,
    ResourceQuotaConfig,
)

LABEL_MANAGED_BY = "app.kubernetes.io/managed-by"
MANAGED_BY_VALUE = "citadel-operator"


def build_default_deny_policy(
    target_ns: str, config: NetworkPolicyConfig
) -> dict[str, Any]:
    """Build a CiliumNetworkPolicy that denies all traffic by default.

    Exceptions: DNS resolution (kube-system:53) and K8s API (port 6443).
    """
    return {
        "apiVersion": "cilium.io/v2",
        "kind": "CiliumNetworkPolicy",
        "metadata": {
            "name": f"{target_ns}-default-deny",
            "namespace": target_ns,
            "labels": {LABEL_MANAGED_BY: MANAGED_BY_VALUE},
        },
        "spec": {
            "endpointSelector": {},
            "ingress": [],
            "egress": [
                {
                    "toEndpoints": [
                        {
                            "matchLabels": {
                                "k8s:io.kubernetes.pod.namespace": "kube-system",
                                "k8s-app": "kube-dns",
                            },
                        },
                    ],
                    "toPorts": [
                        {
                            "ports": [
                                {"port": "53", "protocol": "UDP"},
                                {"port": "53", "protocol": "TCP"},
                            ],
                        },
                    ],
                },
                {
                    "toEntities": ["kube-apiserver"],
                    "toPorts": [
                        {
                            "ports": [
                                {"port": "6443", "protocol": "TCP"},
                            ],
                        },
                    ],
                },
            ],
        },
    }


def build_tenant_allow_policy(
    target_ns: str, config: NetworkPolicyConfig
) -> dict[str, Any]:
    """Build a CiliumNetworkPolicy with tenant-specific allow rules."""
    policy: dict[str, Any] = {
        "apiVersion": "cilium.io/v2",
        "kind": "CiliumNetworkPolicy",
        "metadata": {
            "name": f"{target_ns}-tenant-allow",
            "namespace": target_ns,
            "labels": {LABEL_MANAGED_BY: MANAGED_BY_VALUE},
        },
        "spec": {
            "endpointSelector": {},
        },
    }

    ingress_rules: list[dict[str, Any]] = list(config.ingress_rules)
    egress_rules: list[dict[str, Any]] = list(config.egress_rules)

    if config.allow_kube_system:
        ingress_rules.append(
            {
                "fromEndpoints": [
                    {
                        "matchLabels": {
                            "k8s:io.kubernetes.pod.namespace": "kube-system",
                        },
                    },
                ],
            }
        )

    if config.allow_monitoring:
        ingress_rules.append(
            {
                "fromEndpoints": [
                    {
                        "matchLabels": {
                            "k8s:io.kubernetes.pod.namespace": "monitoring",
                        },
                    },
                ],
            }
        )

    # Allow intra-namespace communication
    ingress_rules.append(
        {
            "fromEndpoints": [
                {
                    "matchLabels": {
                        "k8s:io.kubernetes.pod.namespace": target_ns,
                    },
                },
            ],
        }
    )
    egress_rules.append(
        {
            "toEndpoints": [
                {
                    "matchLabels": {
                        "k8s:io.kubernetes.pod.namespace": target_ns,
                    },
                },
            ],
        }
    )

    if ingress_rules:
        policy["spec"]["ingress"] = ingress_rules
    if egress_rules:
        policy["spec"]["egress"] = egress_rules

    return policy


def build_peer_policies(
    target_ns: str, config: NetworkPolicyConfig
) -> list[dict[str, Any]]:
    """Build CiliumNetworkPolicies for bidirectional peer communication."""
    policies: list[dict[str, Any]] = []
    for peer_ns in config.allowed_peers:
        policies.append(
            {
                "apiVersion": "cilium.io/v2",
                "kind": "CiliumNetworkPolicy",
                "metadata": {
                    "name": f"{target_ns}-peer-{peer_ns}",
                    "namespace": target_ns,
                    "labels": {LABEL_MANAGED_BY: MANAGED_BY_VALUE},
                },
                "spec": {
                    "endpointSelector": {},
                    "ingress": [
                        {
                            "fromEndpoints": [
                                {
                                    "matchLabels": {
                                        "k8s:io.kubernetes.pod.namespace": peer_ns,
                                    },
                                },
                            ],
                        },
                    ],
                    "egress": [
                        {
                            "toEndpoints": [
                                {
                                    "matchLabels": {
                                        "k8s:io.kubernetes.pod.namespace": peer_ns,
                                    },
                                },
                            ],
                        },
                    ],
                },
            }
        )
    return policies


def build_resource_quota(
    target_ns: str, config: ResourceQuotaConfig
) -> dict[str, Any] | None:
    """Build a ResourceQuota if quota values are configured."""
    hard: dict[str, str] = {}
    if config.cpu is not None:
        hard["limits.cpu"] = config.cpu
    if config.memory is not None:
        hard["limits.memory"] = config.memory
    if config.pods is not None:
        hard["pods"] = config.pods
    if config.storage is not None:
        hard["requests.storage"] = config.storage
    if config.pvcs is not None:
        hard["persistentvolumeclaims"] = config.pvcs
    for key, value in config.custom:
        hard[key] = str(value)

    if not hard:
        return None

    return {
        "apiVersion": "v1",
        "kind": "ResourceQuota",
        "metadata": {
            "name": f"{target_ns}-quota",
            "namespace": target_ns,
            "labels": {LABEL_MANAGED_BY: MANAGED_BY_VALUE},
        },
        "spec": {"hard": hard},
    }


def build_limit_range(
    target_ns: str, config: LimitRangeConfig
) -> dict[str, Any] | None:
    """Build a LimitRange from configuration."""
    return {
        "apiVersion": "v1",
        "kind": "LimitRange",
        "metadata": {
            "name": f"{target_ns}-limits",
            "namespace": target_ns,
            "labels": {LABEL_MANAGED_BY: MANAGED_BY_VALUE},
        },
        "spec": {
            "limits": [
                {
                    "type": "Container",
                    "default": {
                        "cpu": config.default_cpu,
                        "memory": config.default_memory,
                    },
                    "defaultRequest": {
                        "cpu": config.default_request_cpu,
                        "memory": config.default_request_memory,
                    },
                    "max": {
                        "cpu": config.max_cpu,
                        "memory": config.max_memory,
                    },
                },
            ],
        },
    }
