"""RBAC permission checking and fix YAML generation.

Uses SelfSubjectAccessReview (can_i) for per-verb granularity.
No side effects — reads permissions only.
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import TYPE_CHECKING

import yaml

from kuberoku.models import RbacFixYaml, RbacPermission, RbacReport

if TYPE_CHECKING:
    from kuberoku.k8s.protocols import K8sClientProtocol


@dataclass(frozen=True)
class _RbacRule:
    """A single expected RBAC rule."""

    resource: str
    api_group: str
    verbs: tuple[str, ...]
    subresource: str
    required: bool  # True = required, False = elevated
    used_by: str


# ── Required permissions (core functionality) ──────────────────

_REQUIRED_RULES: tuple[_RbacRule, ...] = (
    _RbacRule(
        resource="namespaces",
        api_group="",
        verbs=("get", "list"),
        subresource="",
        required=True,
        used_by="apps",
    ),
    _RbacRule(
        resource="configmaps",
        api_group="",
        verbs=("get", "list", "create", "update", "patch", "delete"),
        subresource="",
        required=True,
        used_by="apps, config, releases",
    ),
    _RbacRule(
        resource="deployments",
        api_group="apps",
        verbs=("get", "list", "create", "update", "patch", "delete"),
        subresource="",
        required=True,
        used_by="deploy, ps",
    ),
    _RbacRule(
        resource="pods",
        api_group="",
        verbs=("get", "list", "delete"),
        subresource="",
        required=True,
        used_by="ps, logs",
    ),
    _RbacRule(
        resource="pods",
        api_group="",
        verbs=("get",),
        subresource="log",
        required=True,
        used_by="logs",
    ),
    _RbacRule(
        resource="pods",
        api_group="",
        verbs=("create",),
        subresource="exec",
        required=True,
        used_by="exec",
    ),
    _RbacRule(
        resource="services",
        api_group="",
        verbs=("get", "list", "create", "update", "delete"),
        subresource="",
        required=True,
        used_by="deploy, addons",
    ),
    _RbacRule(
        resource="jobs",
        api_group="batch",
        verbs=("get", "list", "create", "delete"),
        subresource="",
        required=True,
        used_by="run",
    ),
)

# ── Elevated permissions (optional features) ──────────────────

_ELEVATED_RULES: tuple[_RbacRule, ...] = (
    _RbacRule(
        resource="secrets",
        api_group="",
        verbs=("get", "list", "create", "update", "patch", "delete"),
        subresource="",
        required=False,
        used_by="config --secret",
    ),
    _RbacRule(
        resource="ingresses",
        api_group="networking.k8s.io",
        verbs=("get", "list", "create", "update", "delete"),
        subresource="",
        required=False,
        used_by="domains",
    ),
    _RbacRule(
        resource="statefulsets",
        api_group="apps",
        verbs=("get", "list", "create", "update", "patch", "delete"),
        subresource="",
        required=False,
        used_by="addons",
    ),
    _RbacRule(
        resource="persistentvolumeclaims",
        api_group="",
        verbs=("get", "list", "create", "delete"),
        subresource="",
        required=False,
        used_by="addons",
    ),
    _RbacRule(
        resource="networkpolicies",
        api_group="networking.k8s.io",
        verbs=("get", "list", "create", "update", "delete"),
        subresource="",
        required=False,
        used_by="networking",
    ),
)

ALL_RULES: tuple[_RbacRule, ...] = _REQUIRED_RULES + _ELEVATED_RULES


def check_rbac(
    k8s: K8sClientProtocol,
    namespace: str,
) -> RbacReport:
    """Check all RBAC permissions via SelfSubjectAccessReview."""
    permissions: list[RbacPermission] = []

    for rule in ALL_RULES:
        for verb in rule.verbs:
            allowed = k8s.can_i(
                verb=verb,
                resource=rule.resource,
                namespace=namespace,
                api_group=rule.api_group,
                subresource=rule.subresource,
            )
            permissions.append(
                RbacPermission(
                    resource=rule.resource,
                    api_group=rule.api_group,
                    verb=verb,
                    subresource=rule.subresource,
                    required=rule.required,
                    allowed=allowed,
                    used_by=rule.used_by,
                )
            )

    all_required = all(p.allowed for p in permissions if p.required)
    all_elevated = all(p.allowed for p in permissions if not p.required)

    return RbacReport(
        permissions=tuple(permissions),
        all_required_allowed=all_required,
        all_elevated_allowed=all_elevated,
    )


def generate_rbac_yaml(
    report: RbacReport,
    namespace: str,
    role_name: str = "kuberoku-role",
    sa_name: str = "",
) -> RbacFixYaml:
    """Generate Role + RoleBinding YAML for missing permissions only."""
    # Group missing permissions by (api_group, resource, subresource)
    missing: dict[tuple[str, str, str], set[str]] = {}
    for perm in report.permissions:
        if not perm.allowed:
            key = (perm.api_group, perm.resource, perm.subresource)
            missing.setdefault(key, set()).add(perm.verb)

    if not missing:
        return RbacFixYaml(role_yaml="", rolebinding_yaml="", missing_count=0)

    # Build Role rules
    rules: list[dict[str, list[str]]] = []
    for (api_group, resource, subresource), verbs in sorted(missing.items()):
        rule: dict[str, list[str]] = {
            "apiGroups": [api_group],
            "resources": [f"{resource}/{subresource}" if subresource else resource],
            "verbs": sorted(verbs),
        }
        rules.append(rule)

    role = {
        "apiVersion": "rbac.authorization.k8s.io/v1",
        "kind": "Role",
        "metadata": {
            "name": role_name,
            "namespace": namespace,
        },
        "rules": rules,
    }

    binding = {
        "apiVersion": "rbac.authorization.k8s.io/v1",
        "kind": "RoleBinding",
        "metadata": {
            "name": f"{role_name}-binding",
            "namespace": namespace,
        },
        "roleRef": {
            "apiGroup": "rbac.authorization.k8s.io",
            "kind": "Role",
            "name": role_name,
        },
        "subjects": [
            {
                "kind": "User",
                "name": sa_name or "<YOUR_USER>",
                "apiGroup": "rbac.authorization.k8s.io",
            }
        ],
    }

    role_yaml = yaml.dump(role, default_flow_style=False, sort_keys=False)
    binding_yaml = yaml.dump(binding, default_flow_style=False, sort_keys=False)

    return RbacFixYaml(
        role_yaml=role_yaml,
        rolebinding_yaml=binding_yaml,
        missing_count=len(missing),
    )
