<script setup>
import { ref, computed } from 'vue';
import { CloudArrowUpIcon, XMarkIcon, DocumentIcon } from '@heroicons/vue/24/outline';

const props = defineProps({
    modelValue: {
        type: [File, null],
        default: null,
    },
    label: {
        type: String,
        default: 'Dosya Yükle',
    },
    required: {
        type: Boolean,
        default: false,
    },
    help: {
        type: String,
        default: '',
    },
     error: {
        type: String,
        default: '',
    },
    accept: {
        type: String,
        default: 'image/*,application/pdf', // Default accepted file types
    },
    id: {
        type: String,
        default: () => `dropzone-${Math.random().toString(36).substring(2, 15)}`,
    },
    disabled: {
        type: Boolean,
        default: false,
    },
    maxSizeMb: {
        type: Number,
        default: null,
    },
    maxHeight: {
        type: Number,
        default: null,
    },
});

const emit = defineEmits(['update:modelValue']);

const fileInput = ref(null);
const isDragging = ref(false);
const internalError = ref('');

const displayError = computed(() => props.error || internalError.value);

const componentValue = computed({
    get: () => props.modelValue,
    set: (value) => emit('update:modelValue', value),
});

const openFileDialog = () => {
    if (props.disabled) return;
    fileInput.value?.click();
};

const loadImage = (file) => {
    return new Promise((resolve, reject) => {
        const objectUrl = URL.createObjectURL(file);
        const image = new Image();
        image.onload = () => {
            URL.revokeObjectURL(objectUrl);
            resolve(image);
        };
        image.onerror = () => {
            URL.revokeObjectURL(objectUrl);
            reject(new Error('Görsel okunamadı.'));
        };
        image.src = objectUrl;
    });
};

const isResizableImage = (file) => file.type.startsWith('image/') && file.type !== 'image/svg+xml';

const resizeImageToMaxHeight = async (file, maxHeight) => {
    const image = await loadImage(file);
    if (image.naturalHeight <= maxHeight) {
        return file;
    }

    const ratio = maxHeight / image.naturalHeight;
    const canvas = document.createElement('canvas');
    canvas.width = Math.round(image.naturalWidth * ratio);
    canvas.height = maxHeight;
    canvas.getContext('2d').drawImage(image, 0, 0, canvas.width, canvas.height);

    const mimeType = file.type === 'image/png' ? 'image/png' : 'image/jpeg';
    const blob = await new Promise((resolve) => canvas.toBlob(resolve, mimeType, 0.92));

    return blob ? new File([blob], file.name, { type: mimeType, lastModified: Date.now() }) : file;
};

const setFile = async (file) => {
    internalError.value = '';
    let processedFile = file;

    if (props.maxHeight && isResizableImage(file)) {
        try {
            processedFile = await resizeImageToMaxHeight(file, props.maxHeight);
        } catch (e) {
            internalError.value = 'Görsel okunamadı.';
            return;
        }
    }

    if (props.maxSizeMb && processedFile.size > props.maxSizeMb * 1024 * 1024) {
        internalError.value = `Dosya boyutu en fazla ${props.maxSizeMb} MB olabilir.`;
        return;
    }

    componentValue.value = processedFile;
};

const handleFileChange = async (event) => {
    const files = event.target.files;
    if (files && files.length > 0) {
        await setFile(files[0]);
    }
    if (fileInput.value) {
        fileInput.value.value = '';
    }
};

const acceptedTypesDisplay = computed(() => {
    if (!props.accept) return '';
    const typeMap = {
        'application/pdf': 'PDF',
        'image/jpeg': 'JPG',
        'image/jpg': 'JPG',
        'image/png': 'PNG',
        'image/*': 'Resim',
    };

    const displayTypes = new Set();
    props.accept.split(',')
        .map(type => type.trim().toLowerCase())
        .forEach(type => {
            if (typeMap[type]) {
                displayTypes.add(typeMap[type]);
            } else if (type.startsWith('image/')) {
                displayTypes.add('Resim');
            }
        });

    return Array.from(displayTypes).join(', ');
});

