<script setup>
    import { computed, onBeforeUnmount, ref, watch } from 'vue';
    import { Cropper } from 'vue-advanced-cropper';
    import 'vue-advanced-cropper/dist/style.css';

    const props = defineProps({
        modelValue: {
            type: Boolean,
            default: false,
        },
        title: {
            type: String,
            default: 'Edit image',
        },
        confirmLabel: {
            type: String,
            default: 'Save image',
        },
        aspectRatio: {
            type: Number,
            default: 1,
        },
        uploadLabel: {
            type: String,
            default: 'Choose image',
        },
        isSaving: {
            type: Boolean,
            default: false,
        },
        initialUrl: {
            type: String,
            default: '',
        },
        sourceLabel: {
            type: String,
            default: 'Image URL',
        },
        loadLabel: {
            type: String,
            default: 'Load URL',
        },
    });

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

    const cropper = ref(null);
    const imageUrl = ref(null);
    const remoteUrl = ref('');
    const error = ref(null);

    const isReady = computed(() => Boolean(imageUrl.value));

    function closeDialog() {
        emit('update:modelValue', false);
    }

    function revokeImageUrl() {
        if (imageUrl.value?.startsWith('blob:')) {
            URL.revokeObjectURL(imageUrl.value);
        }
    }

    function resetState() {
        revokeImageUrl();
        imageUrl.value = props.initialUrl || null;
        remoteUrl.value = props.initialUrl || '';
        error.value = null;
    }

    function onFileSelected(value) {
        const file = Array.isArray(value) ? value[0] : value;

        if (!file) {
            return;
        }

        revokeImageUrl();
        imageUrl.value = URL.createObjectURL(file);
        error.value = null;
    }

    function loadImageFromUrl() {
        if (!remoteUrl.value) {
            error.value = 'Enter an image URL before loading it.';
            return;
        }

        revokeImageUrl();
        imageUrl.value = remoteUrl.value;
        error.value = null;
    }

    function zoom(factor) {
        cropper.value?.zoom(factor);
    }

    function rotate(angle) {
        cropper.value?.rotate(angle);
    }

    async function saveCrop() {
        const result = cropper.value?.getResult();
        const canvas = result?.canvas;

        if (!canvas) {
            error.value = 'Select an image before saving.';
            return;
        }

        const blob = await new Promise(resolve => canvas.toBlob(resolve, 'image/png', 0.92));

        if (!blob) {
            error.value = 'The edited image could not be prepared.';
            return;
        }

        const file = new File([blob], 'cropped-image.png', { type: 'image/png' });
        const dataUrl = canvas.toDataURL('image/png', 0.92);
        await emit('save', { file, dataUrl, sourceUrl: remoteUrl.value || imageUrl.value });
    }

    watch(
        () => props.modelValue,
        isOpen => {
            if (isOpen) {
                resetState();
            } else {
                resetState();
            }
        }
    );

    onBeforeUnmount(() => {
        revokeImageUrl();
    });
</script>

