<template>
    <MalioDrawer
        :model-value="modelValue"
        drawer-class="w-full max-w-[450px]"
        header-class="border-b border-black"
        footer-class="justify-between border-t border-black p-6"
        @update:model-value="emit('update:modelValue', $event)"
    >
        <template #header>
            <h2 class="text-[24px] font-bold">
                {{ t('admin.users.drawer.title', { username: user?.username ?? '' }) }}
            </h2>
        </template>
        <div class="flex flex-col space-y-4 py-4">
            <!-- Etat d'erreur de chargement des referentiels : bloque la
                 sauvegarde pour empecher un ecrasement silencieux des droits. -->
            <div
                v-if="loadFailed"
                class="flex items-center gap-2 rounded-lg border border-red-300 bg-red-50 px-4 py-3 text-sm text-red-800"
            >
                <Icon name="mdi:alert-circle-outline" class="size-5 shrink-0" />
                {{ t('admin.users.drawer.loadFailed') }}
            </div>

            <!-- Avertissement auto-edition -->
            <div
                v-if="isSelfEdit"
                class="flex items-center gap-2 rounded-lg border border-yellow-300 bg-yellow-50 px-4 py-3 text-sm text-yellow-800"
            >
                <Icon name="mdi:alert-outline" class="size-5 shrink-0" />
                {{ t('admin.users.drawer.selfWarning') }}
            </div>

            <!-- Toggle Administrateur -->
            <MalioCheckbox
                id="admin-toggle"
                :label="t('admin.users.drawer.adminToggle')"
                :model-value="form.isAdmin"
                label-class="font-semibold text-sm text-neutral-700"
                @update:model-value="form.isAdmin = $event"
            />

            <!-- Section Roles -->
            <!-- !mt-0 : la MalioCheckbox au-dessus expose son slot message (16px),
                 qui couvre deja l'ecart attendu — pas besoin du space-y-4 ici. -->
            <div class="!mt-0">
                <h4 class="mb-3 text-sm font-semibold text-neutral-700">
                    {{ t('admin.users.drawer.rolesSection') }}
                </h4>
                <div class="flex flex-col">
                    <MalioCheckbox
                        v-for="role in allRoles"
                        :key="role.id"
                        :id="`role-${role.id}`"
                        :label="role.label"
                        :model-value="selectedRoleIds.has(role.id)"
                        label-class="text-sm text-neutral-600"
                        @update:model-value="(val: boolean) => toggleRole(role.id, val)"
                    />
                </div>
            </div>

            <!-- Section Permissions directes -->
            <div>
                <h4 class="mb-3 text-sm font-semibold text-neutral-700">
                    {{ t('admin.users.drawer.directPermissionsSection') }}
                </h4>
                <div v-if="permissionsByModule.length === 0" class="text-sm text-neutral-400">
                    {{ t('admin.roles.permissions.noPermissions') }}
                </div>
                <PermissionAccordion
                    v-else
                    :groups-by-module="permissionsByModule"
                    :selected-ids="selectedDirectPermissionIds"
                    id-prefix="direct"
                    @toggle="handleTogglePermission"
                    @toggle-all="handleToggleAll"
                />
            </div>

            <!-- Section Sites autorises (ticket 2 module Sites) -->
            <div>
                <h4 class="mb-3 text-sm font-semibold text-neutral-700">
                    {{ t('admin.users.drawer.sitesSection') }}
                </h4>
                <div v-if="allSites.length === 0" class="text-sm text-neutral-400">
                    {{ t('admin.sites.noSites') }}
                </div>
                <div class="flex flex-col">
                    <MalioCheckbox
                        v-for="site in allSites"
                        :id="`site-${site.id}`"
                        :key="site.id"
                        :label="site.name"
                        :model-value="selectedSiteIds.has(site.id)"
                        label-class="text-sm text-neutral-600"
                        @update:model-value="(val: boolean) => toggleSite(site.id, val)"
                    />
                </div>
            </div>

            <!-- Section Resume permissions effectives -->
            <div>
                <h4 class="mb-3 text-sm font-semibold text-neutral-700">
                    {{ t('admin.users.drawer.summarySection') }}
                </h4>
                <EffectivePermissions :permissions="effectivePermissions" />
            </div>

        </div>

        <!-- Footer fixe : depuis la 1.7.1 le slot #footer est un frere du body
             scrollable (shrink-0), donc reellement fige sans sticky. -->
        <template #footer>
            <MalioButton
                :label="t('common.cancel')"
                variant="tertiary"
                button-class="w-m-btn-action"
                @click="emit('update:modelValue', false)"
            />
            <MalioButton
                :label="t('common.save')"
                variant="primary"
                button-class="w-m-btn-action"
                :disabled="saving || loadFailed"
                @click="handleSave"
            />
        </template>
    </MalioDrawer>
