<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 { MediaItem, MediaUploadState } from '@/types/media';

const props = withDefaults(
    defineProps<{
        label?: string;
        description?: string;
        maxFiles?: number;
        required?: boolean;
        existing?: MediaItem[];
        errors?: string[];
        id?: string;
    }>(),
    {
        label: 'Foto',
        maxFiles: 1,
        required: false,
        existing: () => [],
        errors: () => [],
        id: 'media-upload',
    },
);

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);

    if (props.maxFiles === 1) {
        previewUrls.value.forEach((url) => URL.revokeObjectURL(url));
        previewUrls.value = [];
        state.value.newFiles = [];

        state.value.existing.forEach((item) => {
            if (!state.value.removeIds.includes(item.id)) {
                state.value.removeIds = [...state.value.removeIds, item.id];
            }
        });
    }

    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. Klik thumbnail untuk memperbesar.
        </FieldDescription>
        <FieldDescription v-else>
            Klik thumbnail untuk memperbesar.
        </FieldDescription>

        <div 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>

            <label v-if="canAddMore" :for="id"
                class="flex size-14 cursor-pointer flex-col items-center justify-center gap-1 rounded-md border border-dashed text-muted-foreground hover:bg-muted/30">
                <ImagePlus class="size-4" />
                <span class="text-[10px]">Tambah</span>
                <Input :id="id" type="file" accept="image/*" :multiple="maxFiles > 1" class="sr-only"
                    @change="onFileChange" />
            </label>
        </div>

        <FieldError :errors="errors" />

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