"use client";

import Breadcrumbs from "@/components/user/components/Breadcrumbs";
import {
    deleteChapter,
    deleteChapterImage,
    getChapterDetail,
    publishChapter,
    reorderChapterImages,
    updateChapter,
    uploadChapterImages,
} from "@/libs/axios/modules/chapter";
import { getMyKomikDetail } from "@/libs/axios/modules/komik";
import { ChapterImage, ChapterModel, PostModel } from "@/models";
import {
    closestCenter,
    DndContext,
    DragEndEvent,
    KeyboardSensor,
    PointerSensor,
    useSensor,
    useSensors,
} from "@dnd-kit/core";
import {
    arrayMove,
    rectSortingStrategy,
    SortableContext,
    sortableKeyboardCoordinates,
    useSortable,
} from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { format } from "date-fns";
import { AnimatePresence, motion } from "framer-motion";
import {
    Calendar,
    ChevronDown,
    GripVertical,
    ImagePlus,
    Send,
    Trash2,
    X,
} from "lucide-react";
import { useRouter } from "next/navigation";
import { useCallback, useEffect, useRef, useState } from "react";
import { toast } from "react-toastify";

const MAX_IMAGES = 10;
const MIN_COINS = 4;
const MAX_COINS = 20;

function SortableImageThumb({
    id,
    url,
    index,
    onRemove,
}: {
    id: number;
    url: string;
    index: number;
    onRemove: () => void;
}) {
    const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id });

    const style: React.CSSProperties = {
        transform: CSS.Transform.toString(transform),
        transition,
        opacity: isDragging ? 0.6 : 1,
        zIndex: isDragging ? 10 : undefined,
    };

    return (
        <div
            ref={setNodeRef}
            style={style}
            {...attributes}
            {...listeners}
            className="relative group rounded-xl overflow-hidden border border-slate-200 bg-slate-50 cursor-grab active:cursor-grabbing shadow-sm touch-none"
        >
            <img
                src={url}
                alt={`Halaman ${index + 1}`}
                className="w-full aspect-3/4 object-cover pointer-events-none select-none"
            />
            <span className="absolute top-2 left-2 w-6 h-6 rounded-full bg-black/60 text-white text-xs font-semibold flex items-center justify-center">
                {index + 1}
            </span>
            <div className="absolute top-2 right-2 w-6 h-6 rounded-full bg-black/40 text-white/80 flex items-center justify-center pointer-events-none opacity-0 group-hover:opacity-100 transition-opacity">
                <GripVertical className="w-3.5 h-3.5" />
            </div>
            <button
                type="button"
                onClick={(e) => {
                    e.stopPropagation();
                    onRemove();
                }}
                onPointerDown={(e) => e.stopPropagation()}
                className="absolute bottom-2 right-2 w-6 h-6 rounded-full bg-black/60 text-white flex items-center justify-center hover:bg-red-500 transition-colors cursor-pointer z-10"
            >
                <X className="w-3.5 h-3.5" />
            </button>
        </div>
    );
}

