import { useState, useCallback, useRef } from "react";
import { apiClient } from "@/utils/apiClient";
import { toast } from "sonner";
import { useDropzone } from "react-dropzone";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from "@/components/ui/popover";
import {
  Loader2,
  Eye,
  EyeOff,
  Trash2,
  Plus,
  Upload,
  Link,
} from "lucide-react";
import {
  Dialog,
  DialogContent,
  DialogTitle,
} from "@/components/ui/dialog";
import { cn } from "@/lib/utils";
import { IMAGE_UPLOAD_ACCEPT, MAX_UPLOAD_BYTES, formatUploadLimitMb } from "@/config/uploads";
import {
  DndContext,
  closestCenter,
  KeyboardSensor,
  PointerSensor,
  useSensor,
  useSensors,
  DragOverlay,
  type DragStartEvent,
  type DragEndEvent,
  type DragOverEvent,
} from "@dnd-kit/core";
import {
  arrayMove,
  SortableContext,
  sortableKeyboardCoordinates,
  rectSortingStrategy,
  useSortable,
} from "@dnd-kit/sortable";
import type { ProductImage } from "./types";

// ── Helper: get best image URL ─────────────────────────────────────────

export function getImageSrc(img: ProductImage, preferLarge = false): string | null {
  if (img.imageUrl) return img.imageUrl;
  if (preferLarge) {
    return (
      img.sizes["600x600"]?.url ??
      null
    );
  }
  return (
    img.sizes["300x300"]?.url ??
    null
  );
}

// ── Sortable Image Cell ─────────────────────────────────────────────────

function SortableImageCell({
  image,
  index,
  isMain,
  showOrder,
  onToggleHidden,
  onDelete,
  onZoom,
}: {
  image: ProductImage;
  index: number;
  isMain: boolean;
  showOrder: boolean;
  onToggleHidden: (iid: number | string) => void;
  onDelete: (iid: number | string) => void;
  onZoom: (image: ProductImage) => void;
}) {
  const {
    attributes,
    listeners,
    setNodeRef,
    isDragging,
  } = useSortable({ id: image.iid });

  const didDragRef = useRef(false);
  const pointerStartRef = useRef<{ x: number; y: number } | null>(null);

  const src = getImageSrc(image);
  if (!src) return null;

  const iconSize = isMain ? "h-4 w-4" : "h-4 w-4";
  const btnPad = isMain ? "p-1" : "p-0.5";
  const btnGap = isMain ? "gap-1" : "gap-0.5";
  const orderSize = "w-7 h-7 text-base";

  return (
    <div
      ref={setNodeRef}
      {...attributes}
      {...listeners}
      onPointerDown={(e) => {
        didDragRef.current = false;
        pointerStartRef.current = { x: e.clientX, y: e.clientY };
        listeners?.onPointerDown?.(e);
      }}
      onPointerMove={(e) => {
        if (pointerStartRef.current) {
          const dx = e.clientX - pointerStartRef.current.x;
          const dy = e.clientY - pointerStartRef.current.y;
          if (Math.abs(dx) > 3 || Math.abs(dy) > 3) {
            didDragRef.current = true;
          }
        }
      }}
      onClick={() => {
        if (!didDragRef.current) {
          onZoom(image);
        }
      }}
      className={cn(
        "relative group border bg-white cursor-grab active:cursor-grabbing transition-opacity",
        isMain ? "rounded-lg col-span-2 row-span-2" : "rounded-md",
        image.hidden && "opacity-50 ring-2 ring-yellow-400",
        isDragging && "opacity-30",
      )}
      style={{ touchAction: "none" }}
    >
      {/* Action buttons */}
      <div className={cn("absolute top-0.5 right-0.5 z-10 flex opacity-0 group-hover:opacity-100 transition-opacity", btnGap)}>
        <button
          type="button"
          onPointerDown={(e) => e.stopPropagation()}
          onClick={(e) => { e.stopPropagation(); onToggleHidden(image.iid); }}
          className={cn(btnPad, "rounded bg-black/40 text-white hover:bg-black/60")}
          title={image.hidden ? "Show image" : "Hide image"}
        >
          {image.hidden ? <EyeOff className={iconSize} /> : <Eye className={iconSize} />}
        </button>
        <button
          type="button"
          onPointerDown={(e) => e.stopPropagation()}
          onClick={(e) => { e.stopPropagation(); onDelete(image.iid); }}
          className={cn(btnPad, "rounded bg-red-500/80 text-white hover:bg-red-600")}
          title="Delete image"
        >
          <Trash2 className={iconSize} />
        </button>
      </div>

      {image.hidden && (
        <div className="absolute bottom-0.5 right-0.5 z-10">
          <Badge variant="outline" className={cn(
            "bg-yellow-100 border-yellow-400 px-1 py-0 leading-tight",
            isMain ? "text-sm" : "text-xs"
          )}>
            Hidden
          </Badge>
        </div>
      )}

      {showOrder && (
        <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-10">
          <span className={cn("flex items-center justify-center rounded-full bg-black/30 text-white font-medium", orderSize)}>
            {index}
          </span>
        </div>
      )}

      <img
        src={src}
        alt={`Image ${image.iid}`}
        className={cn(
          "w-full h-full object-contain pointer-events-none select-none",
          isMain ? "rounded-lg" : "rounded-md"
        )}
        draggable={false}
      />
    </div>
  );
}

