"""
Central role/capability matrix for the application.

This module keeps legacy roles working while enabling granular RBAC.
"""

from __future__ import annotations

from typing import Iterable


class Capability:
    # Technical-level permissions
    CREATE = "create"
    READ = "read"
    UPDATE = "update"
    DELETE = "delete"
    APPROVE = "approve"
    EXPORT = "export"
    PRINT = "print"
    VOID = "void"
    REVERSE = "reverse"

    # Cross-domain permissions
    USERS_MANAGE = "users.manage"
    SETTINGS_MANAGE = "settings.manage"
    REPORTS_VIEW = "reports.view"
    REPORTS_EXPORT = "reports.export"
    AUDIT_VIEW = "audit.view"

    # Inventory
    INVENTORY_READ = "inventory.read"
    INVENTORY_ITEM_CREATE = "inventory.item.create"
    INVENTORY_ITEM_UPDATE = "inventory.item.update"
    INVENTORY_PRICE_UPDATE = "inventory.price.update"
    INVENTORY_STOCK_ADJUST = "inventory.stock.adjust"
    INVENTORY_STOCK_APPROVE = "inventory.stock.approve"
    INVENTORY_TRANSFER_REQUEST = "inventory.transfer.request"
    INVENTORY_TRANSFER_APPROVE = "inventory.transfer.approve"
    INVENTORY_PURCHASE_MANAGE = "inventory.purchase.manage"
    INVENTORY_PURCHASE_RECEIVE = "inventory.purchase.receive"
    INVENTORY_SUPPLIER_MANAGE = "inventory.supplier.manage"
    INVENTORY_EXPORT = "inventory.export"

    # Sales / invoice
    SALES_READ_ALL = "sales.read.all"
    SALES_READ_OWN = "sales.read.own"
    SALES_CREATE = "sales.create"
    SALES_PRINT = "sales.print"
    SALES_VOID = "sales.void"
    SALES_DELETE = "sales.delete"

    INVOICE_READ_ALL = "invoice.read.all"
    INVOICE_READ_OWN = "invoice.read.own"
    INVOICE_CREATE = "invoice.create"
    INVOICE_PRINT = "invoice.print"
    INVOICE_VOID = "invoice.void"
    INVOICE_DELETE = "invoice.delete"
    INVOICE_PAYMENT_MANAGE = "invoice.payment.manage"

    # Finance
    FINANCE_READ = "finance.read"
    FINANCE_CREATE = "finance.create"
    FINANCE_UPDATE = "finance.update"
    FINANCE_DELETE = "finance.delete"
    FINANCE_EXPORT = "finance.export"

    # Assets
    ASSET_READ = "asset.read"
    ASSET_MANAGE = "asset.manage"


class Role:
    SUPER_ADMIN = "super_admin"
    STORE_MANAGER = "store_manager"
    STOREKEEPER = "storekeeper"
    ACCOUNTANT = "accountant"
    SALES_OFFICER = "sales_officer"
    AUDITOR = "auditor"

    # Legacy values still present in existing databases
    OWNER = "owner"
    MANAGER = "manager"
    CASHIER = "cashier"


LEGACY_ROLE_MAP = {
    Role.OWNER: Role.SUPER_ADMIN,
    Role.MANAGER: Role.STORE_MANAGER,
    Role.CASHIER: Role.SALES_OFFICER,
}


def normalize_role(role: str | None) -> str:
    if not role:
        return ""
    return LEGACY_ROLE_MAP.get(role, role)


def _base_write_capabilities() -> set[str]:
    return {
        Capability.CREATE,
        Capability.READ,
        Capability.UPDATE,
        Capability.APPROVE,
        Capability.EXPORT,
        Capability.PRINT,
        Capability.VOID,
        Capability.REVERSE,
    }