function ConfirmModal({
    open,
    title,
    description,
    confirmLabel,
    danger,
    loading,
    onConfirm,
    onClose,
}: {
    open: boolean;
    title: string;
    description: string;
    confirmLabel: string;
    danger?: boolean;
    loading?: boolean;
    onConfirm: () => void;
    onClose: () => void;
}) {
    if (!open) return null;
    return (
        <AnimatePresence>
            {open && (
                <>
                    <motion.div
                        className="fixed inset-0 z-9999 bg-black/40 backdrop-blur-sm"
                        onClick={onClose}
                        initial={{ opacity: 0 }}
                        animate={{ opacity: 1 }}
                        exit={{ opacity: 0 }}
                    />
                    <div className="fixed inset-0 z-10000 flex items-center justify-center p-4">
                        <motion.div
                            className="bg-white rounded-2xl shadow-xl p-6 w-full max-w-sm flex flex-col gap-4"
                            initial={{ scale: 0.9, opacity: 0 }}
                            animate={{ scale: 1, opacity: 1 }}
                            exit={{ scale: 0.9, opacity: 0 }}
                            transition={{ type: "spring", stiffness: 300, damping: 25 }}
                        >
                            <div className="flex flex-col gap-1.5">
                                <p className="font-semibold text-slate-800">{title}</p>
                                <p className="text-sm text-slate-500">{description}</p>
                            </div>
                            <div className="flex gap-2 justify-end">
                                <button
                                    onClick={onClose}
                                    disabled={loading}
                                    className="px-4 py-2 text-sm text-slate-600 hover:bg-slate-100 rounded-xl transition-colors cursor-pointer"
                                >
                                    Batal
                                </button>
                                <button
                                    onClick={onConfirm}
                                    disabled={loading}
                                    className={`px-4 py-2 text-sm font-semibold rounded-xl transition-colors cursor-pointer
                                        ${danger
                                            ? "bg-red-500 text-white hover:bg-red-600"
                                            : "bg-primary-500 text-white hover:bg-primary-600"}
                                        ${loading ? "opacity-60 cursor-not-allowed" : ""}`}
                                >
                                    {loading ? "Memproses..." : confirmLabel}
                                </button>
                            </div>
                        </motion.div>
                    </div>
                </>
            )}
        </AnimatePresence>
    );
}

function OptionsDropdown({
    open,
    onPublish,
    onDelete,
    onClose,
}: {
    open: boolean;
    onPublish: () => void;
    onDelete: () => void;
    onClose: () => void;
}) {
    useEffect(() => {
        if (!open) return;
        const handler = (e: MouseEvent) => {
            const target = e.target as HTMLElement;
            if (!target.closest("[data-options-dropdown]")) onClose();
        };
        document.addEventListener("mousedown", handler);
        return () => document.removeEventListener("mousedown", handler);
    }, [open, onClose]);

    return (
        <div className="relative" data-options-dropdown>
            <AnimatePresence>
                {open && (
                    <motion.div
                        className="absolute right-0 top-full mt-2 bg-white rounded-2xl shadow-xl border border-slate-100 w-52 overflow-hidden z-50"
                        initial={{ opacity: 0, y: -8, scale: 0.96 }}
                        animate={{ opacity: 1, y: 0, scale: 1 }}
                        exit={{ opacity: 0, y: -8, scale: 0.96 }}
                        transition={{ duration: 0.15 }}
                    >
                        <button
                            onClick={onPublish}
                            className="w-full flex items-center gap-3 px-4 py-3 text-sm text-slate-700 hover:bg-slate-50 transition-colors cursor-pointer"
                        >
                            <Send className="w-4 h-4 text-slate-400" />
                            Publikasikan
                        </button>
                        <button
                            onClick={onDelete}
                            className="w-full flex items-center gap-3 px-4 py-3 text-sm text-red-600 hover:bg-red-50 transition-colors cursor-pointer"
                        >
                            <Trash2 className="w-4 h-4" />
                            Hapus Episode
                        </button>
                    </motion.div>
                )}
            </AnimatePresence>
        </div>
    );
}

interface Props {
    token: string;
    postId: string;
    chapterId: string;
}

