import type { ShopUserRole } from "./enums.js"

export const SHOP_USER_PERMISSION_KEYS = [
    "cash_session.open",
    "cash_session.close",
    "cash_session.reopen",
    "cash_session.manual_operation.create",
    "cash_session.deposit_refund.create",
    "cash_session.deposit_refund.undo_own",
] as const

export type ShopUserPermissionKey = typeof SHOP_USER_PERMISSION_KEYS[number]

export type ShopUserPermissionOverrideMode = "inherit" | "grant" | "deny"

export interface ShopUserPermissionOverrides {
    grants: ShopUserPermissionKey[]
    denies: ShopUserPermissionKey[]
}

export interface ShopUserPermissionDefinition {
    key: ShopUserPermissionKey
    label: string
    description: string
    group: "session" | "cash_movements" | "deposit_refunds"
}

export const SHOP_USER_PERMISSION_DEFINITIONS: ShopUserPermissionDefinition[] = [
    {
        key: "cash_session.open",
        label: "Ouvrir une session de caisse",
        description: "Préparer et ouvrir la caisse de la station qui lui est attribuée.",
        group: "session",
    },
    {
        key: "cash_session.close",
        label: "Fermer une session de caisse",
        description: "Clôturer une caisse ouverte après comptage.",
        group: "session",
    },
    {
        key: "cash_session.reopen",
        label: "Réouvrir une session clôturée",
        description: "Réouvrir la dernière session si une clôture a été faite par erreur.",
        group: "session",
    },
    {
        key: "cash_session.manual_operation.create",
        label: "Créer un mouvement manuel de caisse",
        description: "Ajouter ou retirer de l'argent manuellement dans une caisse ouverte.",
        group: "cash_movements",
    },
    {
        key: "cash_session.deposit_refund.create",
        label: "Enregistrer un retour de consigne",
        description: "Créer un mouvement de retour de consigne dans la caisse.",
        group: "deposit_refunds",
    },
    {
        key: "cash_session.deposit_refund.undo_own",
        label: "Annuler son dernier retour de consigne",
        description: "Annuler le dernier retour de consigne créé par ce compte.",
        group: "deposit_refunds",
    },
]

export const CASH_SESSION_LIFECYCLE_PERMISSION_KEYS: ShopUserPermissionKey[] = [
    "cash_session.open",
    "cash_session.close",
    "cash_session.reopen",
]

export const CASH_SESSION_MOVEMENT_PERMISSION_KEYS: ShopUserPermissionKey[] = [
    "cash_session.manual_operation.create",
    "cash_session.deposit_refund.create",
    "cash_session.deposit_refund.undo_own",
]

const ROLE_DEFAULT_PERMISSION_MAP: Record<ShopUserRole, ShopUserPermissionKey[]> = {
    MANAGER: [...SHOP_USER_PERMISSION_KEYS],
    CASHIER: [...SHOP_USER_PERMISSION_KEYS],
    STAFF: [...SHOP_USER_PERMISSION_KEYS],
    RUNNER: [],
}

function dedupePermissionKeys(keys: readonly ShopUserPermissionKey[]): ShopUserPermissionKey[] {
    return [...new Set(keys)]
}

function isShopUserPermissionKey(value: unknown): value is ShopUserPermissionKey {
    return typeof value === "string" && (SHOP_USER_PERMISSION_KEYS as readonly string[]).includes(value)
}

export function normalizeShopUserPermissionOverrides(input?: Partial<ShopUserPermissionOverrides> | null): ShopUserPermissionOverrides {
    const grants = Array.isArray(input?.grants)
        ? dedupePermissionKeys(input.grants.filter(isShopUserPermissionKey))
        : []
    const denies = Array.isArray(input?.denies)
        ? dedupePermissionKeys(input.denies.filter(isShopUserPermissionKey))
        : []

    return {
        grants: grants.filter((permission) => !denies.includes(permission)),
        denies,
    }
}

export function isEmptyShopUserPermissionOverrides(input?: Partial<ShopUserPermissionOverrides> | null): boolean {
    const normalized = normalizeShopUserPermissionOverrides(input)
    return normalized.grants.length === 0 && normalized.denies.length === 0
}

export function getRoleDefaultPermissions(role: ShopUserRole): ShopUserPermissionKey[] {
    return [...(ROLE_DEFAULT_PERMISSION_MAP[role] ?? [])]
}

export function resolveShopUserEffectivePermissions(
    role: ShopUserRole,
    overrides?: Partial<ShopUserPermissionOverrides> | null,
): ShopUserPermissionKey[] {
    const normalized = normalizeShopUserPermissionOverrides(overrides)
    const effective = new Set<ShopUserPermissionKey>(getRoleDefaultPermissions(role))

    for (const denied of normalized.denies) {
        effective.delete(denied)
    }

    for (const granted of normalized.grants) {
        effective.add(granted)
    }

    return [...effective]
}

export function hasShopUserPermission(
    user: {
        role: ShopUserRole
        effectivePermissions?: readonly string[] | null
        permissionOverrides?: Partial<ShopUserPermissionOverrides> | null
    } | null | undefined,
    permission: ShopUserPermissionKey,
): boolean {
    if (!user) return false

    const explicit = Array.isArray(user.effectivePermissions)
        ? user.effectivePermissions.filter(isShopUserPermissionKey)
        : null

    const effective = explicit ?? resolveShopUserEffectivePermissions(user.role, user.permissionOverrides)
    return effective.includes(permission)
}

export function hasAnyShopUserPermission(
    user: {
        role: ShopUserRole
        effectivePermissions?: readonly string[] | null
        permissionOverrides?: Partial<ShopUserPermissionOverrides> | null
    } | null | undefined,
    permissions: readonly ShopUserPermissionKey[],
): boolean {
    return permissions.some((permission) => hasShopUserPermission(user, permission))
}

export function getShopUserPermissionOverrideMode(
    overrides: Partial<ShopUserPermissionOverrides> | null | undefined,
    permission: ShopUserPermissionKey,
): ShopUserPermissionOverrideMode {
    const normalized = normalizeShopUserPermissionOverrides(overrides)
    if (normalized.grants.includes(permission)) return "grant"
    if (normalized.denies.includes(permission)) return "deny"
    return "inherit"
}