ROLE_CAPABILITIES: dict[str, set[str]] = {
    Role.SUPER_ADMIN: {"*"},
    Role.STORE_MANAGER: _base_write_capabilities()
    | {
        Capability.REPORTS_VIEW,
        Capability.REPORTS_EXPORT,
        Capability.INVENTORY_READ,
        Capability.INVENTORY_ITEM_CREATE,
        Capability.INVENTORY_ITEM_UPDATE,
        Capability.INVENTORY_PRICE_UPDATE,
        Capability.INVENTORY_STOCK_ADJUST,
        Capability.INVENTORY_STOCK_APPROVE,
        Capability.INVENTORY_TRANSFER_REQUEST,
        Capability.INVENTORY_TRANSFER_APPROVE,
        Capability.INVENTORY_PURCHASE_MANAGE,
        Capability.INVENTORY_PURCHASE_RECEIVE,
        Capability.INVENTORY_SUPPLIER_MANAGE,
        Capability.INVENTORY_EXPORT,
        Capability.SALES_READ_ALL,
        Capability.SALES_CREATE,
        Capability.SALES_PRINT,
        Capability.SALES_VOID,
        Capability.INVOICE_READ_ALL,
        Capability.INVOICE_CREATE,
        Capability.INVOICE_PRINT,
        Capability.INVOICE_VOID,
        Capability.INVOICE_PAYMENT_MANAGE,
        Capability.FINANCE_READ,
        Capability.FINANCE_CREATE,
        Capability.FINANCE_UPDATE,
        Capability.FINANCE_EXPORT,
        Capability.ASSET_READ,
        Capability.ASSET_MANAGE,
    },
    Role.STOREKEEPER: {
        Capability.CREATE,
        Capability.READ,
        Capability.UPDATE,
        Capability.INVENTORY_READ,
        Capability.INVENTORY_ITEM_CREATE,
        Capability.INVENTORY_ITEM_UPDATE,
        Capability.INVENTORY_STOCK_ADJUST,
        Capability.INVENTORY_TRANSFER_REQUEST,
        Capability.INVENTORY_PURCHASE_RECEIVE,
        Capability.REPORTS_VIEW,
        Capability.ASSET_READ,
    },
    Role.ACCOUNTANT: {
        Capability.CREATE,
        Capability.READ,
        Capability.UPDATE,
        Capability.EXPORT,
        Capability.REPORTS_VIEW,
        Capability.REPORTS_EXPORT,
        Capability.INVENTORY_READ,
        Capability.SALES_READ_ALL,
        Capability.INVOICE_READ_ALL,
        Capability.INVOICE_PAYMENT_MANAGE,
        Capability.FINANCE_READ,
        Capability.FINANCE_CREATE,
        Capability.FINANCE_UPDATE,
        Capability.FINANCE_EXPORT,
        Capability.ASSET_READ,
    },
    Role.SALES_OFFICER: {
        Capability.CREATE,
        Capability.READ,
        Capability.PRINT,
        Capability.SALES_READ_OWN,
        Capability.SALES_CREATE,
        Capability.SALES_PRINT,
        Capability.INVOICE_READ_OWN,
        Capability.INVOICE_CREATE,
        Capability.INVOICE_PRINT,
    },
    Role.AUDITOR: {
        Capability.READ,
        Capability.REPORTS_VIEW,
        Capability.AUDIT_VIEW,
        Capability.INVENTORY_READ,
        Capability.SALES_READ_ALL,
        Capability.INVOICE_READ_ALL,
        Capability.FINANCE_READ,
        Capability.ASSET_READ,
    },
}


ROLE_IMPLICATIONS: dict[str, set[str]] = {
    Role.SUPER_ADMIN: {
        Role.SUPER_ADMIN,
        Role.STORE_MANAGER,
        Role.STOREKEEPER,
        Role.ACCOUNTANT,
        Role.SALES_OFFICER,
        Role.AUDITOR,
    },
    Role.STORE_MANAGER: {
        Role.STORE_MANAGER,
        Role.STOREKEEPER,
        Role.SALES_OFFICER,
    },
    Role.STOREKEEPER: {Role.STOREKEEPER},
    Role.ACCOUNTANT: {Role.ACCOUNTANT},
    Role.SALES_OFFICER: {Role.SALES_OFFICER},
    Role.AUDITOR: {Role.AUDITOR},
}


def _capability_matches(granted: str, required: str) -> bool:
    if granted == "*" or granted == required:
        return True
    if granted.endswith(".*"):
        return required.startswith(granted[:-1])
    return False


def role_has_capability(role: str | None, capability: str) -> bool:
    normalized = normalize_role(role)
    granted_caps = ROLE_CAPABILITIES.get(normalized, set())
    return any(_capability_matches(granted, capability) for granted in granted_caps)


def role_has_all_capabilities(role: str | None, capabilities: Iterable[str]) -> bool:
    return all(role_has_capability(role, cap) for cap in capabilities)


def role_has_any_capability(role: str | None, capabilities: Iterable[str]) -> bool:
    return any(role_has_capability(role, cap) for cap in capabilities)


def role_matches(role: str | None, allowed_roles: Iterable[str]) -> bool:
    actual = normalize_role(role)
    implied = ROLE_IMPLICATIONS.get(actual, {actual})
    normalized_allowed = {normalize_role(r) for r in allowed_roles}
    return bool(implied & normalized_allowed)


def all_capabilities() -> list[str]:
    """
    Return normalized capability codes used by the system, including wildcard.
    """

    caps = set()
    for name, value in Capability.__dict__.items():
        if name.isupper() and isinstance(value, str) and value:
            caps.add(value)

    for role_caps in ROLE_CAPABILITIES.values():
        caps.update(role_caps)

    return sorted(caps)


def capability_label(capability: str) -> str:
    if capability == "*":
        return "All Capabilities"
    return capability.replace(".", " ").replace("_", " ").title()