export default function EditEpisodePage({ token, postId, chapterId }: Props) {
    const router = useRouter();

    const [komik, setKomik] = useState<PostModel | null>(null);
    const [chapter, setChapter] = useState<ChapterModel | null>(null);
    const [title, setTitle] = useState("");
    const [coins, setCoins] = useState<number | "">(MIN_COINS);
    const [publishedAt, setPublishedAt] = useState<Date>(new Date());
    const [images, setImages] = useState<ChapterImage[]>([]);

    const [loading, setLoading] = useState(true);
    const [saving, setSaving] = useState(false);
    const [deleting, setDeleting] = useState(false);
    const [publishing, setPublishing] = useState(false);
    const [uploadingImages, setUploadingImages] = useState(false);

    const [showOptions, setShowOptions] = useState(false);
    const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
    const [showPublishConfirm, setShowPublishConfirm] = useState(false);

    const dateRef = useRef<HTMLInputElement>(null);
    const fileInputRef = useRef<HTMLInputElement>(null);

    useEffect(() => {
        const load = async () => {
            try {
                const [{ data: komikRes }, { data: chapterRes }] = await Promise.all([
                    getMyKomikDetail(token, postId),
                    getChapterDetail(chapterId),
                ]);
                if (komikRes?.data) setKomik(komikRes.data);
                if (chapterRes?.data) {
                    const c = chapterRes.data;
                    setChapter(c);
                    setTitle(c.title);
                    setCoins(c.coins ?? MIN_COINS);
                    setPublishedAt(new Date(c.published_at));
                    setImages(c.images ?? []);
                }
            } catch (err) {
                console.error(err);
            } finally {
                setLoading(false);
            }
        };
        load();
    }, [chapterId, postId, token]);

    const coinsValid = typeof coins === "number" && coins >= MIN_COINS && coins <= MAX_COINS;
    const canSave = !!(title && publishedAt && coinsValid);

    const onPickImages = async (files: FileList | null) => {
        if (!files || files.length === 0) return;

        const incoming = Array.from(files);
        const remainingSlots = MAX_IMAGES - images.length;

        if (remainingSlots <= 0) {
            toast.error(`Maksimal ${MAX_IMAGES} gambar per episode`, { toastId: "max-images" });
            return;
        }

        const accepted = incoming.slice(0, remainingSlots);
        if (incoming.length > accepted.length) {
            toast.error(`Maksimal ${MAX_IMAGES} gambar per episode`, { toastId: "max-images" });
        }

        setUploadingImages(true);
        try {
            const { data: res } = await uploadChapterImages(token, chapterId, accepted);
            if (res?.data?.images) setImages(res.data.images);
        } catch (err: any) {
            toast.error(err?.data?.message ?? err.message ?? "Gagal upload gambar");
        } finally {
            setUploadingImages(false);
        }
    };

    const removeImage = async (mediaId: number) => {
        const prevImages = images;
        setImages((prev) => prev.filter((img) => img.id !== mediaId));
        try {
            const { data: res } = await deleteChapterImage(token, chapterId, mediaId);
            if (res?.data?.images) setImages(res.data.images);
        } catch (err: any) {
            setImages(prevImages);
            toast.error(err?.data?.message ?? err.message ?? "Gagal menghapus gambar");
        }
    };

    const onReorderImages = async (newOrder: ChapterImage[]) => {
        const prevImages = images;
        setImages(newOrder);
        try {
            await reorderChapterImages(token, chapterId, newOrder.map((img) => img.id));
        } catch (err: any) {
            setImages(prevImages);
            toast.error(err?.data?.message ?? err.message ?? "Gagal mengurutkan gambar");
        }
    };

    const sensors = useSensors(
        useSensor(PointerSensor, { activationConstraint: { distance: 8 } }),
        useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates })
    );

    const handleDragEnd = (event: DragEndEvent) => {
        const { active, over } = event;
        if (!over || active.id === over.id) return;

        const oldIndex = images.findIndex((img) => img.id === active.id);
        const newIndex = images.findIndex((img) => img.id === over.id);
        onReorderImages(arrayMove(images, oldIndex, newIndex));
    };

    const onSave = useCallback(async () => {
        if (!canSave || saving) return;
        setSaving(true);
        try {
            await updateChapter(token, {
                id: chapterId,
                title,
                post_id: postId,
                coins: coins as number,
                published_at: publishedAt,
            });
            toast.success("Episode berhasil diperbarui");
            router.back();
        } catch (err: any) {
            toast.error(err?.data?.message ?? err.message ?? "Terjadi kesalahan");
        } finally {
            setSaving(false);
        }
    }, [canSave, saving, title, chapterId, postId, coins, publishedAt, token, router]);

    const onDelete = useCallback(async () => {
        setDeleting(true);
        try {
            await deleteChapter(token, { id: chapterId });
            toast.success("Episode berhasil dihapus");
            router.back();
        } catch (err: any) {
            toast.error(err?.data?.message ?? err.message ?? "Terjadi kesalahan");
        } finally {
            setDeleting(false);
            setShowDeleteConfirm(false);
        }
    }, [token, chapterId, router]);

    const onPublish = useCallback(async () => {
        setPublishing(true);
        try {
            await publishChapter(token, {
                id: chapterId,
                title,
                post_id: postId,
                coins: coins as number,
                published_at: publishedAt,
            });
            toast.success("Episode berhasil dipublikasikan");
            router.back();
        } catch (err: any) {
            toast.error(err?.data?.message ?? err.message ?? "Terjadi kesalahan");
        } finally {
            setPublishing(false);
            setShowPublishConfirm(false);
        }
    }, [token, chapterId, title, postId, coins, publishedAt, router]);

    const toInputValue = (d: Date) => format(d, "yyyy-MM-dd'T'HH:mm");

    if (loading) {
        return (
            <div className="flex flex-col gap-6 w-full animate-pulse">
                <div className="h-8 bg-slate-100 rounded-xl w-64" />
                <div className="h-14 bg-slate-100 rounded-xl" />
                <div className="h-96 bg-slate-100 rounded-xl" />
            </div>
        );
    }

    return (
        <div className="flex flex-col gap-6 w-full">
            <Breadcrumbs
                title="Dashboard"
                subtitle={[
                    { title: "Daftar Komik", href: "/user/komik" },
                    { title: komik?.title || "", href: "/user/komik" },
                    { title: "Edit Episode", href: `/user/komik/chapter/${postId}/edit/${chapterId}` },
                ]}
                href="/user"
            />

            <div className="sticky top-0 z-10 bg-white border-b border-slate-100 rounded-t-xl shadow-sm">
                <div className="flex items-center justify-between px-4 py-3">
                    <div className="flex items-center gap-2">
                        <button
                            onClick={() => router.back()}
                            className="w-9 h-9 rounded-xl border border-slate-200 flex items-center justify-center hover:bg-slate-50 transition-colors cursor-pointer"
                        >
                            <svg className="w-4 h-4 text-slate-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
                            </svg>
                        </button>
                        <span className="text-base font-semibold text-slate-800">Edit Episode</span>
                    </div>

                    <div className="flex items-center gap-2">
                        <button
                            onClick={onSave}
                            disabled={!canSave || saving}
                            className={`px-4 py-1.5 rounded-full text-sm font-semibold border transition-all cursor-pointer
                                ${canSave && !saving
                                    ? "border-primary-500 text-primary-500 hover:bg-primary-50"
                                    : "border-slate-200 text-slate-300 cursor-not-allowed"}`}
                        >
                            {saving ? "Menyimpan..." : "Simpan"}
                        </button>

                        <div className="relative">
                            <button
                                onClick={() => setShowOptions((v) => !v)}
                                className="w-9 h-9 flex items-center justify-center rounded-xl border border-slate-200 hover:bg-slate-50 transition-colors cursor-pointer"
                            >
                                <ChevronDown className="w-4 h-4 text-slate-600" />
                            </button>
                            <OptionsDropdown
                                open={showOptions}
                                onPublish={() => { setShowOptions(false); setShowPublishConfirm(true); }}
                                onDelete={() => { setShowOptions(false); setShowDeleteConfirm(true); }}
                                onClose={() => setShowOptions(false)}
                            />
                        </div>
                    </div>
                </div>

                <div className="flex items-center gap-2 px-4 bg-slate-50 border-t border-slate-100">
                    <input
                        type="text"
                        value={title}
                        onChange={(e) => setTitle(e.target.value)}
                        placeholder="Tulis judul episode..."
                        className="flex-1 py-3 bg-transparent text-sm text-slate-800 placeholder:text-slate-400 outline-none font-medium"
                    />
                </div>

                <div
                    className="flex items-center gap-2 px-4 py-2.5 bg-slate-50 border-t border-slate-100 cursor-pointer hover:bg-slate-100 transition-colors"
                    onClick={() => dateRef.current?.showPicker()}
                >
                    <Calendar className="w-4 h-4 text-slate-400 shrink-0" />
                    <span className="text-xs text-slate-400">
                        Tanggal Terbit:{" "}
                        <span className="font-semibold text-slate-700">
                            {format(publishedAt, "dd MMM yyyy, HH:mm")}
                        </span>
                    </span>
                    <input
                        ref={dateRef}
                        type="datetime-local"
                        value={toInputValue(publishedAt)}
                        onChange={(e) => e.target.value && setPublishedAt(new Date(e.target.value))}
                        className="sr-only"
                        readOnly
                    />
                </div>

                <div className="flex items-center gap-2 px-4 py-2.5 bg-slate-50 border-t border-slate-100">
                    <span className="text-xs text-slate-400 shrink-0">Harga Episode:</span>
                    <input
                        type="number"
                        min={MIN_COINS}
                        max={MAX_COINS}
                        value={coins}
                        onChange={(e) => {
                            const v = e.target.value;
                            setCoins(v === "" ? "" : Number(v));
                        }}
                        className="w-20 px-2 py-1 text-sm font-semibold text-slate-800 bg-white border border-slate-200 rounded-lg outline-none focus:ring-2 focus:ring-primary-300"
                    />
                    <span className="text-xs text-slate-400">koin ({MIN_COINS}–{MAX_COINS})</span>
                    {coins !== "" && !coinsValid && (
                        <span className="text-xs text-red-500">Harus {MIN_COINS}-{MAX_COINS} koin</span>
                    )}
                </div>
            </div>

            <div className="bg-white rounded-xl shadow-sm p-5 flex flex-col gap-4">
                <div className="flex items-center justify-between">
                    <h2 className="text-sm font-semibold text-slate-800">Halaman Episode</h2>
                    <span className="text-xs text-slate-400">
                        {uploadingImages ? "Mengunggah..." : `${images.length}/${MAX_IMAGES} gambar`}
                    </span>
                </div>

                {images.length > 0 && (
                    <p className="text-xs text-slate-400 flex items-center gap-1.5">
                        <GripVertical className="w-3.5 h-3.5" />
                        Seret gambar buat ngatur urutan halaman.
                    </p>
                )}

                <DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
                    <SortableContext items={images.map((img) => img.id)} strategy={rectSortingStrategy}>
                        <div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-4">
                            {images.map((img, index) => (
                                <SortableImageThumb
                                    key={img.id}
                                    id={img.id}
                                    url={img.url}
                                    index={index}
                                    onRemove={() => removeImage(img.id)}
                                />
                            ))}
                        </div>
                    </SortableContext>
                </DndContext>

                {images.length < MAX_IMAGES && (
                    <button
                        type="button"
                        disabled={uploadingImages}
                        onClick={() => fileInputRef.current?.click()}
                        className="flex flex-col items-center justify-center gap-2 py-10 rounded-xl border-2 border-dashed border-slate-200 text-slate-400 hover:border-primary-300 hover:text-primary-500 transition-colors cursor-pointer disabled:opacity-60 disabled:cursor-not-allowed"
                    >
                        <ImagePlus className="w-6 h-6" />
                        <span className="text-sm font-medium">Tambah Gambar</span>
                    </button>
                )}

                <input
                    ref={fileInputRef}
                    type="file"
                    accept="image/*"
                    multiple
                    className="hidden"
                    onChange={(e) => {
                        onPickImages(e.target.files);
                        e.target.value = "";
                    }}
                />
            </div>

            <ConfirmModal
                open={showDeleteConfirm}
                title="Hapus episode ini?"
                description={`"${chapter?.title ?? title}" akan dihapus permanen dan tidak bisa dikembalikan.`}
                confirmLabel="Hapus"
                danger
                loading={deleting}
                onConfirm={onDelete}
                onClose={() => setShowDeleteConfirm(false)}
            />

            <ConfirmModal
                open={showPublishConfirm}
                title="Publikasikan episode ini?"
                description={`"${title}" akan dikirim buat direview sebelum tayang ke pembaca.`}
                confirmLabel="Publikasikan"
                loading={publishing}
                onConfirm={onPublish}
                onClose={() => setShowPublishConfirm(false)}
            />
        </div>
    );
}
