"""Role × Section permission service.

Loads `backend/config/role_permissions.yaml` once at import time, exposes:

  ALL_SECTIONS      — flat list of every section_id defined in the matrix
  KNOWN_ROLES       — list of role names allowed by the matrix
  role_can(role, section_id)        — bool check
  sections_for(role)                — list of section_ids the role can see
  groups_for(role)                  — {group_key: [section_id, ...]} for sidebar

Used by:
  - middleware/auth.require_section(section_id) — per-endpoint guard
  - /api/auth/me/sections                       — sidebar filter feed
"""
from __future__ import annotations

from collections import defaultdict
from pathlib import Path
from typing import Iterable

import yaml

_CFG_PATH = Path(__file__).resolve().parent / "role_permissions.yaml"

ADMIN_ROLE = "Admin"


def _load() -> dict:
    if not _CFG_PATH.exists():
        return {"roles": [ADMIN_ROLE], "permissions": {}}
    with _CFG_PATH.open(encoding="utf-8") as f:
        return yaml.safe_load(f) or {}


_RAW = _load()
KNOWN_ROLES: list[str] = list(_RAW.get("roles", []))
_PERMS_RAW: dict[str, dict[str, list[str]]] = _RAW.get("permissions", {})

# Flatten into:  section_id -> set of roles
#                section_id -> group_key  (for sidebar grouping)
_SECTION_ROLES: dict[str, set[str]] = {}
_SECTION_GROUP: dict[str, str] = {}
_GROUP_SECTIONS: dict[str, list[str]] = defaultdict(list)

for group, items in _PERMS_RAW.items():
    if not isinstance(items, dict):
        continue
    for section_id, roles in items.items():
        # Admin implicit
        role_set = set(roles or [])
        role_set.add(ADMIN_ROLE)
        _SECTION_ROLES[section_id] = role_set
        _SECTION_GROUP[section_id] = group
        _GROUP_SECTIONS[group].append(section_id)

ALL_SECTIONS: list[str] = list(_SECTION_ROLES.keys())


def role_can(role: str, section_id: str) -> bool:
    """True if role has access to section_id. Admin always wins."""
    if role == ADMIN_ROLE:
        return True
    allowed = _SECTION_ROLES.get(section_id)
    if allowed is None:
        # Unknown section: deny by default. Logged for debug elsewhere.
        return False
    return role in allowed


def sections_for(role: str) -> list[str]:
    """All section_ids visible to this role."""
    if role == ADMIN_ROLE:
        return list(ALL_SECTIONS)
    return [s for s, roles in _SECTION_ROLES.items() if role in roles]


def groups_for(role: str) -> dict[str, list[str]]:
    """Group → [section_id, ...] limited to those the role can see."""
    out: dict[str, list[str]] = defaultdict(list)
    for s in sections_for(role):
        out[_SECTION_GROUP.get(s, "other")].append(s)
    return dict(out)


def section_group(section_id: str) -> str:
    return _SECTION_GROUP.get(section_id, "other")


def roles_for_section(section_id: str) -> Iterable[str]:
    return _SECTION_ROLES.get(section_id, set())
