from __future__ import annotations

from collections.abc import Iterable

from govoplan_core.security.permissions import scopes_grant


LEGACY_TO_MODULE_SCOPES: dict[str, str] = {
    "admin:users:read": "access:membership:read",
    "admin:users:create": "access:membership:create",
    "admin:users:update": "access:membership:update",
    "admin:users:suspend": "access:membership:update",
    "admin:groups:read": "access:group:read",
    "admin:groups:write": "access:group:write",
    "admin:groups:manage_members": "access:group:manage_members",
    "admin:roles:read": "access:role:read",
    "admin:roles:write": "access:role:write",
    "admin:roles:assign": "access:role:assign",
    "admin:api_keys:read": "access:api_key:read",
    "admin:api_keys:create": "access:api_key:create",
    "admin:api_keys:revoke": "access:api_key:revoke",
    "admin:settings:read": "access:setting:read",
    "admin:settings:write": "access:setting:write",
    "admin:policies:read": "access:policy:read",
    "admin:policies:write": "access:policy:write",
    "system:tenants:read": "access:tenant:read",
    "system:tenants:create": "access:tenant:create",
    "system:tenants:update": "access:tenant:update",
    "system:tenants:suspend": "access:tenant:suspend",
    "system:accounts:read": "access:account:read",
    "system:accounts:create": "access:account:create",
    "system:accounts:update": "access:account:update",
    "system:accounts:suspend": "access:account:suspend",
    "system:roles:read": "access:system_role:read",
    "system:roles:write": "access:system_role:write",
    "system:roles:assign": "access:system_role:assign",
    "system:access:read": "access:system_role:read",
    "system:access:assign": "access:system_role:assign",
    "system:audit:read": "access:audit:read",
    "system:settings:read": "access:system_setting:read",
    "system:settings:write": "access:system_setting:write",
    "system:maintenance:access": "access:maintenance:access",
    "system:governance:read": "access:governance:read",
    "system:governance:write": "access:governance:write",
    "campaign:read": "campaigns:campaign:read",
    "campaign:create": "campaigns:campaign:create",
    "campaign:update": "campaigns:campaign:update",
    "campaign:copy": "campaigns:campaign:copy",
    "campaign:archive": "campaigns:campaign:archive",
    "campaign:delete": "campaigns:campaign:delete",
    "campaign:share": "campaigns:campaign:share",
    "campaign:validate": "campaigns:campaign:validate",
    "campaign:build": "campaigns:campaign:build",
    "campaign:review": "campaigns:campaign:review",
    "campaign:send_test": "campaigns:campaign:send_test",
    "campaign:queue": "campaigns:campaign:queue",
    "campaign:control": "campaigns:campaign:control",
    "campaign:send": "campaigns:campaign:send",
    "campaign:retry": "campaigns:campaign:retry",
    "campaign:reconcile": "campaigns:campaign:reconcile",
    "recipients:read": "campaigns:recipient:read",
    "recipients:write": "campaigns:recipient:write",
    "recipients:import": "campaigns:recipient:import",
    "recipients:export": "campaigns:recipient:export",
    "reports:read": "campaigns:report:read",
    "reports:export": "campaigns:report:export",
    "reports:send": "campaigns:report:send",
    "files:read": "files:file:read",
    "files:download": "files:file:download",
    "files:upload": "files:file:upload",
    "files:organize": "files:file:organize",
    "files:share": "files:file:share",
    "files:delete": "files:file:delete",
    "files:admin": "files:file:admin",
    "mail_servers:read": "mail:profile:read",
    "mail_servers:use": "mail:profile:use",
    "mail_servers:test": "mail:profile:test",
    "mail_servers:mailbox_read": "mail:mailbox:read",
    "mail_servers:write": "mail:profile:write",
    "mail_servers:manage_credentials": "mail:secret:manage",
}

MODULE_TO_LEGACY_SCOPE_ALIASES: dict[str, tuple[str, ...]] = {}
for legacy_scope, module_scope in LEGACY_TO_MODULE_SCOPES.items():
    MODULE_TO_LEGACY_SCOPE_ALIASES[module_scope] = (
        *MODULE_TO_LEGACY_SCOPE_ALIASES.get(module_scope, ()),
        legacy_scope,
    )
MODULE_TO_LEGACY_SCOPES = {
    module_scope: aliases[0]
    for module_scope, aliases in MODULE_TO_LEGACY_SCOPE_ALIASES.items()
}
MODULE_SYSTEM_SCOPES = frozenset(
    module
    for legacy, module in LEGACY_TO_MODULE_SCOPES.items()
    if legacy.startswith("system:")
)
LEGACY_SYSTEM_SCOPES = frozenset(
    legacy
    for legacy in LEGACY_TO_MODULE_SCOPES
    if legacy.startswith("system:")
)
MODULE_TENANT_SCOPES = frozenset(LEGACY_TO_MODULE_SCOPES.values()) - MODULE_SYSTEM_SCOPES


def compatible_required_scopes(required: str) -> tuple[str, ...]:
    legacy_aliases = MODULE_TO_LEGACY_SCOPE_ALIASES.get(required)
    if legacy_aliases:
        return (required, *legacy_aliases)
    module = LEGACY_TO_MODULE_SCOPES.get(required)
    if module:
        return (required, module)
    return (required,)


def scopes_grant_compatible(scopes: Iterable[str], required: str) -> bool:
    granted = list(scopes)
    if required in MODULE_SYSTEM_SCOPES or required in LEGACY_SYSTEM_SCOPES:
        return "*" in granted or "system:*" in granted or any(
            scope != "tenant:*" and scopes_grant([scope], alias)
            for scope in granted
            for alias in compatible_required_scopes(required)
        )
    if scopes_grant(granted, required):
        return True
    if "tenant:*" in granted or "*" in granted:
        if required in MODULE_TENANT_SCOPES:
            return True
    if "system:*" in granted or "*" in granted:
        if required in MODULE_SYSTEM_SCOPES:
            return True
    return any(scopes_grant(granted, alias) for alias in compatible_required_scopes(required) if alias != required)