</template>

<script setup lang="ts">
import type { Permission, PermissionModule, Role, UserListItem, UserRbacDetail, EffectivePermission } from '~/shared/types/rbac'
import type { Site } from '~/shared/types/sites'

const { t } = useI18n()
const api = useApi()
const auth = useAuthStore()

const props = defineProps<{
    modelValue: boolean
    user: UserListItem | null
}>()

const emit = defineEmits<{
    'update:modelValue': [value: boolean]
    saved: []
}>()

const saving = ref(false)
const allRoles = ref<Role[]>([])
const allPermissions = ref<Permission[]>([])
const allSites = ref<Site[]>([])
// Signale un echec de chargement des referentiels : on bloque alors la
// sauvegarde pour eviter qu'un drawer ouvert sans donnees (403, reseau)
// n'ecrase silencieusement l'etat RBAC du user (vidage roles/permissions/sites).
const loadFailed = ref(false)

const form = ref({ isAdmin: false })
const selectedRoleIds = ref(new Set<number>())
const selectedDirectPermissionIds = ref(new Set<number>())
const selectedSiteIds = ref(new Set<number>())

// Detecter l'auto-edition
const isSelfEdit = computed(() => props.user?.id === auth.user?.id)

// Extraire un ID depuis une IRI API Platform
function iriToId(iri: string): number {
    return Number(iri.split('/').pop())
}

// Grouper les permissions par module (pour les checkboxes)
const permissionsByModule = computed<PermissionModule[]>(() => {
    const groups = new Map<string, Permission[]>()
    for (const perm of allPermissions.value) {
        if (perm.orphan) continue
        const list = groups.get(perm.module) || []
        list.push(perm)
        groups.set(perm.module, list)
    }
    return Array.from(groups.entries())
        .map(([module, permissions]) => ({ module, permissions }))
        .sort((a, b) => a.module.localeCompare(b.module))
})

// Calculer les permissions effectives avec leurs sources
const effectivePermissions = computed<EffectivePermission[]>(() => {
    const permMap = new Map<number, Permission>()
    for (const p of allPermissions.value) {
        if (!p.orphan) permMap.set(p.id, p)
    }

    // Construire la map permissionId -> sources[]
    const result = new Map<number, string[]>()

    // Permissions heritees des roles
    for (const roleId of selectedRoleIds.value) {
        const role = allRoles.value.find(r => r.id === roleId)
        if (!role) continue
        for (const p of role.permissions) {
            const pid = typeof p === 'string' ? iriToId(p) : p.id
            const sources = result.get(pid) || []
            sources.push(t('admin.users.drawer.sourceRole', { role: role.label }))
            result.set(pid, sources)
        }
    }

    // Permissions directes
    for (const pid of selectedDirectPermissionIds.value) {
        const sources = result.get(pid) || []
        sources.push(t('admin.users.drawer.sourceDirect'))
        result.set(pid, sources)
    }

    // Construire la liste finale
    return Array.from(result.entries())
        .map(([pid, sources]) => {
            const perm = permMap.get(pid)
            if (!perm) return null
            return { code: perm.code, label: perm.label, module: perm.module, sources }
        })
        .filter((p): p is EffectivePermission => p !== null)
        .sort((a, b) => a.code.localeCompare(b.code))
})

