'use client';

import { useState, useEffect, useRef, useCallback } from 'react';
import { useParams, useRouter } from 'next/navigation';
import Link from 'next/link';
import { useLanguage } from '@/context/LanguageContext';
import { photosApi, eventsApi, PhotoGallery, Photo, Event, GalleryVisibility } from '@/lib/api';
import Card from '@/components/ui/Card';
import Button from '@/components/ui/Button';
import { Skeleton, ImageGridSkeleton } from '@/components/ui/Skeleton';
import Lightbox from '@/components/Lightbox';
import VisibilityBadge from '../VisibilityBadge';
import {
  ArrowLeftIcon,
  ArrowUpTrayIcon,
  ArrowPathIcon,
  CheckIcon,
  CheckCircleIcon,
  ChevronDownIcon,
  ChevronUpIcon,
  DocumentDuplicateIcon,
  ExclamationCircleIcon,
  ExclamationTriangleIcon,
  LinkIcon,
  PhotoIcon,
  StarIcon,
  TrashIcon,
  XMarkIcon,
} from '@heroicons/react/24/outline';
import { StarIcon as StarIconSolid } from '@heroicons/react/24/solid';
import toast from 'react-hot-toast';

interface UploadItem {
  key: string;
  name: string;
  sizeBytes: number;
  progress: number; // 0..1 while uploading
  // 'duplicate' is terminal: the gallery already held these bytes, so nothing
  // was stored and no new tile appears in the grid.
  status: 'queued' | 'uploading' | 'processing' | 'error' | 'duplicate';
  error?: string;
  photoId?: string;
}

function formatBytes(n: number) {
  if (n >= 1 << 20) return `${(n / (1 << 20)).toFixed(1)} MB`;
  if (n >= 1 << 10) return `${Math.round(n / (1 << 10))} KB`;
  return `${n} B`;
}