<template>
    <v-dialog
        :model-value="modelValue"
        max-width="920"
        @update:model-value="emit('update:modelValue', $event)"
    >
        <div class="cropper-card">
            <div class="cropper-header">
                <div>
                    <div class="cropper-eyebrow">Image editor</div>
                    <h2>{{ title }}</h2>
                </div>
                <v-btn variant="text" :ripple="false"
                    class="plain-button"
                    :disabled="isSaving"
                    @click="closeDialog"
                >
                    Close
                </v-btn>
            </div>

            <div class="cropper-actions">
                <v-file-input
                    :label="uploadLabel"
                    accept="image/*"
                    :disabled="isSaving"
                    density="compact"
                    variant="outlined"
                    hide-details
                    @update:model-value="onFileSelected"
                />
                <div class="url-controls">
                    <v-text-field
                        v-model="remoteUrl"
                        type="url"
                        class="url-input"
                        :placeholder="sourceLabel"
                        :disabled="isSaving"
                        density="compact"
                        variant="outlined"
                        hide-details
                    />
                    <v-btn variant="text" :ripple="false"
                        class="action-button secondary"
                        :disabled="isSaving"
                        @click="loadImageFromUrl"
                    >
                        {{ loadLabel }}
                    </v-btn>
                </div>
                <v-btn variant="text" :ripple="false"
                    class="action-button secondary"
                    :disabled="!isReady || isSaving"
                    @click="zoom(1.15)"
                >
                    Zoom in
                </v-btn>
                <v-btn variant="text" :ripple="false"
                    class="action-button secondary"
                    :disabled="!isReady || isSaving"
                    @click="zoom(0.85)"
                >
                    Zoom out
                </v-btn>
                <v-btn variant="text" :ripple="false"
                    class="action-button secondary"
                    :disabled="!isReady || isSaving"
                    @click="rotate(-90)"
                >
                    Rotate left
                </v-btn>
                <v-btn variant="text" :ripple="false"
                    class="action-button secondary"
                    :disabled="!isReady || isSaving"
                    @click="rotate(90)"
                >
                    Rotate right
                </v-btn>
            </div>

            <div
                v-if="error"
                class="error-message"
            >
                {{ error }}
            </div>

            <div
                v-if="isReady"
                class="cropper-stage"
            >
                <Cropper
                    ref="cropper"
                    :src="imageUrl"
                    :stencil-props="{ aspectRatio }"
                    image-restriction="stencil"
                />
            </div>
            <div
                v-else
                class="empty-state"
            >
                Choose an image to crop and upload.
            </div>

            <div class="footer-actions">
                <v-btn variant="text" :ripple="false"
                    class="action-button secondary"
                    :disabled="isSaving"
                    @click="closeDialog"
                >
                    Cancel
                </v-btn>
                <v-btn variant="text" :ripple="false"
                    class="action-button"
                    :disabled="!isReady || isSaving"
                    @click="saveCrop"
                >
                    <v-progress-circular
                        v-if="isSaving"
                        indeterminate
                        :size="16"
                        :width="2"
                    />
                    <span>{{ isSaving ? 'Saving...' : confirmLabel }}</span>
                </v-btn>
            </div>
        </div>
    </v-dialog>
</template>

<style scoped>
@reference "@/assets/main.css";
    .cropper-card {
        @apply flex flex-col gap-5 rounded-[1.75rem] border p-5;
        background: var(--app-surface-raised);
        border-color: var(--app-border-subtle);
    }

    .cropper-header {
        @apply flex items-start justify-between gap-4;
    }

    .cropper-eyebrow {
        @apply text-xs font-bold uppercase tracking-[0.24em];
        color: var(--app-color-on-tertiary);
    }

    .cropper-header h2 {
        @apply mt-2 text-2xl font-black;
        color: var(--app-color-on-surface);
    }

    .cropper-actions,
    .footer-actions {
        @apply flex flex-wrap gap-3;
    }

    .url-controls {
        @apply flex min-w-full flex-wrap gap-3 md:min-w-0 md:flex-1;
    }

    .url-input {
        @apply min-w-0 flex-1 rounded-full border px-4 py-3 text-sm;
        border-color: var(--app-border-subtle);
        background: rgba(255, 255, 255, 0.92);
        color: var(--app-color-on-surface);
    }

    .footer-actions {
        @apply justify-end;
    }

    .action-button,
    .plain-button {
        @apply inline-flex items-center justify-center gap-2 rounded-full px-4 py-3 text-sm font-bold transition;
    }

    .action-button {
        background: var(--app-color-on-surface);
        color: var(--app-color-on-primary);
    }

    .action-button.secondary,
    .plain-button {
        background: rgba(255, 255, 255, 0.84);
        color: var(--app-color-on-surface);
        border: 1px solid var(--app-border-subtle);
    }

    .cropper-stage {
        @apply overflow-hidden rounded-[1.5rem] border;
        height: 28rem;
        border-color: var(--app-border-subtle);
        background: var(--app-color-on-primary);
    }

    .empty-state,
    .error-message {
        @apply rounded-[1.25rem] border p-4 text-sm;
    }

    .empty-state {
        border-color: var(--app-border-subtle);
        color: var(--app-text-muted);
        background: rgba(255, 250, 242, 0.9);
    }

    .error-message {
        border-color: rgba(185, 28, 28, 0.12);
        color: var(--app-danger-muted);
        background: rgba(254, 226, 226, 0.75);
    }

    .hidden-input {
        display: none;
    }
</style>