// ── Image Manager Section ──────────────────────────────────────────────

let newImageCounter = 0;

const normalizeImageUrlInput = (url: string) => {
  const trimmed = url.trim();
  if (!trimmed) return "";
  if (trimmed.startsWith("http://") || trimmed.startsWith("https://")) {
    return trimmed;
  }
  return `https://${trimmed}`;
};

const isLikelyWebpUrl = (url: string) => {
  try {
    const parsed = new URL(normalizeImageUrlInput(url));
    return parsed.pathname.toLowerCase().endsWith(".webp");
  } catch {
    return url.toLowerCase().includes(".webp");
  }
};

export function ImageManager({
  images,
  setImages,
  isLoading,
  maxRows = 3.2,
}: {
  images: ProductImage[];
  setImages: React.Dispatch<React.SetStateAction<ProductImage[]>>;
  isLoading: boolean;
  maxRows?: number;
}) {
  const [activeId, setActiveId] = useState<number | string | null>(null);
  const [zoomImage, setZoomImage] = useState<ProductImage | null>(null);
  const [urlInput, setUrlInput] = useState("");
  const [isUploading, setIsUploading] = useState(false);
  const [addOpen, setAddOpen] = useState(false);

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

  const handleDragStart = useCallback((event: DragStartEvent) => {
    setActiveId(event.active.id as number | string);
  }, []);

  // Live reorder during drag so the grid layout updates immediately
  const handleDragOver = useCallback(
    (event: DragOverEvent) => {
      const { active, over } = event;
      if (!over || active.id === over.id) return;

      setImages((prev) => {
        const oldIndex = prev.findIndex((img) => img.iid === active.id);
        const newIndex = prev.findIndex((img) => img.iid === over.id);
        if (oldIndex === newIndex) return prev;
        return arrayMove(prev, oldIndex, newIndex);
      });
    },
    [setImages]
  );

  const handleDragEnd = useCallback(
    (_event: DragEndEvent) => {
      setActiveId(null);
    },
    []
  );

  const toggleHidden = useCallback(
    (iid: number | string) => {
      setImages((prev) =>
        prev.map((img) =>
          img.iid === iid ? { ...img, hidden: !img.hidden } : img
        )
      );
    },
    [setImages]
  );

  const deleteImage = useCallback(
    (iid: number | string) => {
      setImages((prev) => prev.filter((img) => img.iid !== iid));
    },
    [setImages]
  );

  const addNewImage = useCallback(
    (imageUrl: string) => {
      const id = `new-${newImageCounter++}`;
      const newImg: ProductImage = {
        iid: id,
        order: 0,
        hidden: false,
        sizes: {},
        imageUrl,
        isNew: true,
      };
      setImages((prev) => [...prev, newImg]);
    },
    [setImages]
  );

  const handleFileUpload = useCallback(
    async (files: File[]) => {
      if (files.length === 0) return;
      setIsUploading(true);
      setAddOpen(false);
      try {
        for (const file of files) {
          try {
            const formData = new FormData();
            formData.append("image", file);
            const res = await apiClient.post("/api/import/upload-image", formData);
            if (res.data?.imageUrl) {
              addNewImage(res.data.imageUrl);
            }
          } catch (err) {
            const serverMessage =
              (err as { response?: { data?: { error?: string } } })?.response?.data?.error;
            toast.error(`Failed to upload ${file.name}${serverMessage ? `: ${serverMessage}` : ""}`);
          }
        }
      } finally {
        setIsUploading(false);
      }
    },
    [addNewImage]
  );

  const { getRootProps, getInputProps, isDragActive, open: openFilePicker } = useDropzone({
    accept: IMAGE_UPLOAD_ACCEPT,
    maxSize: MAX_UPLOAD_BYTES,
    multiple: true,
    noClick: true,
    noKeyboard: true,
    disabled: isUploading,
    onDrop: handleFileUpload,
    onDropRejected: (rejections) => {
      rejections.forEach((rejection) => {
        const tooLarge = rejection.errors.some((e) => e.code === "file-too-large");
        const reason = tooLarge
          ? `larger than ${formatUploadLimitMb()} limit`
          : rejection.errors[0]?.message ?? "rejected";
        toast.error(`${rejection.file.name}: ${reason}`);
      });
    },
  });

  const handleUrlAdd = useCallback(async () => {
    const url = urlInput.trim();
    if (!url) return;

    if (!isLikelyWebpUrl(url)) {
      addNewImage(url);
      setUrlInput("");
      setAddOpen(false);
      return;
    }

    setIsUploading(true);
    setAddOpen(false);

    try {
      const res = await apiClient.post("/api/import/upload-image-url", {
        imageUrl: normalizeImageUrlInput(url),
      });
      if (res.data?.imageUrl) {
        addNewImage(res.data.imageUrl);
        setUrlInput("");
      } else {
        throw new Error("Upload response did not include an image URL");
      }
    } catch (error) {
      console.error("Failed to convert WebP image URL:", error);
      toast.error(`Failed to convert WebP image URL: ${error instanceof Error ? error.message : "Unknown error"}`);
    } finally {
      setIsUploading(false);
    }
  }, [urlInput, addNewImage]);

  const activeImage = activeId
    ? images.find((img) => img.iid === activeId)
    : null;

  if (isLoading) {
    return (
      <div className="flex items-center justify-center h-40">
        <Loader2 className="h-6 w-6 animate-spin text-muted-foreground" />
      </div>
    );
  }

  const imageIds = images.map((img) => img.iid);

  const CELL = 100;
  const GAP = 6;
  const gridSize = CELL * 3 + GAP * 2;

  const maxHeight = CELL * maxRows + GAP * (maxRows - 1);

  return (
    <div style={{ width: gridSize }}>
      <DndContext
        sensors={sensors}
        collisionDetection={closestCenter}
        onDragStart={handleDragStart}
        onDragOver={handleDragOver}
        onDragEnd={handleDragEnd}
      >
        <SortableContext items={imageIds} strategy={rectSortingStrategy}>
          <div
            className="grid overflow-y-auto"
            style={{
              gridTemplateColumns: `repeat(3, ${CELL}px)`,
              gridAutoRows: CELL,
              gap: GAP,
              maxHeight,
            }}
          >
            {images.map((img, i) => (
              <SortableImageCell
                key={img.iid}
                image={img}
                index={i + 1}
                isMain={i === 0}
                showOrder={!!activeId}
                onToggleHidden={toggleHidden}
                onDelete={deleteImage}
                onZoom={setZoomImage}
              />
            ))}

            {/* Add button with file drop */}
            <div
              {...getRootProps()}
              className={cn(
                "rounded-md border-2 border-dashed transition-colors",
                isDragActive
                  ? "border-primary bg-primary/10"
                  : "border-muted-foreground/25 hover:border-muted-foreground/50"
              )}
            >
              <input {...getInputProps()} />
              <Popover open={addOpen} onOpenChange={setAddOpen}>
                <PopoverTrigger asChild>
                  <button
                    type="button"
                    disabled={isUploading}
                    className={cn(
                      "w-full h-full flex items-center justify-center transition-colors",
                      isDragActive
                        ? "text-primary"
                        : "text-muted-foreground hover:text-foreground"
                    )}
                  >
                    {isUploading ? (
                      <Loader2 className="h-4 w-4 animate-spin" />
                    ) : (
                      <Plus className="h-5 w-5" />
                    )}
                  </button>
                </PopoverTrigger>
                <PopoverContent className="w-64 p-3 space-y-2" align="start">
                  <Button
                    type="button"
                    variant="outline"
                    size="sm"
                    className="w-full justify-start gap-2"
                    onClick={() => {
                      setAddOpen(false);
                      openFilePicker();
                    }}
                  >
                    <Upload className="h-4 w-4" />
                    Upload Image
                  </Button>
                  <div className="flex gap-1.5 items-center">
                    <Link className="h-4 w-4 text-muted-foreground shrink-0" />
                    <Input
                      placeholder="Image URL..."
                      value={urlInput}
                      onChange={(e) => setUrlInput(e.target.value)}
                      onKeyDown={(e) => {
                        if (e.key === "Enter") {
                          e.preventDefault();
                          handleUrlAdd();
                        }
                      }}
                      className="text-sm h-7"
                    />
                    <Button
                      type="button"
                      size="sm"
                      onClick={handleUrlAdd}
                      disabled={!urlInput.trim()}
                      className="h-7 px-2 shrink-0"
                    >
                      Add
                    </Button>
                  </div>
                </PopoverContent>
              </Popover>
            </div>
          </div>
        </SortableContext>
        <DragOverlay>
          {activeImage ? (
            <div className="rounded-md border bg-white shadow-lg" style={{ width: CELL, height: CELL }}>
              <img
                src={getImageSrc(activeImage) ?? ""}
                alt="Dragging"
                className="w-full h-full object-cover rounded-md"
              />
            </div>
          ) : null}
        </DragOverlay>
      </DndContext>

      {/* Full-size image overlay */}
      <Dialog open={!!zoomImage} onOpenChange={(open) => !open && setZoomImage(null)}>
        <DialogContent className="max-w-3xl p-2">
          <DialogTitle className="sr-only">Product image preview</DialogTitle>
          {zoomImage && (
            <img
              src={getImageSrc(zoomImage, true) ?? ""}
              alt={`Image ${zoomImage.iid}`}
              className="w-full h-auto object-contain max-h-[80vh] rounded"
            />
          )}
        </DialogContent>
      </Dialog>
    </div>
  );
}