// Charger les referentiels (roles, permissions, sites) + le detail RBAC du user
// en parallele pour minimiser le TTFB a l'ouverture du drawer.
// Le detail RBAC est la seule source de verite pour l'etat initial du formulaire :
// props.user vient de la liste /api/users qui n'expose pas les sites (groupe leger).
async function loadData(userId: number) {
    loadFailed.value = false
    try {
        const [rolesData, permsData, sitesData, userRbac] = await Promise.all([
            // `toast: true` : en cas d'echec, l'utilisateur voit un toast
            // d'erreur. Sans ce feedback, le drawer s'afficherait vide et la
            // sauvegarde ecraserait silencieusement l'etat RBAC du user.
            api.get<{ member: Role[] }>('/roles', {}, { toast: true }),
            api.get<{ member: Permission[] }>('/permissions', { orphan: false, itemsPerPage: 999 }, { toast: true }),
            api.get<{ member: Site[] }>('/sites', { itemsPerPage: 999 }, { toast: true }),
            api.get<UserRbacDetail>(`/users/${userId}/rbac`, {}, { toast: true }),
        ])
        allRoles.value = rolesData.member
        allPermissions.value = permsData.member
        allSites.value = sitesData.member

        form.value.isAdmin = userRbac.isAdmin
        selectedRoleIds.value = new Set((userRbac.roles ?? []).map(iriToId))
        selectedDirectPermissionIds.value = new Set((userRbac.directPermissions ?? []).map(iriToId))
        selectedSiteIds.value = new Set((userRbac.sites ?? []).map(iriToId))
    } catch {
        loadFailed.value = true
        allRoles.value = []
        allPermissions.value = []
        allSites.value = []
        resetForm()
    }
}

function resetForm() {
    form.value.isAdmin = false
    selectedRoleIds.value = new Set()
    selectedDirectPermissionIds.value = new Set()
    selectedSiteIds.value = new Set()
}

// Recharger a l'ouverture OU quand le user change pendant que le drawer est ouvert.
// Le watch combine evite un double fetch si les deux changent dans le meme tick.
watch([() => props.modelValue, () => props.user?.id], ([open, userId]) => {
    if (open && userId) {
        loadData(userId)
    } else if (!open) {
        resetForm()
    }
}, { immediate: true })

function toggleRole(id: number, selected: boolean) {
    const ids = new Set(selectedRoleIds.value)
    if (selected) ids.add(id)
    else ids.delete(id)
    selectedRoleIds.value = ids
}

function handleTogglePermission(id: number, selected: boolean) {
    const ids = new Set(selectedDirectPermissionIds.value)
    if (selected) ids.add(id)
    else ids.delete(id)
    selectedDirectPermissionIds.value = ids
}

function handleToggleAll(module: string, selected: boolean) {
    const ids = new Set(selectedDirectPermissionIds.value)
    const group = permissionsByModule.value.find(g => g.module === module)
    if (!group) return
    for (const perm of group.permissions) {
        if (selected) ids.add(perm.id)
        else ids.delete(perm.id)
    }
    selectedDirectPermissionIds.value = ids
}

function toggleSite(id: number, selected: boolean) {
    const ids = new Set(selectedSiteIds.value)
    if (selected) ids.add(id)
    else ids.delete(id)
    selectedSiteIds.value = ids
}

async function handleSave() {
    if (!props.user) return
    saving.value = true
    try {
        await api.patch(`/users/${props.user.id}/rbac`, {
            isAdmin: form.value.isAdmin,
            roles: Array.from(selectedRoleIds.value).map(id => `/api/roles/${id}`),
            directPermissions: Array.from(selectedDirectPermissionIds.value).map(id => `/api/permissions/${id}`),
            sites: Array.from(selectedSiteIds.value).map(id => `/api/sites/${id}`),
        }, {
            toastSuccessMessage: t('admin.users.toast.updated'),
        })
        // Rafraichir les donnees du user courant si auto-edition
        if (isSelfEdit.value) {
            await auth.refreshUser()
        }
        emit('saved')
        emit('update:modelValue', false)
    } finally {
        saving.value = false
    }
}
</script>
