"use client";

import { ImagePlus, Loader2, Trash2, UploadCloud, X } from "lucide-react";
import { useCallback, useRef, useState } from "react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { getAuthToken } from "@/utils/token";

// ─── Types ───────────────────────────────────────────────────────────────────

export type UploadedImage = {
  url: string;
  filename: string;
  contentType: string;
  size: number;
};

type UploadState = "idle" | "uploading" | "success" | "error";

type FileItem = {
  id: string;
  file: File;
  previewUrl: string;
  state: UploadState;
  uploaded?: UploadedImage;
  error?: string;
};

export type ImageUploaderProps = {
  /** Upload endpoint. Defaults to /brapi/v2/upload/image */
  uploadUrl?: string;
  /** Max files allowed (default: unlimited) */
  maxFiles?: number;
  /** Max file size in bytes (default: 10 MB) */
  maxSize?: number;
  /** Called when each file finishes uploading successfully */
  onUploaded?: (image: UploadedImage, file: File) => void;
  /** Called when a file is removed */
  onRemoved?: (url: string) => void;
  /** Controlled value: list of already-uploaded image URLs */
  value?: string[];
  /** Additional class for the root element */
  className?: string;
  /** Whether to show image grid preview */
  showPreview?: boolean;
  /** Disable the uploader */
  disabled?: boolean;
};

// ─── Helpers ─────────────────────────────────────────────────────────────────

function formatBytes(bytes: number): string {
  if (bytes < 1024) return `${bytes} B`;
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
  return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
}

const ALLOWED_TYPES = ["image/jpeg", "image/jpg", "image/png", "image/gif", "image/webp", "image/bmp"];
const DEFAULT_MAX_SIZE = 10 * 1024 * 1024;

// ─── Component ───────────────────────────────────────────────────────────────

