<script setup lang="ts">
import { ImagePlus, X } from '@lucide/vue';
import { computed, onBeforeUnmount, ref } from 'vue';
import MediaPreviewDialog from '@/components/media/MediaPreviewDialog.vue';
import { Button } from '@/components/ui/button';
import { Field, FieldDescription, FieldError, FieldLabel } from '@/components/ui/field';
import { Input } from '@/components/ui/input';
import {
    createMediaUploadState,
    mediaCount,
} from '@/types/media';
import type { MediaUploadState } from '@/types/media';

const props = withDefaults(
    defineProps<{
        id: string;
        label: string;
        description?: string;
        maxFiles?: number;
        required?: boolean;
        errors?: string[];
    }>(),
    {
        maxFiles: 5,
        required: false,
        errors: () => [],
    },
);

const state = defineModel<MediaUploadState>({
    default: () => createMediaUploadState(),
});

const previewUrls = ref<string[]>([]);
const previewOpen = ref(false);
const previewUrl = ref<string | null>(null);

const remainingSlots = computed(() => props.maxFiles - mediaCount(state.value));
const canAddMore = computed(() => remainingSlots.value > 0);

const visibleExisting = computed(() =>
    state.value.existing.filter((item) => !state.value.removeIds.includes(item.id)),
);

function onFileChange(event: Event) {
    const input = event.target as HTMLInputElement;
    const files = Array.from(input.files ?? []);

    if (files.length === 0) {
        return;
    }

    const allowed = files.slice(0, remainingSlots.value);

    allowed.forEach((file) => {
        state.value.newFiles.push(file);
        previewUrls.value.push(URL.createObjectURL(file));
    });

    input.value = '';
}

function removeExisting(id: number) {
    if (!state.value.removeIds.includes(id)) {
        state.value.removeIds = [...state.value.removeIds, id];
    }
}

function removeNew(index: number) {
    const url = previewUrls.value[index];

    if (url) {
        URL.revokeObjectURL(url);
    }

    previewUrls.value.splice(index, 1);
    state.value.newFiles.splice(index, 1);
}

function openPreview(url: string) {
    previewUrl.value = url;
    previewOpen.value = true;
}

onBeforeUnmount(() => {
    previewUrls.value.forEach((url) => URL.revokeObjectURL(url));
});
</script>

<template>
    <Field>
        <FieldLabel :for="id" :required="required">
            {{ label }}
        </FieldLabel>
        <FieldDescription v-if="description">
            {{ description }}
        </FieldDescription>
        <FieldDescription v-else-if="maxFiles > 1">
            Maks. {{ maxFiles }} gambar. Gambar pertama akan dijadikan thumbnail.
        </FieldDescription>

        <div class="space-y-3">
            <div v-if="visibleExisting.length > 0 || previewUrls.length > 0" class="flex flex-wrap items-center gap-2">
                <button v-for="item in visibleExisting" :key="`existing-${item.id}`" type="button"
                    class="group relative size-14 overflow-hidden rounded-md border bg-muted/30"
                    @click="openPreview(item.url)">
                    <img :src="item.thumb_url" :alt="label" class="size-full object-cover">
                    <Button type="button" variant="secondary" size="icon"
                        class="absolute top-0.5 right-0.5 size-5 opacity-0 transition-opacity group-hover:opacity-100"
                        @click.stop="removeExisting(item.id)">
                        <X class="size-3" />
                    </Button>
                </button>

                <button v-for="(url, index) in previewUrls" :key="`new-${index}`" type="button"
                    class="group relative size-14 overflow-hidden rounded-md border bg-muted/30"
                    @click="openPreview(url)">
                    <img :src="url" :alt="label" class="size-full object-cover">
                    <Button type="button" variant="secondary" size="icon"
                        class="absolute top-0.5 right-0.5 size-5 opacity-0 transition-opacity group-hover:opacity-100"
                        @click.stop="removeNew(index)">
                        <X class="size-3" />
                    </Button>
                </button>
            </div>

            <div v-if="canAddMore" class="flex items-center gap-2">
                <Input :id="id" type="file" accept="image/*" :multiple="maxFiles > 1" class="max-w-md cursor-pointer"
                    @change="onFileChange" />
                <ImagePlus class="text-muted-foreground size-5 shrink-0" />
            </div>
        </div>

        <FieldError :errors="errors" />

        <MediaPreviewDialog v-model:open="previewOpen" :url="previewUrl" :title="label" />
    </Field>
</template>
