<script setup lang="ts">
import { useDropZone } from '@vueuse/core';
import { toast } from 'vue-sonner';
import DirectoryListContextMenu from '~/components/DirectoryListContextMenu.vue';
import RenameEntryDialog from '~/components/actions/RenameEntryDialog.vue';
import { fetchFile, getWarrenDirectory, warrenRm } from '~/lib/api/warrens';
import type { DirectoryEntry } from '~/shared/types';

definePageMeta({
    middleware: ['authenticated'],
});

const warrenStore = useWarrenStore();
const loadingIndicator = useLoadingIndicator();
const uploadStore = useUploadStore();
const warrenPath = computed(() => useWarrenPath());

const dropZoneRef = ref<HTMLElement>();

const dropZone = useDropZone(dropZoneRef, {
    onDrop,
    multiple: true,
});

if (warrenStore.current == null) {
    await navigateTo({
        path: '/warrens',
    });
}

useAsyncData(
    'current-directory',
    async () => {
        if (warrenStore.current == null) {
            return {
                files: [],
                parent: null,
            };
        }

        loadingIndicator.start();
        warrenStore.loading = true;

        const { files, parent } = await getWarrenDirectory(
            warrenStore.current.warrenId,
            warrenStore.current.path
        );

        warrenStore.loading = false;
        loadingIndicator.finish();

        warrenStore.setCurrentWarrenEntries(files, parent);
    },
    { watch: [warrenPath] }
);

function onDrop(files: File[] | null, e: DragEvent) {
    if (files == null) {
        return;
    }

    e.preventDefault();

    if (warrenStore.current == null) {
        toast.warning('Upload', {
            description: 'Enter a warren before attempting to upload files',
        });
        return;
    }

    const added = uploadStore.addFiles(
        warrenStore.current.warrenId,
        warrenStore.current.path,
        files
    );

    if (added) {
        uploadStore.dialogOpen = true;
    }
}

/**
 * Returns whether the event was a selection event (so further execution can be stopped)
 */
function handleEntrySelectionClick(
    selectedEntry: DirectoryEntry,
    event: MouseEvent
): boolean {
    if (!event.ctrlKey && !event.shiftKey) {
        return false;
    }

    if (warrenStore.current == null || warrenStore.current.dir == null) {
        return true;
    }

    if (event.ctrlKey || (event.shiftKey && warrenStore.selection.size === 0)) {
        if (
            warrenStore.toggleSelection(selectedEntry) ||
            warrenStore.selectionRangeAnchor == null
        ) {
            warrenStore.setSelectedRangeAnchor(selectedEntry);
        }
        return true;
    }

    if (!event.shiftKey) {
        return false;
    }

    const anchor = warrenStore.selectionRangeAnchor;

    if (anchor == null) {
        return true;
    }

    const entries = warrenStore.current.dir.entries.toSorted((a, b) =>
        a.name.localeCompare(b.name)
    );
    const anchorIndex = entries.findIndex(
        (entry) => entry.name === anchor.name
    );

    const clickedIndex = entries.findIndex(
        (e) => e.name === selectedEntry.name
    );

    // NOTE: This has to change if there are ever multiple sorting orders

    const targetSelection = [];

    if (clickedIndex > anchorIndex) {
        for (let i = anchorIndex; i <= clickedIndex; i++) {
            targetSelection.push(entries[i]);
        }
    } else {
        for (let i = clickedIndex; i <= anchorIndex; i++) {
            targetSelection.push(entries[i]);
        }
    }

    warrenStore.setSelection(targetSelection);

    return true;
}

async function onEntryClicked(entry: DirectoryEntry, event: MouseEvent) {
    event.stopPropagation();

    if (warrenStore.loading || warrenStore.current == null) {
        return;
    }

    if (handleEntrySelectionClick(entry, event)) {
        return;
    }

    if (entry.fileType === 'directory') {
        warrenStore.addToCurrentWarrenPath(entry.name);
        return;
    }

    if (entry.mimeType == null) {
        return;
    }

    if (entry.mimeType.startsWith('image/')) {
        const result = await fetchFile(
            warrenStore.current.warrenId,
            warrenStore.current.path,
            entry.name
        );
        if (result.success) {
            const url = URL.createObjectURL(result.data);
            warrenStore.imageViewer.src = url;
        }
    }
}

function onEntryDownload(entry: DirectoryEntry) {
    if (warrenStore.current == null) {
        return;
    }

    let downloadName: string;
    let downloadApiUrl: string;

    const targets = getTargetsFromSelection(entry, warrenStore.selection);
    if (targets.length === 1) {
        downloadName =
            entry.fileType === 'directory'
                ? `${targets[0].name}.zip`
                : targets[0].name;
        downloadApiUrl = getApiUrl(
            `warrens/files/cat?warrenId=${warrenStore.current.warrenId}&paths=${joinPaths(warrenStore.current.path, targets[0].name)}`
        );
    } else {
        downloadName = 'download.zip';
        const paths = targets
            .map((entry) => joinPaths(warrenStore.current!.path, entry.name))
            .join(':');

        downloadApiUrl = getApiUrl(
            `warrens/files/cat?warrenId=${warrenStore.current.warrenId}&paths=${paths}`
        );
    }

    downloadFile(downloadName, downloadApiUrl);
}
async function onEntryDelete(entry: DirectoryEntry, force: boolean) {
    if (warrenStore.current == null) {
        return;
    }

    const targets = getTargetsFromSelection(entry, warrenStore.selection).map(
        (entry) => joinPaths(warrenStore.current!.path, entry.name)
    );

    await warrenRm(warrenStore.current.warrenId, targets, force);
}

function onBack() {
    warrenStore.backCurrentPath();
}

function onParentClick() {
    warrenStore.clearSelection();
}
</script>

<template>
    <div ref="dropZoneRef" class="grow" @click="onParentClick">
        <DirectoryListContextMenu class="w-full grow">
            <DirectoryList
                v-if="
                    warrenStore.current != null &&
                    warrenStore.current.dir != null
                "
                :is-over-drop-zone="
                    dropZone.isOverDropZone.value &&
                    dropZone.files.value != null
                "
                :entries="warrenStore.current.dir.entries"
                :parent="warrenStore.current.dir.parent"
                @entry-click="onEntryClicked"
                @entry-download="onEntryDownload"
                @entry-delete="onEntryDelete"
                @back="onBack"
            />
        </DirectoryListContextMenu>
        <RenameEntryDialog />
    </div>
</template>