const handleDrop = async (event) => {
    if (props.disabled) return;
    isDragging.value = false;
    const files = event.dataTransfer?.files;
    if (files && files.length > 0) {
        const file = files[0];
        if (props.accept.includes('*/*') || props.accept.split(',').some(type => file.type.match(type.trim().replace('*', '.*')))) {
            await setFile(file);
        } else {
            internalError.value = `Geçersiz dosya türü: ${file.type}.`;
        }
    }
};

const removeFile = () => {
    internalError.value = '';
    componentValue.value = null;
};

const dragOver = () => {
    if (props.disabled) return;
    isDragging.value = true;
};

const dragLeave = () => {
    isDragging.value = false;
};

const formatBytes = (bytes, decimals = 2) => {
    if (!+bytes) return '0 Bytes';
    const k = 1024;
    const dm = decimals < 0 ? 0 : decimals;
    const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
};
</script>

<template>
    <div class="flex flex-col gap-2">
        <label
            :for="id"
            :class="`${
                disabled ? 'text-gray-400' : 'text-gray-900 dark:text-white'
            } block text-sm font-medium`"
        >
            {{ label }} <span v-if="required" class="text-red-600">*</span>
        </label>

        <div
            :class="[
                'flex justify-center items-center w-full px-6 py-10 border-2 border-dashed rounded-lg cursor-pointer transition-colors',
                disabled ? 'bg-gray-200 border-gray-300 cursor-not-allowed' :
                isDragging ? 'border-primary-500 bg-primary-50' :
                displayError ? 'border-red-400 bg-red-50' :
                'border-gray-300 hover:border-gray-400 bg-gray-50 dark:border-gray-600 dark:hover:border-gray-500 dark:bg-gray-700 dark:hover:bg-gray-600',
            ]"
            @click="openFileDialog"
            @dragover.prevent="dragOver"
            @dragleave.prevent="dragLeave"
            @drop.prevent="handleDrop"
        >
            <input
                :id="id"
                ref="fileInput"
                type="file"
                class="hidden"
                :accept="accept"
                :disabled="disabled"
                @change="handleFileChange"
            />

            <div v-if="!componentValue" class="text-center">
                <CloudArrowUpIcon class="mx-auto h-12 w-12 text-gray-400" />
                <p class="mt-2 text-sm text-gray-600 dark:text-gray-400">
                    <span class="font-semibold">Dosya seçmek için tıklayın</span> veya sürükleyip bırakın
                </p>
                <p class="mt-1 text-xs text-gray-500 dark:text-gray-500">
                    ({{ acceptedTypesDisplay }})
                </p>
            </div>

            <div v-else class="text-center w-full">
                 <div class="flex items-center justify-center gap-2 bg-gray-100 dark:bg-gray-600 p-3 rounded-md border border-gray-300 dark:border-gray-500 relative">
                    <DocumentIcon class="h-8 w-8 text-gray-500 dark:text-gray-400 flex-shrink-0" />
                    <div class="text-left overflow-hidden">
                        <p class="text-sm font-medium text-gray-900 dark:text-gray-200 truncate" :title="componentValue.name">
                            {{ componentValue.name }}
                        </p>
                        <p class="text-xs text-gray-500 dark:text-gray-400">
                            {{ formatBytes(componentValue.size) }}
                        </p>
                    </div>
                    <button
                        v-if="!disabled"
                        type="button"
                        @click.stop="removeFile"
                        class="absolute top-1 right-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 p-1 rounded-full bg-white dark:bg-gray-700 hover:bg-gray-100 dark:hover:bg-gray-500"
                        aria-label="Remove file"
                    >
                        <XMarkIcon class="h-4 w-4" />
                    </button>
                </div>
            </div>
        </div>
         <div v-if="help" class="text-sm text-gray-600">
            {{ help }}
        </div>
        <div v-if="displayError" class="text-sm text-red-600">
            {{ displayError }}
        </div>
    </div>
</template>