<script lang="ts">
/**
 * Format bytes as human-readable text.
 * 
 * @param bytes Number of bytes.
 * @param si True to use metric (SI) units, aka powers of 1000. False to use 
 *           binary (IEC), aka powers of 1024.
 * @param dp Number of decimal places to display.
 * 
 * @return Formatted string.
 */
 function textualFileSize(bytes: number, si: boolean = false, dp: number = 2) {
  const thresh = si ? 1000 : 1024;

  if (Math.abs(bytes) < thresh) {
    return bytes + ' B';
  }

  const units = ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
  let u = -1;
  const r = 10**dp;

  do {
    bytes /= thresh;
    ++u;
  } while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1);


  return bytes.toFixed(dp) + ' ' + units[u];
}
</script>

<script setup lang="ts">
import { format } from '~/shared/general.util';
import { iconByType } from '~/shared/content.util';
import { Icon } from '@iconify/vue/dist/iconify.js';

interface File
{
    path: string;
    owner: number;
    title: string;
    type: "file" | "canvas" | "markdown" | 'folder';
    size: number;
    navigable: boolean;
    private: boolean;
    order: number;
    visit: number;
    timestamp: string;
}
interface User
{
    id: number;
    username: string;
    state: number;
    session: {
        id: number;
    }[];
    data: {
        id: number;
        signin: string;
        lastTimestamp: string;
        logCount: number;
    };
    permission: string[];
}

definePageMeta({
    rights: ['admin'],
});

const toaster = useToast();

const { data: users } = useFetch('/api/admin/users', {
    transform: (users) => { 
        //@ts-ignore
        users.forEach(e => e.permission = e.permission.map(p => p.permission)); 
        //@ts-ignore
        return users as User[];
    },
});
const { data: pages } = useFetch('/api/admin/pages');

const sorter = ref<((a: File, b: File) => number) | null>(null);
const sortField = ref<keyof File | null>(null), sortOrder = ref<null | 'asc' | 'desc'>('asc');
const sortedPage = ref([...pages.value ?? []]);

const permissionCopy = ref<string[]>([]);

watch([sortField, sortOrder, sorter], () => {
    sortedPage.value = (sorter.value === null ? ([...pages.value ?? []]) : sortedPage.value.sort(sorter.value))
}, {
    immediate: true,
});

function sort(field: keyof File, type: 'string' | 'number')
{
    if(sortField.value === field)
    {
        if(sortOrder.value === 'asc')
        {
            sortOrder.value = 'desc';
            sorter.value = type === 'string' ? (a: File, b: File) => (b[field] as string).localeCompare(a[field] as string) : (a: File, b: File) => (b[field] as number) - (a[field] as number);
        }
        else
        {
            sortOrder.value = null;
            sortField.value = null;
            sorter.value = null;
        }
    }
    else
    {
        sortField.value = field;
        sortOrder.value = 'asc';
        sorter.value = type === 'string' ? (a: File, b: File) => (a[field] as string).localeCompare(b[field] as string) : (a: File, b: File) => (a[field] as number) - (b[field] as number);
    }
}
async function editPermissions(user: User)
{
    try
    {
        await $fetch(`/api/admin/user/${user.id}/permissions`, {
            method: 'POST',
            body: permissionCopy.value,
        });
        user.permission = permissionCopy.value;
        toaster.add({
            duration: 10000, type: 'success', content: 'Permissions mises à jour.', timer: true,
        });
    }
    catch(e)
    {
        toaster.add({
            duration: 10000, type: 'error', content: (e as any).message, timer: true,
        });
    }
}
async function logout(user: User)
{
    try
    {
        await $fetch(`/api/admin/user/${user.id}/logout`, {
            method: 'POST',
        });

        user.session.length = 0;
        
        toaster.add({
            duration: 10000, type: 'success', content: 'L\'utilisateur vient d\'être déconnecté.', timer: true,
        });
    }
    catch(e)
    {
        toaster.add({
            duration: 10000, type: 'error', content: (e as any).message, timer: true,
        });
    }
}
</script>