export default function AdminGalleryDetailPage() {
  const { id } = useParams<{ id: string }>();
  const router = useRouter();
  const { locale } = useLanguage();
  const es = locale === 'es';

  const [gallery, setGallery] = useState<PhotoGallery | null>(null);
  const [photos, setPhotos] = useState<Photo[]>([]);
  const [events, setEvents] = useState<Event[]>([]);
  const [loading, setLoading] = useState(true);
  const [copied, setCopied] = useState(false);
  const [dragId, setDragId] = useState<string | null>(null);
  const [lightboxIndex, setLightboxIndex] = useState<number | null>(null);
  const fileInputRef = useRef<HTMLInputElement>(null);

  // Uploader panel state (Google Drive style: one row per file).
  const [uploads, setUploads] = useState<UploadItem[]>([]);
  const [panelCollapsed, setPanelCollapsed] = useState(false);
  const uploadQueue = useRef<{ key: string; file: File }[]>([]);
  const pumpRunning = useRef(false);

  const load = useCallback(async () => {
    try {
      const [detail, ev] = await Promise.all([photosApi.getGallery(id), eventsApi.getAll()]);
      setGallery(detail.gallery);
      setPhotos(detail.photos);
      setEvents(ev.events);
    } catch {
      toast.error(es ? 'No se pudo cargar la galería' : 'Failed to load gallery');
      router.push('/admin/photos');
    } finally {
      setLoading(false);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [id]);

  useEffect(() => {
    load();
  }, [load]);

  // Poll while any photo is still processing so thumbnails (and the
  // uploader panel rows) update as the worker finishes them.
  const processingCount = photos.filter((p) => p.status === 'queued' || p.status === 'processing').length;
  useEffect(() => {
    if (processingCount === 0) return;
    const timer = setInterval(async () => {
      try {
        const detail = await photosApi.getGallery(id);
        setGallery(detail.gallery);
        setPhotos(detail.photos);
      } catch {
        /* transient; next tick retries */
      }
    }, 3000);
    return () => clearInterval(timer);
  }, [processingCount, id]);

  const patchUpload = (key: string, patch: Partial<UploadItem>) => {
    setUploads((prev) => prev.map((u) => (u.key === key ? { ...u, ...patch } : u)));
  };

  // Sequential upload pump: one file at a time, byte progress per file.
  const pump = useCallback(async () => {
    if (pumpRunning.current) return;
    pumpRunning.current = true;
    while (uploadQueue.current.length > 0) {
      const { key, file } = uploadQueue.current.shift()!;
      patchUpload(key, { status: 'uploading', progress: 0 });
      try {
        const { photos: added } = await photosApi.uploadPhotoWithProgress(id, file, (fraction) =>
          patchUpload(key, { progress: fraction })
        );
        // A duplicate echoes back a photo already in the grid, so merge by id
        // instead of appending (`duplicate` is an upload outcome, not photo
        // state, so it is dropped here).
        setPhotos((prev) => {
          const byId = new Map(prev.map((p) => [p.id, p]));
          added.forEach(({ duplicate: _duplicate, ...photo }) => byId.set(photo.id, photo));
          return Array.from(byId.values());
        });
        patchUpload(key, {
          status: added[0]?.duplicate ? 'duplicate' : 'processing',
          progress: 1,
          photoId: added[0]?.id,
        });
      } catch (err) {
        patchUpload(key, {
          status: 'error',
          error: err instanceof Error ? err.message : 'Upload failed',
        });
      }
    }
    pumpRunning.current = false;
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [id]);

  const handleUpload = (files: FileList | File[]) => {
    const list = Array.from(files);
    if (list.length === 0) return;
    const items: UploadItem[] = list.map((file) => ({
      key: `${Date.now()}-${Math.random().toString(36).slice(2)}`,
      name: file.name,
      sizeBytes: file.size,
      progress: 0,
      status: 'queued',
    }));
    uploadQueue.current.push(...items.map((item, i) => ({ key: item.key, file: list[i] })));
    setUploads((prev) => [...prev, ...items]);
    setPanelCollapsed(false);
    pump();
    if (fileInputRef.current) fileInputRef.current.value = '';
  };

  // A row is "done" once its photo finished processing; the panel derives
  // this from the photos list instead of tracking it separately. 'duplicate'
  // is terminal and never reconciled — its photo was already there.
  const displayStatus = (u: UploadItem): { state: string; error?: string } => {
    if (u.status === 'processing' && u.photoId) {
      const photo = photos.find((p) => p.id === u.photoId);
      if (photo?.status === 'ready') return { state: 'done' };
      if (photo?.status === 'failed') return { state: 'error', error: photo.lastError || 'Processing failed' };
    }
    return { state: u.status, error: u.error };
  };

  const activeUploads = uploads.filter((u) => {
    const s = displayStatus(u).state;
    return s === 'queued' || s === 'uploading' || s === 'processing';
  }).length;

  const saveField = async (patch: Parameters<typeof photosApi.updateGallery>[1], okMsg?: string) => {
    try {
      const { gallery: updated } = await photosApi.updateGallery(id, patch);
      setGallery(updated);
      if (okMsg) toast.success(okMsg);
    } catch (err) {
      toast.error(err instanceof Error ? err.message : 'Update failed');
    }
  };

  const copyShareLink = async () => {
    if (!gallery?.shareUrl) return;
    try {
      await navigator.clipboard.writeText(gallery.shareUrl);
      setCopied(true);
      toast.success(es ? 'Enlace copiado' : 'Share link copied');
      setTimeout(() => setCopied(false), 2000);
    } catch {
      toast.error(es ? 'No se pudo copiar' : 'Failed to copy');
    }
  };

  const rotateToken = async () => {
    if (!confirm(es ? '¿Invalidar el enlace actual y generar uno nuevo?' : 'Invalidate the current link and generate a new one?'))
      return;
    try {
      const { gallery: updated } = await photosApi.rotateShareToken(id);
      setGallery(updated);
      toast.success(es ? 'Nuevo enlace generado' : 'New share link generated');
    } catch (err) {
      toast.error(err instanceof Error ? err.message : 'Failed');
    }
  };

  const deletePhoto = async (photoId: string): Promise<boolean> => {
    if (!confirm(es ? '¿Eliminar esta foto?' : 'Delete this photo?')) return false;
    try {
      await photosApi.deletePhoto(photoId);
      setPhotos((prev) => prev.filter((p) => p.id !== photoId));
      toast.success(es ? 'Foto eliminada' : 'Photo deleted');
      return true;
    } catch (err) {
      toast.error(err instanceof Error ? err.message : 'Failed to delete');
      return false;
    }
  };

  const retryPhoto = async (photoId: string) => {
    try {
      const { photo } = await photosApi.retryPhoto(photoId);
      setPhotos((prev) => prev.map((p) => (p.id === photoId ? photo : p)));
    } catch (err) {
      toast.error(err instanceof Error ? err.message : 'Failed to retry');
    }
  };

  const deleteGallery = async () => {
    if (!confirm(es ? '¿Eliminar toda la galería y sus fotos?' : 'Delete the whole gallery and its photos?')) return;
    try {
      await photosApi.deleteGallery(id);
      toast.success(es ? 'Galería eliminada' : 'Gallery deleted');
      router.push('/admin/photos');
    } catch (err) {
      toast.error(err instanceof Error ? err.message : 'Failed to delete');
    }
  };

  // HTML5 drag-and-drop reorder; persisted on drop.
  const onDropReorder = async (targetId: string) => {
    if (!dragId || dragId === targetId) return;
    const ids = photos.map((p) => p.id);
    const from = ids.indexOf(dragId);
    const to = ids.indexOf(targetId);
    if (from < 0 || to < 0) return;
    ids.splice(to, 0, ids.splice(from, 1)[0]);
    const reordered = ids
      .map((pid) => photos.find((p) => p.id === pid))
      .filter((p): p is Photo => !!p);
    setPhotos(reordered);
    setDragId(null);
    try {
      await photosApi.reorderPhotos(id, ids);
    } catch (err) {
      toast.error(err instanceof Error ? err.message : 'Failed to save order');
      load();
    }
  };

  if (loading || !gallery) {
    return (
      <div>
        <Skeleton className="h-8 w-64 mb-6" />
        <ImageGridSkeleton />
      </div>
    );
  }

  const readyPhotos = photos.filter((p) => p.status === 'ready' && p.urls.thumb);
  const lightboxItems = readyPhotos.map((p) => ({
    id: p.id,
    previewUrl: p.urls.preview || p.urls.original,
    downloadUrl: p.urls.original,
    filename: p.originalFilename,
    thumbUrl: p.urls.thumb,
  }));
  const openLightboxFor = (photoId: string) => {
    const idx = readyPhotos.findIndex((p) => p.id === photoId);
    if (idx >= 0) setLightboxIndex(idx);
  };

  return (
    <div
      onDragOver={(e) => {
        if (e.dataTransfer.types.includes('Files')) e.preventDefault();
      }}
      onDrop={(e) => {
        if (e.dataTransfer.files.length > 0) {
          e.preventDefault();
          handleUpload(e.dataTransfer.files);
        }
      }}
    >
      <div className="flex flex-wrap items-center justify-between gap-3 mb-6">
        <div className="flex items-center gap-3 min-w-0">
          <Link href="/admin/photos" className="text-gray-400 hover:text-gray-600">
            <ArrowLeftIcon className="w-6 h-6" />
          </Link>
          <h1 className="text-2xl font-bold text-primary-dark truncate">
            {es && gallery.titleEs ? gallery.titleEs : gallery.title}
          </h1>
          <VisibilityBadge visibility={gallery.visibility} />
        </div>
        <div className="flex items-center gap-2">
          <Button onClick={() => fileInputRef.current?.click()}>
            <ArrowUpTrayIcon className="w-5 h-5 mr-2" />
            {es ? 'Subir fotos' : 'Upload photos'}
          </Button>
          <input
            ref={fileInputRef}
            type="file"
            accept="image/jpeg,image/png,image/gif,image/webp,image/heic,.heic"
            multiple
            onChange={(e) => e.target.files && handleUpload(e.target.files)}
            className="hidden"
          />
        </div>
      </div>

      {/* Settings */}
      <Card className="p-4 mb-6">
        <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
          <div>
            <label className="block text-sm font-medium mb-1">{es ? 'Visibilidad' : 'Visibility'}</label>
            <select
              value={gallery.visibility}
              onChange={(e) =>
                saveField(
                  { visibility: e.target.value as GalleryVisibility },
                  es ? 'Visibilidad actualizada' : 'Visibility updated'
                )
              }
              className="w-full px-4 py-2 rounded-btn border border-secondary-light-gray"
            >
              <option value="private">{es ? 'Privada (solo admins)' : 'Private (admins only)'}</option>
              <option value="public">{es ? 'Pública (listada)' : 'Public (listed)'}</option>
              <option value="link">{es ? 'Solo con enlace' : 'Link only'}</option>
              <option value="ticket">{es ? 'Con entrada del evento' : 'Ticket holders of the event'}</option>
            </select>
          </div>
          <div>
            <label className="block text-sm font-medium mb-1">{es ? 'Evento vinculado' : 'Linked event'}</label>
            <select
              value={gallery.eventId || ''}
              onChange={(e) =>
                saveField({ eventId: e.target.value }, es ? 'Evento actualizado' : 'Event updated')
              }
              className="w-full px-4 py-2 rounded-btn border border-secondary-light-gray"
            >
              <option value="">{es ? 'Ninguno' : 'None'}</option>
              {events.map((ev) => (
                <option key={ev.id} value={ev.id}>
                  {ev.title}
                </option>
              ))}
            </select>
            {gallery.visibility === 'ticket' && !gallery.eventId && (
              <p className="text-xs text-red-600 mt-1">
                {es
                  ? 'El modo "con entrada" necesita un evento vinculado'
                  : 'Ticket mode needs a linked event'}
              </p>
            )}
          </div>
          <div>
            <label className="block text-sm font-medium mb-1">{es ? 'Enlace para compartir' : 'Share link'}</label>
            <div className="flex gap-2">
              <input
                type="text"
                readOnly
                value={gallery.shareUrl || ''}
                className="flex-1 min-w-0 px-3 py-2 text-sm border rounded-btn bg-gray-50"
              />
              <Button size="sm" onClick={copyShareLink} title={es ? 'Copiar' : 'Copy'}>
                {copied ? <CheckIcon className="w-4 h-4" /> : <LinkIcon className="w-4 h-4" />}
              </Button>
              <Button size="sm" variant="outline" onClick={rotateToken} title={es ? 'Regenerar' : 'Regenerate'}>
                <ArrowPathIcon className="w-4 h-4" />
              </Button>
            </div>
            {gallery.event && (
              <p className="text-xs text-gray-500 mt-1">
                {es
                  ? 'Publicada en la página del evento: '
                  : 'Published on the event page: '}
                <span className="font-mono">/events/{gallery.event.slug}/gallery</span>
              </p>
            )}
          </div>
        </div>
      </Card>

      {processingCount > 0 && (
        <p className="text-sm text-gray-600 mb-4">
          {es
            ? `Procesando ${processingCount} foto(s)…`
            : `Processing ${processingCount} photo(s)…`}
        </p>
      )}

      {/* Photo grid */}
      {photos.length === 0 ? (
        <Card className="p-12 text-center">
          <ArrowUpTrayIcon className="w-16 h-16 mx-auto text-gray-300 mb-4" />
          <p className="text-gray-500">
            {es
              ? 'Arrastra fotos aquí o usa el botón de subir'
              : 'Drag photos here or use the upload button'}
          </p>
        </Card>
      ) : (
        <div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4">
          {photos.map((photo) => (
            <Card
              key={photo.id}
              draggable
              onDragStart={() => setDragId(photo.id)}
              onDragOver={(e) => {
                if (dragId) e.preventDefault();
              }}
              onDrop={(e) => {
                if (dragId) {
                  e.preventDefault();
                  e.stopPropagation();
                  onDropReorder(photo.id);
                }
              }}
              className={`group relative overflow-hidden aspect-square cursor-grab ${
                dragId === photo.id ? 'opacity-50' : ''
              }`}
            >
              {photo.status === 'ready' && photo.urls.thumb ? (
                // eslint-disable-next-line @next/next/no-img-element
                <img
                  src={photo.urls.thumb}
                  alt=""
                  className="w-full h-full object-cover cursor-pointer"
                  draggable={false}
                  onClick={() => openLightboxFor(photo.id)}
                />
              ) : photo.status === 'failed' ? (
                <div className="w-full h-full flex flex-col items-center justify-center bg-red-50 text-red-600 p-2 text-center">
                  <ExclamationTriangleIcon className="w-8 h-8 mb-1" />
                  <span className="text-xs line-clamp-3">{photo.lastError || 'Failed'}</span>
                </div>
              ) : (
                <div className="w-full h-full flex items-center justify-center bg-gray-100">
                  <div className="animate-spin w-6 h-6 border-2 border-primary-yellow border-t-transparent rounded-full" />
                </div>
              )}

              {gallery.coverPhotoId === photo.id && (
                <StarIconSolid className="absolute top-2 left-2 w-5 h-5 text-primary-yellow drop-shadow" />
              )}

              <div className="absolute inset-x-0 bottom-0 bg-black/60 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center gap-1 py-1.5">
                {photo.status === 'ready' && (
                  <button
                    onClick={() =>
                      saveField({ coverPhotoId: photo.id }, es ? 'Portada actualizada' : 'Cover updated')
                    }
                    className="p-1.5 text-white hover:text-primary-yellow"
                    title={es ? 'Usar como portada' : 'Set as cover'}
                  >
                    <StarIcon className="w-5 h-5" />
                  </button>
                )}
                {photo.status === 'failed' && (
                  <button
                    onClick={() => retryPhoto(photo.id)}
                    className="p-1.5 text-white hover:text-primary-yellow"
                    title={es ? 'Reintentar' : 'Retry'}
                  >
                    <ArrowPathIcon className="w-5 h-5" />
                  </button>
                )}
                <button
                  onClick={() => deletePhoto(photo.id)}
                  className="p-1.5 text-white hover:text-red-400"
                  title={es ? 'Eliminar' : 'Delete'}
                >
                  <TrashIcon className="w-5 h-5" />
                </button>
              </div>
            </Card>
          ))}
        </div>
      )}

      {/* Danger zone */}
      <div className="mt-10 pt-6 border-t border-secondary-light-gray flex justify-end">
        <Button variant="danger" onClick={deleteGallery}>
          <TrashIcon className="w-5 h-5 mr-2" />
          {es ? 'Eliminar galería' : 'Delete gallery'}
        </Button>
      </div>

      {/* Lightbox with admin actions */}
      {lightboxIndex !== null && lightboxItems.length > 0 && (
        <Lightbox
          items={lightboxItems}
          index={Math.min(lightboxIndex, lightboxItems.length - 1)}
          onClose={() => setLightboxIndex(null)}
          onNavigate={setLightboxIndex}
          renderActions={(item) => (
            <>
              <button
                onClick={() =>
                  saveField({ coverPhotoId: item.id }, es ? 'Portada actualizada' : 'Cover updated')
                }
                className="text-white hover:text-primary-yellow"
                title={es ? 'Usar como portada' : 'Set as cover'}
              >
                {gallery.coverPhotoId === item.id ? (
                  <StarIconSolid className="w-7 h-7 text-primary-yellow" />
                ) : (
                  <StarIcon className="w-7 h-7" />
                )}
              </button>
              <button
                onClick={async () => {
                  const before = readyPhotos.length;
                  const deleted = await deletePhoto(item.id);
                  if (!deleted) return;
                  if (before <= 1) setLightboxIndex(null);
                  else setLightboxIndex((i) => (i === null ? null : Math.max(0, Math.min(i, before - 2))));
                }}
                className="text-white hover:text-red-400"
                title={es ? 'Eliminar' : 'Delete'}
              >
                <TrashIcon className="w-7 h-7" />
              </button>
            </>
          )}
        />
      )}

      {/* Uploader panel (Google Drive style) */}
      {uploads.length > 0 && (
        <div className="fixed bottom-4 right-4 z-40 w-96 max-w-[calc(100vw-2rem)]">
          <div className="bg-white rounded-card shadow-card-hover border border-secondary-light-gray overflow-hidden">
            <div className="flex items-center justify-between px-4 py-3 bg-primary-dark text-white">
              <p className="text-sm font-medium">
                {activeUploads > 0
                  ? es
                    ? `Subiendo ${uploads.length - activeUploads}/${uploads.length}…`
                    : `Uploading ${uploads.length - activeUploads}/${uploads.length}…`
                  : es
                    ? `${uploads.length} subida(s) completada(s)`
                    : `${uploads.length} upload(s) complete`}
              </p>
              <div className="flex items-center gap-1">
                <button
                  onClick={() => setPanelCollapsed((c) => !c)}
                  className="p-1 hover:text-primary-yellow"
                  aria-label={panelCollapsed ? 'Expand' : 'Collapse'}
                >
                  {panelCollapsed ? <ChevronUpIcon className="w-5 h-5" /> : <ChevronDownIcon className="w-5 h-5" />}
                </button>
                {activeUploads === 0 && (
                  <button
                    onClick={() => setUploads([])}
                    className="p-1 hover:text-primary-yellow"
                    aria-label="Close"
                  >
                    <XMarkIcon className="w-5 h-5" />
                  </button>
                )}
              </div>
            </div>
            {!panelCollapsed && (
              <ul className="max-h-72 overflow-y-auto divide-y divide-secondary-light-gray">
                {uploads.map((u) => {
                  const ds = displayStatus(u);
                  return (
                    <li key={u.key} className="px-4 py-2.5">
                      <div className="flex items-center gap-3">
                        <PhotoIcon className="w-5 h-5 text-gray-400 shrink-0" />
                        <div className="flex-1 min-w-0">
                          <p className="text-sm text-primary-dark truncate" title={u.name}>
                            {u.name}
                          </p>
                          <p className="text-xs text-gray-500">
                            {ds.state === 'uploading' && `${Math.round(u.progress * 100)}% · ${formatBytes(u.sizeBytes)}`}
                            {ds.state === 'queued' && (es ? 'En cola' : 'Queued')}
                            {ds.state === 'processing' && (es ? 'Procesando…' : 'Processing…')}
                            {ds.state === 'done' && formatBytes(u.sizeBytes)}
                            {ds.state === 'duplicate' && (
                              <span className="text-amber-600">
                                {es ? 'Ya está en esta galería' : 'Already in this gallery'}
                              </span>
                            )}
                            {ds.state === 'error' && (
                              <span className="text-red-600" title={ds.error}>
                                {ds.error}
                              </span>
                            )}
                          </p>
                        </div>
                        <span className="shrink-0">
                          {ds.state === 'done' && <CheckCircleIcon className="w-6 h-6 text-green-600" />}
                          {ds.state === 'duplicate' && (
                            <DocumentDuplicateIcon className="w-6 h-6 text-amber-600" />
                          )}
                          {ds.state === 'error' && <ExclamationCircleIcon className="w-6 h-6 text-red-600" />}
                          {ds.state === 'processing' && (
                            <div className="animate-spin w-5 h-5 border-2 border-primary-yellow border-t-transparent rounded-full" />
                          )}
                          {ds.state === 'uploading' && (
                            <span className="text-xs text-gray-600 tabular-nums">{Math.round(u.progress * 100)}%</span>
                          )}
                        </span>
                      </div>
                      {ds.state === 'uploading' && (
                        <div className="mt-1.5 h-1 rounded-full bg-secondary-gray overflow-hidden">
                          <div
                            className="h-full bg-primary-yellow transition-[width] duration-200"
                            style={{ width: `${Math.round(u.progress * 100)}%` }}
                          />
                        </div>
                      )}
                    </li>
                  );
                })}
              </ul>
            )}
          </div>
        </div>
      )}
    </div>
  );
}
