import { BookMarked, GripVertical, UploadCloud, X } from 'lucide-react';
import { useState } from 'react';
import { MediaPickerDialog } from '@/components/admin/media-picker-dialog';
import type { MediaPickerItem } from '@/components/admin/media-picker-dialog';
import { Label } from '@/components/ui/label';

type GalleryExisting = { id: number; url: string };

type GalleryChange = {
    /** File upload baru (dikirim sebagai `gallery[]`). */
    files: File[];
    /** ID media dari library (dikirim sebagai `gallery_media_ids[]`). */
    mediaIds: number[];
    /** ID galeri existing yang dihapus (dikirim sebagai `gallery_removed[]`). */
    removedIds: number[];
    /**
     * Urutan final seluruh galeri (dikirim sebagai `gallery_order[]`).
     * Token: `existing:{id}`, `upload:{index}`, atau `library:{id}`.
     */
    order: string[];
};

export type { GalleryExisting, GalleryChange };

type Props = {
    label: string;
    /** Item galeri existing (saat edit). */
    existing: GalleryExisting[];
    /** Batas maksimal total gambar (existing yang dipertahankan + upload + library). */
    max?: number;
    onChange: (change: GalleryChange) => void;
};

type GalleryEntry =
    | { kind: 'existing'; key: string; id: number; src: string; name: string }
    | {
          kind: 'upload';
          key: string;
          file: File;
          src: string;
          name: string;
          stats?: { original: string; compressed: string; savings: number };
      }
    | { kind: 'library'; key: string; id: number; src: string; name: string };

const initialExistingKeys = (existing: GalleryExisting[]) =>
    existing.map((item) => `existing-${item.id}`);

/**
 * Galeri multi-pilih reusable — pola sama di semua modul: upload file baru,
 * pilih gambar dari Library Media (mode multi), hapus item (existing ditandai
 * untuk dihapus saat submit). Seluruh item tampil dalam satu list dan bisa
 * diurut ulang via drag & drop sebelum disimpan.
 *
 * Kompresi: gambar otomatis dikompresi sebelum upload (max 1920px, quality 80%).
 */