export function ImageUploader({
  uploadUrl = "/brapi/v2/upload/image",
  maxFiles,
  maxSize = DEFAULT_MAX_SIZE,
  onUploaded,
  onRemoved,
  value,
  className,
  showPreview = true,
  disabled = false,
}: ImageUploaderProps) {
  const [items, setItems] = useState<FileItem[]>([]);
  const [isDragging, setIsDragging] = useState(false);
  const inputRef = useRef<HTMLInputElement>(null);

  const updateItem = useCallback((id: string, patch: Partial<FileItem>) => {
    setItems((prev) => prev.map((it) => (it.id === id ? { ...it, ...patch } : it)));
  }, []);

  const uploadFile = useCallback(
    async (item: FileItem) => {
      updateItem(item.id, { state: "uploading" });

      const form = new FormData();
      form.append("file", item.file);

      try {
        const token = getAuthToken();
        const res = await fetch(uploadUrl, {
          method: "POST",
          body: form,
          headers: token ? { Authorization: `Bearer ${token}` } : undefined,
        });
        if (!res.ok) {
          const body = await res.json().catch(() => ({}));
          throw new Error(body?.detail ?? `HTTP ${res.status}`);
        }
        const data = (await res.json()) as {
          url: string;
          filename: string;
          content_type?: string;
          size?: number;
        };
        const uploaded: UploadedImage = {
          url: data.url,
          filename: data.filename,
          contentType: data.content_type ?? item.file.type,
          size: data.size ?? item.file.size,
        };
        updateItem(item.id, { state: "success", uploaded });
        onUploaded?.(uploaded, item.file);
      } catch (err) {
        updateItem(item.id, { state: "error", error: (err as Error).message });
      }
    },
    [uploadUrl, updateItem, onUploaded]
  );

  const addFiles = useCallback(
    (files: File[]) => {
      if (disabled) return;

      const filtered = files.filter((f) => {
        if (!ALLOWED_TYPES.includes(f.type)) return false;
        if (f.size > maxSize) return false;
        return true;
      });

      const remaining = maxFiles ? maxFiles - items.length : Infinity;
      const toAdd = filtered.slice(0, remaining);

      const newItems: FileItem[] = toAdd.map((file) => ({
        id: `${Date.now()}-${Math.random()}`,
        file,
        previewUrl: URL.createObjectURL(file),
        state: "idle" as UploadState,
      }));

      setItems((prev) => [...prev, ...newItems]);

      for (const item of newItems) {
        uploadFile(item);
      }
    },
    [disabled, maxFiles, maxSize, items.length, uploadFile]
  );

  const removeItem = useCallback(
    (id: string) => {
      setItems((prev) => {
        const item = prev.find((it) => it.id === id);
        if (item) {
          URL.revokeObjectURL(item.previewUrl);
          if (item.uploaded?.url) onRemoved?.(item.uploaded.url);
        }
        return prev.filter((it) => it.id !== id);
      });
    },
    [onRemoved]
  );

  const onDragOver = useCallback((e: React.DragEvent) => {
    e.preventDefault();
    setIsDragging(true);
  }, []);

  const onDragLeave = useCallback(() => setIsDragging(false), []);

  const onDrop = useCallback(
    (e: React.DragEvent) => {
      e.preventDefault();
      setIsDragging(false);
      const files = Array.from(e.dataTransfer.files);
      addFiles(files);
    },
    [addFiles]
  );

  const onInputChange = useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      const files = Array.from(e.target.files ?? []);
      addFiles(files);
      e.target.value = "";
    },
    [addFiles]
  );

  const hasItems = items.length > 0 || (value && value.length > 0);
  const isAtMax = maxFiles !== undefined && items.length >= maxFiles;
  const canUpload = !disabled && !isAtMax;

  return (
    <div className={cn("space-y-4", className)}>
      {/* Drop zone */}
      <div
        className={cn(
          "relative flex flex-col items-center justify-center gap-3 rounded-xl border-2 border-dashed px-6 py-10 transition-colors",
          isDragging
            ? "border-primary bg-primary/5"
            : "border-border bg-muted/20 hover:border-primary/50 hover:bg-muted/30",
          disabled && "pointer-events-none opacity-50",
          isAtMax && "pointer-events-none opacity-60"
        )}
        onDragOver={onDragOver}
        onDragLeave={onDragLeave}
        onDrop={onDrop}
        onClick={() => canUpload && inputRef.current?.click()}
        role="button"
        tabIndex={canUpload ? 0 : -1}
        onKeyDown={(e) => e.key === "Enter" && canUpload && inputRef.current?.click()}
      >
        <div
          className={cn(
            "rounded-full p-4 transition-colors",
            isDragging ? "bg-primary/10" : "bg-muted"
          )}
        >
          <UploadCloud
            className={cn(
              "h-8 w-8 transition-colors",
              isDragging ? "text-primary" : "text-muted-foreground"
            )}
          />
        </div>

        <div className="text-center">
          <p className="text-sm font-medium text-foreground">
            {isDragging ? "松开以上传" : "点击或拖拽图片到此区域"}
          </p>
          <p className="mt-1 text-xs text-muted-foreground">
            支持 JPG、PNG、GIF、WebP、BMP · 最大 {formatBytes(maxSize)}
            {maxFiles ? ` · 最多 ${maxFiles} 张` : ""}
          </p>
        </div>

        {isAtMax && (
          <Badge variant="secondary" className="text-xs">
            已达上限 {maxFiles} 张
          </Badge>
        )}

        <input
          ref={inputRef}
          type="file"
          accept={ALLOWED_TYPES.join(",")}
          multiple={maxFiles !== 1}
          className="hidden"
          onChange={onInputChange}
          disabled={!canUpload}
        />
      </div>

      {/* Preview grid */}
      {showPreview && hasItems && (
        <div className="grid grid-cols-2 gap-3 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5">
          {/* Locally queued / uploading files */}
          {items.map((item) => (
            <div
              key={item.id}
              className="group relative aspect-square overflow-hidden rounded-lg border border-border bg-muted"
            >
              {/* eslint-disable-next-line @next/next/no-img-element */}
              <img
                src={item.previewUrl}
                alt={item.file.name}
                className="h-full w-full object-cover"
              />

              {/* Overlay per state */}
              {item.state === "uploading" && (
                <div className="absolute inset-0 flex flex-col items-center justify-center gap-1 bg-black/50 backdrop-blur-sm">
                  <Loader2 className="h-5 w-5 animate-spin text-white" />
                  <span className="text-xs text-white">上传中…</span>
                </div>
              )}

              {item.state === "error" && (
                <div className="absolute inset-0 flex flex-col items-center justify-center gap-1 bg-destructive/80 p-2">
                  <X className="h-5 w-5 text-white" />
                  <span className="line-clamp-2 text-center text-[10px] text-white">
                    {item.error}
                  </span>
                </div>
              )}

              {item.state === "success" && (
                <div className="absolute right-1 top-1">
                  <Badge className="bg-green-500/90 px-1.5 py-0.5 text-[10px] text-white hover:bg-green-500/90">
                    ✓
                  </Badge>
                </div>
              )}

              {/* Remove button */}
              <Button
                type="button"
                variant="destructive"
                size="icon"
                className="absolute bottom-1 right-1 h-6 w-6 opacity-0 transition-opacity group-hover:opacity-100"
                onClick={(e) => {
                  e.stopPropagation();
                  removeItem(item.id);
                }}
              >
                <Trash2 className="h-3 w-3" />
              </Button>

              {/* File name tooltip */}
              <div className="absolute bottom-0 left-0 right-0 translate-y-full bg-black/70 px-2 py-1 text-[10px] text-white opacity-0 transition-all group-hover:translate-y-0 group-hover:opacity-100">
                {item.file.name}
              </div>
            </div>
          ))}

          {/* Externally provided URLs (controlled mode) */}
          {value?.map((url) => (
            <div
              key={url}
              className="group relative aspect-square overflow-hidden rounded-lg border border-border bg-muted"
            >
              {/* eslint-disable-next-line @next/next/no-img-element */}
              <img src={url} alt={url} className="h-full w-full object-cover" />
              <Button
                type="button"
                variant="destructive"
                size="icon"
                className="absolute bottom-1 right-1 h-6 w-6 opacity-0 transition-opacity group-hover:opacity-100"
                onClick={(e) => {
                  e.stopPropagation();
                  onRemoved?.(url);
                }}
              >
                <Trash2 className="h-3 w-3" />
              </Button>
            </div>
          ))}

          {/* Add more tile */}
          {canUpload && (
            <button
              type="button"
              className="flex aspect-square items-center justify-center rounded-lg border-2 border-dashed border-border bg-muted/20 transition-colors hover:border-primary/50 hover:bg-muted/30"
              onClick={() => inputRef.current?.click()}
            >
              <ImagePlus className="h-6 w-6 text-muted-foreground" />
            </button>
          )}
        </div>
      )}
    </div>
  );
}