// ── Mini Image Preview (for minimal layout) ─────────────────────────────

export function MiniImagePreview({
  images,
  isLoading,
}: {
  images: ProductImage[];
  isLoading: boolean;
}) {
  const [zoomImage, setZoomImage] = useState<ProductImage | null>(null);

  // Match the full ImageManager grid width (CELL * 3 + GAP * 2)
  // Match the main image cell size (CELL * 2 + GAP)
  const SIZE = 206;

  if (isLoading) {
    return (
      <div className="flex items-center justify-center" style={{ width: SIZE, height: SIZE }}>
        <Loader2 className="h-5 w-5 animate-spin text-muted-foreground" />
      </div>
    );
  }

  const first = images[0];
  if (!first) return null;

  const src = getImageSrc(first);
  if (!src) return null;

  const extra = images.length - 1;

  return (
    <>
      <div
        className="relative rounded-lg border bg-white cursor-pointer shrink-0"
        style={{ width: SIZE, height: SIZE }}
        onClick={() => setZoomImage(first)}
      >
        <img
          src={src}
          alt="Product"
          className="w-full h-full object-cover rounded-lg"
        />
        {extra > 0 && (
          <Badge
            variant="secondary"
            className="absolute -bottom-1.5 -right-1.5 text-sm px-1.5 py-0 leading-relaxed"
          >
            +{extra}
          </Badge>
        )}
      </div>
      <Dialog open={!!zoomImage} onOpenChange={(open) => !open && setZoomImage(null)}>
        <DialogContent className="max-w-3xl p-2">
          <DialogTitle className="sr-only">Product image preview</DialogTitle>
          {zoomImage && (
            <img
              src={getImageSrc(zoomImage, true) ?? ""}
              alt={`Image ${zoomImage.iid}`}
              className="w-full h-auto object-contain max-h-[80vh] rounded"
            />
          )}
        </DialogContent>
      </Dialog>
    </>
  );
}