<template>
    <Head>
        <Title>d[any] - Administration</Title>
    </Head>
    <div class="flex flex-1 flex-col p-4">
        <div class="flex flex-row justify-between items-center">
            <ProseH2 class="text-center flex-1">Administration</ProseH2>
            <Button><NuxtLink :to="{ name: 'admin-jobs' }">Jobs</NuxtLink></Button>
        </div>
        <div class="flex flex-1 w-full justify-center items-stretch flex-row gap-4">
            <div class="flex-1">
                <Collapsible v-if=users :label="`Utilisateurs (${users.length})`">
                    <div class="flex flex-1 mt-2">
                        <table class="border-collapse">
                            <thead>
                                <tr>
                                    <th class="border border-light-35 dark:border-dark-35 px-2 py-1">Utilisateur</th>
                                    <th class="border border-light-35 dark:border-dark-35 px-2 py-1">Inscription</th>
                                    <th class="border border-light-35 dark:border-dark-35 px-2 py-1">Dernière connexion</th>
                                    <th class="border border-light-35 dark:border-dark-35 px-2 py-1">Mail</th>
                                    <th class="border border-light-35 dark:border-dark-35 px-2 py-1">Sessions</th>
                                    <th class="border border-light-35 dark:border-dark-35 px-2 py-1">Permissions</th>
                                </tr>
                            </thead>
                            <tbody class="font-normal">
                                <tr v-for="user in users">
                                    <td class="border border-light-35 dark:border-dark-35 px-2 py-1 max-w-32 truncate"><NuxtLink :to="{ name: 'user-id', params: { id: user.id } }" class="hover:text-accent-purple font-bold" :title="user.username">{{ user.username }}</NuxtLink></td>
                                    <td class="border border-light-35 dark:border-dark-35 px-2 py-1 text-sm text-light-70 dark:text-dark-70 text-center">{{ format(new Date(user.data.signin), 'dd/MM/yyyy') }}</td>
                                    <td class="border border-light-35 dark:border-dark-35 px-2 py-1 text-sm text-light-70 dark:text-dark-70 text-center">{{ format(new Date(user.data.lastTimestamp), 'dd/MM/yyyy HH:mm:ss') }}</td>
                                    <td class="border border-light-35 dark:border-dark-35 px-2 py-1 text-center"><Icon :class="{ 'text-light-red dark:text-dark-red': user.state === 0, 'text-light-green dark:text-dark-green': user.state !== 0 }" :icon="user.state === 0 ? `radix-icons:cross-2` : `radix-icons:check`" /></td>
                                    <td class="border border-light-35 dark:border-dark-35 px-2 py-1">
                                        <DialogRoot>
                                            <DialogTrigger asChild><span class="text-accent-blue hover:text-accent-purple font-bold cursor-pointer">{{ user.session.length }}</span></DialogTrigger>
                                            <DialogPortal>
                                                <DialogOverlay class="bg-light-0 dark:bg-dark-0 opacity-70 fixed inset-0 z-40" />
                                                <DialogContent
                                                    class="data-[state=open]:animate-contentShow fixed top-[50%] left-[50%] max-h-[85vh] w-[90vw] max-w-[800px] translate-x-[-50%] translate-y-[-50%] bg-light-10 dark:bg-dark-10 border border-light-30 dark:border-dark-30 p-6 z-50 text-light-100 dark:text-dark-100">
                                                    <DialogTitle class="text-3xl font-light relative -top-2">Deconnecter l'utilisateur ?
                                                    </DialogTitle>
                                                    <div class="flex flex-1 justify-end gap-4">
                                                        <DialogClose asChild><Button>Non</Button></DialogClose>
                                                        <DialogClose asChild><Button @click="() => logout(user)" class="border-light-green dark:border-dark-green hover:border-light-green dark:hover:border-dark-green hover:bg-light-greenBack dark:hover:bg-dark-greenBack text-light-green dark:text-dark-green focus:shadow-light-green dark:focus:shadow-dark-green">Oui</Button></DialogClose>
                                                    </div>
                                                </DialogContent>
                                            </DialogPortal>
                                        </DialogRoot>
                                    </td>
                                    <td class="border border-light-35 dark:border-dark-35 px-2 py-1">
                                        <AlertDialogRoot>
                                            <AlertDialogTrigger asChild><span class="text-accent-blue hover:text-accent-purple font-bold" @click="permissionCopy = [...user.permission]">{{ user.permission.length }}</span></AlertDialogTrigger>
                                            <AlertDialogPortal>
                                                <AlertDialogOverlay class="bg-light-0 dark:bg-dark-0 opacity-70 fixed inset-0 z-40" />
                                                <AlertDialogContent
                                                    class="data-[state=open]:animate-contentShow fixed top-[50%] left-[50%] max-h-[85vh] w-[90vw] max-w-[800px] translate-x-[-50%] translate-y-[-50%] bg-light-10 dark:bg-dark-10 border border-light-30 dark:border-dark-30 p-6 z-50 text-light-100 dark:text-dark-100">
                                                    <AlertDialogTitle class="text-3xl font-light relative -top-2">Permissions de {{ user.username }}</AlertDialogTitle>
                                                    <AlertDialogDescription><TagsInput v-model="permissionCopy" /></AlertDialogDescription>
                                                    <div class="flex flex-1 justify-end gap-4">
                                                        <AlertDialogCancel asChild><Button>Annuler</Button></AlertDialogCancel>
                                                        <AlertDialogAction asChild><Button @click="() => editPermissions(user)" class="border-light-green dark:border-dark-green hover:border-light-green dark:hover:border-dark-green hover:bg-light-greenBack dark:hover:bg-dark-greenBack text-light-green dark:text-dark-green focus:shadow-light-green dark:focus:shadow-dark-green">Modifier</Button></AlertDialogAction>
                                                    </div>
                                                </AlertDialogContent>
                                            </AlertDialogPortal>
                                        </AlertDialogRoot>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                    </div>
                </Collapsible>
            </div>
            <div class="flex-1">
                <Collapsible v-if=pages :label="`Pages (${pages.length})`">
                    <div class="flex flex-1 mt-2">
                        <table class="border-collapse">
                            <thead>
                                <tr>
                                    <th class="border border-light-35 dark:border-dark-35 px-2 py-1"><div class="flex justify-center items-center gap-2"><span>Page</span><span @click="() => sort('title', 'string')"><Icon :icon="sortField === 'title' ? sortOrder === 'asc' ? 'radix-icons:chevron-down' : 'radix-icons:chevron-up' : 'radix-icons:caret-sort'" /></span></div></th>
                                    <th class="border border-light-35 dark:border-dark-35 px-2 py-1"><div class="flex justify-center items-center gap-2"><span>Type</span></div></th>
                                    <th class="border border-light-35 dark:border-dark-35 px-2 py-1"><div class="flex justify-center items-center gap-2"><span>Propriétaire</span><span @click="() => sort('owner', 'number')"><Icon :icon="sortField === 'owner' ? sortOrder === 'asc' ? 'radix-icons:chevron-down' : 'radix-icons:chevron-up' : 'radix-icons:caret-sort'" /></span></div></th>
                                    <th class="border border-light-35 dark:border-dark-35 px-2 py-1"><div class="flex justify-center items-center gap-2"><span>Status</span></div></th>
                                    <th class="border border-light-35 dark:border-dark-35 px-2 py-1"><div class="flex justify-center items-center gap-2"><span>Taille</span><span @click="() => sort('size', 'number')"><Icon :icon="sortField === 'size' ? sortOrder === 'asc' ? 'radix-icons:chevron-down' : 'radix-icons:chevron-up' : 'radix-icons:caret-sort'" /></span></div></th>
                                    <th class="border border-light-35 dark:border-dark-35 px-2 py-1"><div class="flex justify-center items-center gap-2"><span>Visites</span><span @click="() => sort('visit', 'number')"><Icon :icon="sortField === 'visit' ? sortOrder === 'asc' ? 'radix-icons:chevron-down' : 'radix-icons:chevron-up' : 'radix-icons:caret-sort'" /></span></div></th>
                                    <th class="border border-light-35 dark:border-dark-35 px-2 py-1"><div class="flex justify-center items-center gap-2"><span>Actions</span></div></th>
                                </tr>
                            </thead>
                            <tbody class="font-normal">
                                <DialogRoot>
                                    <tr v-for="page in sortedPage" :id="page.path">
                                        <td class="border border-light-35 dark:border-dark-35 px-2 py-1 max-w-48 truncate"><NuxtLink :to="{ name: 'explore-path', params: { path: page.path } }" class="hover:text-accent-purple font-bold" :title="page.title">{{ page.title }}</NuxtLink></td>
                                        <td class="border border-light-35 dark:border-dark-35 px-2 py-1"><Icon :icon="iconByType[page.type]" /></td>
                                        <td class="border border-light-35 dark:border-dark-35 px-2 py-1 text-sm text-light-70 dark:text-dark-70 text-center max-w-32 truncate"><span :title=" users?.find(e => e.id === page.owner)?.username ?? 'Inconnu'">{{ users?.find(e => e.id === page.owner)?.username ?? "Inconnu" }}</span></td>
                                        <td class="border border-light-35 dark:border-dark-35 px-2 py-1 ">
                                            <div class="flex gap-2 justify-center">
                                                <span>
                                                    <Icon v-if="page.private" icon="radix-icons:lock-closed" />
                                                    <Icon v-else class="text-light-50 dark:text-dark-50" icon="radix-icons:lock-open-2" />
                                                </span>
                                                <span>
                                                    <Icon v-if="page.navigable" icon="radix-icons:eye-open" />
                                                    <Icon v-else class="text-light-50 dark:text-dark-50" icon="radix-icons:eye-none" />
                                                </span>
                                            </div>
                                        </td>
                                        <td class="border border-light-35 dark:border-dark-35 px-2 py-1 text-center">{{ textualFileSize(page.size) }}</td>
                                        <td class="border border-light-35 dark:border-dark-35 px-2 py-1 text-center">{{ page.visit }}</td>
                                        <td class="border border-light-35 dark:border-dark-35 px-2 py-1 text-center"><div class="flex justify-center items-center"><NuxtLink :to="{ name: 'explore-edit', hash: '#' + page.path }"><Icon icon="radix-icons:pencil-1" /></NuxtLink></div></td>
                                    </tr>
                                </DialogRoot>
                            </tbody>
                        </table>
                    </div>
                </Collapsible>
            </div>
        </div>
    </div>
</template>