export default function GalleryPicker({
    label,
    existing,
    max = 12,
    onChange,
}: Props) {
    const [entries, setEntries] = useState<GalleryEntry[]>(() =>
        existing.map((item) => ({
            kind: 'existing' as const,
            key: `existing-${item.id}`,
            id: item.id,
            src: item.url,
            name: String(item.id),
        })),
    );
    const [pickerOpen, setPickerOpen] = useState(false);
    const [dragIndex, setDragIndex] = useState<number | null>(null);
    const [overIndex, setOverIndex] = useState<number | null>(null);

    const existingKeys = initialExistingKeys(existing);
    const remainingSlots = Math.max(0, max - entries.length);

    /** Bangun GalleryChange dari daftar urut saat ini. */
    const buildChange = (next: GalleryEntry[]): GalleryChange => {
        const files: File[] = [];
        const mediaIds: number[] = [];
        const order: string[] = [];

        for (const entry of next) {
            if (entry.kind === 'upload') {
                order.push(`upload:${files.length}`);
                files.push(entry.file);
            } else if (entry.kind === 'library') {
                order.push(`library:${entry.id}`);
                mediaIds.push(entry.id);
            } else {
                order.push(`existing:${entry.id}`);
            }
        }

        const keptKeys = new Set(
            next
                .filter((entry) => entry.kind === 'existing')
                .map((entry) => entry.key),
        );
        const removedIds = existingKeys
            .filter((key) => !keptKeys.has(key))
            .map((key) => Number(key.slice('existing-'.length)));

        return { files, mediaIds, removedIds, order };
    };

    const commit = (next: GalleryEntry[]) => {
        setEntries(next);
        onChange(buildChange(next));
    };

    /** Pilih dari Library Media (mode multi) → tambah ke galeri, max slot. */
    const handlePick = (media: MediaPickerItem | MediaPickerItem[]) => {
        const incoming = Array.isArray(media) ? media : [media];
        const accepted = incoming
            .slice(0, Math.max(0, remainingSlots))
            .filter(
                (item) =>
                    !entries.some(
                        (entry) =>
                            entry.kind === 'library' && entry.id === item.id,
                    ),
            );

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

        const added: GalleryEntry[] = accepted.map((item) => ({
            kind: 'library' as const,
            key: `library-${item.id}`,
            id: item.id,
            src: item.thumb_url ?? item.url,
            name: item.name,
        }));

        commit([...entries, ...added]);
        setPickerOpen(false);
    };

    const removeEntry = (key: string) => {
        commit(entries.filter((entry) => entry.key !== key));
    };

    /** Pindahkan item dari index `from` ke index `to` (drag & drop). */
    const moveEntry = (from: number, to: number) => {
        if (from === to) {
            return;
        }

        const next = [...entries];
        const [moved] = next.splice(from, 1);

        next.splice(to, 0, moved);
        commit(next);
    };

    return (
        <div className="space-y-2">
            <Label>{label}</Label>

            {entries.length > 0 ? (
                <>
                    <div className="grid grid-cols-4 gap-2 sm:grid-cols-6 lg:grid-cols-8">
                        {entries.map((entry, index) => (
                            <div
                                key={entry.key}
                                draggable
                                onDragStart={(event) => {
                                    setDragIndex(index);
                                    event.dataTransfer.effectAllowed = 'move';
                                    event.dataTransfer.setData(
                                        'text/plain',
                                        String(index),
                                    );
                                }}
                                onDragOver={(event) => {
                                    event.preventDefault();
                                    event.dataTransfer.dropEffect = 'move';

                                    if (overIndex !== index) {
                                        setOverIndex(index);
                                    }
                                }}
                                onDragLeave={() => {
                                    if (overIndex === index) {
                                        setOverIndex(null);
                                    }
                                }}
                                onDrop={(event) => {
                                    event.preventDefault();
                                    const from =
                                        dragIndex ??
                                        Number(
                                            event.dataTransfer.getData(
                                                'text/plain',
                                            ),
                                        );

                                    setDragIndex(null);
                                    setOverIndex(null);
                                    moveEntry(from, index);
                                }}
                                onDragEnd={() => {
                                    setDragIndex(null);
                                    setOverIndex(null);
                                }}
                                className={`group relative aspect-square cursor-grab overflow-hidden rounded-md border bg-muted active:cursor-grabbing ${
                                    dragIndex === index ? 'opacity-40' : ''
                                } ${overIndex === index ? 'ring-2 ring-primary' : ''}`}
                            >
                                <img
                                    src={entry.src}
                                    alt={entry.name}
                                    draggable={false}
                                    className="size-full object-cover"
                                />

                                {/* Grip — indikator bisa di-drag */}
                                <span className="absolute top-1 left-1 rounded bg-black/50 p-0.5 text-white opacity-0 transition-opacity group-hover:opacity-100">
                                    <GripVertical className="size-3.5" />
                                </span>

                                {/* Nomor urut */}
                                <span className="absolute bottom-1 left-1 rounded bg-black/50 px-1.5 text-[10px] font-semibold text-white">
                                    {index + 1}
                                </span>

                                {/* Penanda item dari Library Media */}
                                {entry.kind === 'library' && (
                                    <span className="absolute top-1 left-1/2 flex -translate-x-1/2 items-center gap-0.5 rounded-full bg-primary px-1.5 py-0.5 text-[9px] font-semibold text-primary-foreground shadow-sm">
                                        <BookMarked className="size-2.5" />
                                        Library
                                    </span>
                                )}

                                {/* Compression stats badge */}
                                {entry.kind === 'upload' && entry.stats && (
                                    <span className="absolute right-1 bottom-1 rounded bg-green-500/90 px-1 py-0.5 text-[8px] font-semibold text-white">
                                        -{entry.stats.savings}%
                                    </span>
                                )}

                                <button
                                    type="button"
                                    onClick={() => removeEntry(entry.key)}
                                    aria-label="Hapus galeri"
                                    className="absolute top-1 right-1 rounded-full bg-destructive p-1 text-white opacity-0 transition-opacity group-hover:opacity-100"
                                >
                                    <X className="size-3.5" />
                                </button>
                            </div>
                        ))}
                        {remainingSlots > 0 && (
                            <button
                                type="button"
                                onClick={() => setPickerOpen(true)}
                                className="flex aspect-square flex-col items-center justify-center gap-1 rounded-md border-2 border-dashed text-center text-xs text-muted-foreground transition-colors hover:border-primary/50 hover:bg-muted/50 focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-none"
                            >
                                <UploadCloud className="size-5" />
                                <span>Pilih media</span>
                            </button>
                        )}
                    </div>
                    <p className="text-xs text-muted-foreground">
                        Seret gambar untuk mengatur urutan — nomor di pojok kiri
                        bawah menunjukkan posisi tampil.
                    </p>
                </>
            ) : (
                <button
                    type="button"
                    disabled={remainingSlots <= 0}
                    onClick={() => setPickerOpen(true)}
                    className="flex min-h-28 w-full flex-col items-center justify-center gap-2 rounded-md border-2 border-dashed text-sm text-muted-foreground transition-colors hover:border-primary/50 hover:bg-muted/50 focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-none disabled:pointer-events-none disabled:opacity-50"
                >
                    <UploadCloud className="size-7" />
                    <span>Klik untuk memilih dari Library Media</span>
                </button>
            )}

            <div className="flex flex-wrap items-center gap-2">
                <span className="text-xs text-muted-foreground">
                    {remainingSlots} slot tersisa
                </span>
            </div>

            {pickerOpen && (
                <MediaPickerDialog
                    open
                    multiple
                    onOpenChange={setPickerOpen}
                    onSelect={handlePick}
                />
            )}
        </div>
    );
}
