"use client";

import { useEffect, useState, type ChangeEvent, type FormEvent } from "react";
import { useRouter } from "next/navigation";
import type { EventGalleryImage } from "@/types/event";
import { Button } from "@/components/ui/button";
import { FileInput } from "@/components/ui/input";
import { useConfirmDialog } from "@/components/ui/confirm-dialog";
import { useToast } from "@/components/ui/toast";
import { FOCUS_RING_TIGHT_CLASSES } from "@/components/ui/styles";
import { clientFetch } from "@/lib/api/client";
import { cn } from "@/lib/utils";
import { MediaSlot } from "./media-slot";

const MAX_GALLERY_IMAGE_BYTES = 5 * 1024 * 1024;
const ACCEPTED_IMAGE_TYPES = ["image/jpeg", "image/png", "image/webp"];

function GallerySection({ eventId, images }: { eventId: number; images: EventGalleryImage[] }) {
  const router = useRouter();
  const [error, setError] = useState<string | null>(null);
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [deletingId, setDeletingId] = useState<number | null>(null);
  const [previewUrl, setPreviewUrl] = useState<string | null>(null);
  const { confirm, dialog } = useConfirmDialog();
  const { toast } = useToast();

  // Revokes whichever ObjectURL is current whenever it's replaced or the
  // component unmounts — otherwise each selected file leaks its blob URL.
  useEffect(() => {
    return () => {
      if (previewUrl) URL.revokeObjectURL(previewUrl);
    };
  }, [previewUrl]);

  function handleFileChange(changeEvent: ChangeEvent<HTMLInputElement>) {
    setError(null);
    if (previewUrl) URL.revokeObjectURL(previewUrl);

    const file = changeEvent.target.files?.[0];
    if (!file) {
      setPreviewUrl(null);
      return;
    }

    if (!ACCEPTED_IMAGE_TYPES.includes(file.type)) {
      setError("Format must be JPG, PNG, or WEBP.");
      changeEvent.target.value = "";
      setPreviewUrl(null);
      return;
    }
    if (file.size > MAX_GALLERY_IMAGE_BYTES) {
      setError("Maximum image size is 5MB.");
      changeEvent.target.value = "";
      setPreviewUrl(null);
      return;
    }

    // Immediate local preview — the server round-trip below only swaps this
    // out for the persisted image once the upload actually succeeds.
    setPreviewUrl(URL.createObjectURL(file));
  }

  async function handleUpload(formEvent: FormEvent<HTMLFormElement>) {
    formEvent.preventDefault();
    setError(null);

    const form = formEvent.currentTarget;
    const fileInput = form.elements.namedItem("image") as HTMLInputElement;
    if (!fileInput.files || fileInput.files.length === 0) return;

    setIsSubmitting(true);
    const formData = new FormData();
    formData.append("image", fileInput.files[0]);

    const res = await clientFetch(`/api/admin/events/${eventId}/gallery`, {
      method: "POST",
      body: formData,
    });
    setIsSubmitting(false);
    const body = await res.json().catch(() => null);

    if (!res.ok) {
      const message = body?.errors?.image?.[0] ?? body?.message ?? "Failed to upload.";
      setError(message);
      toast(message, "error");
      return;
    }

    form.reset();
    if (previewUrl) URL.revokeObjectURL(previewUrl);
    setPreviewUrl(null);
    toast("Gallery image uploaded successfully.", "success");
    router.refresh();
  }

  async function handleDelete(galleryId: number) {
    if (!(await confirm("Delete this gallery image?"))) return;

    setError(null);
    setDeletingId(galleryId);
    const res = await clientFetch(`/api/admin/events/${eventId}/gallery/${galleryId}`, {
      method: "DELETE",
    });
    setDeletingId(null);
    const body = await res.json().catch(() => null);

    if (!res.ok) {
      const message = body?.message ?? "Failed to delete.";
      setError(message);
      toast(message, "error");
      return;
    }
    toast("Gallery image deleted successfully.", "success");
    router.refresh();
  }

  return (
    <div>
      {dialog}
      <p className="text-sm font-medium">Gallery</p>
      <p className="text-xs text-kolabora-neutral-dark/70">
        Additional images for the event detail page (FR-113).
      </p>
      {(images.length > 0 || previewUrl) && (
        <div className="mt-2 flex flex-wrap gap-3">
          {images.map((image) => (
            <div key={image.id} className="relative">
              {/* eslint-disable-next-line @next/next/no-img-element -- organizer-uploaded file, not on next/image allowlist */}
              <img src={image.image_path} alt="Gallery" className="h-24 w-32 rounded-lg object-cover" />
              <button
                type="button"
                onClick={() => handleDelete(image.id)}
                disabled={deletingId === image.id}
                aria-label="Delete this gallery image"
                aria-busy={deletingId === image.id}
                className={cn(
                  "absolute right-1 top-1 rounded-full bg-kolabora-neutral-dark/70 px-2 py-0.5 text-xs text-kolabora-neutral-white hover:bg-red-600 disabled:opacity-50",
                  FOCUS_RING_TIGHT_CLASSES,
                )}
              >
                {deletingId === image.id ? "..." : "×"}
              </button>
            </div>
          ))}
          {previewUrl && (
            <div className="relative">
              {/* eslint-disable-next-line @next/next/no-img-element -- local ObjectURL preview, never a remote/next-image-eligible asset */}
              <img
                src={previewUrl}
                alt="New image preview"
                className="h-24 w-32 rounded-lg object-cover opacity-70"
              />
              <span className="absolute inset-0 flex items-center justify-center rounded-lg bg-kolabora-neutral-dark/30 text-xs font-medium text-kolabora-neutral-white">
                {isSubmitting ? "Uploading..." : "New"}
              </span>
            </div>
          )}
        </div>
      )}
      <form onSubmit={handleUpload} className="mt-3 flex items-center gap-2">
        <FileInput name="image" accept="image/jpeg,image/png,image/webp" onChange={handleFileChange} />
        <Button type="submit" size="sm" loading={isSubmitting} disabled={!previewUrl}>
          + Add Image
        </Button>
      </form>
      {error && <p className="mt-1 text-sm text-red-600">{error}</p>}
    </div>
  );
}

export function EventMediaManager({
  eventId,
  thumbnail,
  banner,
  galleries,
}: {
  eventId: number;
  thumbnail: string | null;
  banner: string | null;
  galleries: EventGalleryImage[];
}) {
  return (
    <div className="flex flex-col gap-8">
      <MediaSlot
        label="Thumbnail"
        fieldName="thumbnail"
        endpoint={`/api/admin/events/${eventId}/media/thumbnail`}
        current={thumbnail}
        hint="JPG/PNG/WEBP, max 5MB, minimum 400x300px (FR-116)."
      />
      <MediaSlot
        label="Banner"
        fieldName="banner"
        endpoint={`/api/admin/events/${eventId}/media/banner`}
        current={banner}
        hint="JPG/PNG/WEBP, max 5MB, minimum 1200x400px — wide format (FR-116)."
      />
      <GallerySection eventId={eventId} images={galleries} />
    </div>
  );
}
