"use client";

import { useAction } from "convex/react";
import { api } from "@/convex/_generated/api";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { cn } from "@/lib/utils";
import { Spinner } from "@/components/ui/spinner";
import { Upload, Check, X, AlertCircle } from "lucide-react";
import { Progress } from "@/components/ui/progress";

type Uploaded = { storageId: string; name: string; size?: number; type?: string; previewUrl?: string };

export function Dropzone({
  onUploaded,
  maxFiles = 5,
  maxSize = 5 * 1024 * 1024,
  multiple = true,
  className,
  currentFileCount = 0,
  currentTotalBytes = 0,
  disabled = false,
}: {
  onUploaded?: (files: Uploaded[]) => void;
  maxFiles?: number;
  maxSize?: number;
  multiple?: boolean;
  className?: string;
  currentFileCount?: number;
  currentTotalBytes?: number;
  disabled?: boolean;
}) {
  const generateUrl = useAction(api.files.generateUploadUrl);
  const inputRef = useRef<HTMLInputElement>(null);
  const [drag, setDrag] = useState(false);
  const [items, setItems] = useState<
    Array<{ id: string; name: string; progress: number; status: "uploading" | "done" | "error" }>
  >([]);

  const normalizedFileCount = Math.max(0, currentFileCount);
  const normalizedTotalBytes = Math.max(0, currentTotalBytes);
  const remainingSlots = Math.max(0, maxFiles - normalizedFileCount);
  const perFileLimitMb = Math.max(1, Math.round(maxSize / (1024 * 1024)));

  // If the parent cleared all selected attachments after a submit,
  // also clear the local status list so we don't keep showing
  // items with the "Pronto" badge and remove button.
  const prevCountRef = useRef<number>(normalizedFileCount);
  useEffect(() => {
    if (prevCountRef.current > 0 && normalizedFileCount === 0) {
      setItems([]);
    }
    prevCountRef.current = normalizedFileCount;
  }, [normalizedFileCount]);

  const infoMessage = useMemo(() => {
    if (normalizedFileCount === 0) {
      return `Cada arquivo com até ${perFileLimitMb}MB • Você pode anexar até ${maxFiles} ${maxFiles === 1 ? "arquivo" : "arquivos"}`;
    }
    const totalLabel = normalizedTotalBytes > 0 ? ` (${formatBytes(normalizedTotalBytes)})` : "";
    if (remainingSlots === 0) {
      return `Limite atingido: ${normalizedFileCount}/${maxFiles} arquivos anexados${totalLabel}`;
    }
    return `${normalizedFileCount}/${maxFiles} arquivos anexados${totalLabel} • Restam ${remainingSlots} ${remainingSlots === 1 ? "arquivo" : "arquivos"}`;
  }, [normalizedFileCount, normalizedTotalBytes, remainingSlots, maxFiles, perFileLimitMb]);

  const startUpload = useCallback(async (files: FileList | File[]) => {
    if (disabled) return;
    const availableSlots = Math.max(0, maxFiles - normalizedFileCount);
    if (availableSlots <= 0) {
      return;
    }
    const list = Array.from(files).slice(0, availableSlots);
    const uploaded: Uploaded[] = [];
    for (const file of list) {
      if (file.size > maxSize) {
        continue;
      }
      const url = await generateUrl({});
      const id = `${file.name}-${file.size}-${Date.now()}`;
      const localPreview = file.type.startsWith("image/") ? URL.createObjectURL(file) : undefined;
      setItems((prev) => [...prev, { id, name: file.name, progress: 0, status: "uploading" }]);
      await new Promise<void>((resolve) => {
        const xhr = new XMLHttpRequest();
        xhr.open("POST", url);
        if (file.type) {
          xhr.setRequestHeader("Content-Type", file.type);
        }
        xhr.upload.onprogress = (e) => {
          if (!e.lengthComputable) return;
          const progress = Math.round((e.loaded / e.total) * 100);
          setItems((prev) => prev.map((it) => (it.id === id ? { ...it, progress } : it)));
        };
        xhr.onload = () => {
          try {
            const res = JSON.parse(xhr.responseText);
            if (res?.storageId) {
              uploaded.push({ storageId: res.storageId, name: file.name, size: file.size, type: file.type, previewUrl: localPreview });
              setItems((prev) =>
                prev.map((it) => (it.id === id ? { ...it, progress: 100, status: "done" } : it))
              );
            } else {
              setItems((prev) =>
                prev.map((it) => (it.id === id ? { ...it, status: "error" } : it))
              );
            }
          } catch {
            setItems((prev) =>
              prev.map((it) => (it.id === id ? { ...it, status: "error" } : it))
            );
          }
          resolve();
        };
        xhr.onerror = () => {
          setItems((prev) =>
            prev.map((it) => (it.id === id ? { ...it, status: "error" } : it))
          );
          resolve();
        };
        xhr.send(file);
      });
    }
    if (uploaded.length) onUploaded?.(uploaded);
  }, [disabled, generateUrl, maxFiles, maxSize, normalizedFileCount, onUploaded]);

  return (
    <div className={cn("space-y-3", className)}>
      <div
        role="button"
        tabIndex={disabled ? -1 : 0}
        className={cn(
          "group relative rounded-xl border border-dashed border-black/30 bg-white p-6 text-center transition-all focus:outline-none focus:ring-2 focus:ring-[#00d6eb]/40 focus:ring-offset-2",
          drag && !disabled ? "border-black bg-black/5" : undefined,
          disabled
            ? "cursor-not-allowed opacity-60 focus:ring-0"
            : "hover:border-black hover:bg-black/5"
        )}
        onClick={() => {
          if (disabled) return
          inputRef.current?.click()
        }}
        onKeyDown={(event) => {
          if (disabled) return
          if (event.key === "Enter" || event.key === " ") {
            event.preventDefault()
            inputRef.current?.click()
          }
        }}
        onDragEnter={(e) => {
          if (disabled) return
          e.preventDefault()
          setDrag(true)
        }}
        onDragOver={(e) => {
          if (disabled) return
          e.preventDefault()
          setDrag(true)
        }}
        onDragLeave={(e) => {
          if (disabled) return
          e.preventDefault()
          setDrag(false)
        }}
        onDrop={(e) => {
          if (disabled) return
          e.preventDefault()
          setDrag(false)
          startUpload(e.dataTransfer.files)
        }}
      >
        <input
          ref={inputRef}
          type="file"
          className="sr-only"
          multiple={multiple}
          disabled={disabled}
          onChange={(e) => e.target.files && startUpload(e.target.files)}
        />
        <div className="flex flex-col items-center gap-3">
          <div className="flex h-12 w-12 items-center justify-center rounded-full bg-[#e5f9fc] text-[#0f172a] shadow-sm transition group-hover:bg-black group-hover:text-white">
            {items.some((it) => it.status === "uploading") ? (
              <Spinner />
            ) : (
              <Upload className="size-5" />
            )}
          </div>
          <p className="text-sm text-neutral-800">
            Arraste arquivos aqui ou <span className="font-semibold text-black underline decoration-dotted">selecione</span>
          </p>
          <p className="text-xs text-neutral-500 text-center">{infoMessage}</p>
        </div>
      </div>
      {items.length > 0 && (
        <div className="space-y-2">
          {items.map((it) => {
            const isUploading = it.status === "uploading";
            const isDone = it.status === "done";
            const isError = it.status === "error";
            return (
              <div key={it.id} className="flex items-center justify-between gap-3 rounded-md border p-2 text-sm">
                <div className="flex flex-1 items-center gap-3 overflow-hidden">
                  <span className="truncate">{it.name}</span>
                  {isError ? (
                    <span className="inline-flex items-center gap-1 text-xs text-rose-600">
                      <AlertCircle className="size-3.5" /> Falhou
                    </span>
                  ) : null}
                </div>
                <div className="flex items-center gap-2">
                  {isUploading ? (
                    <>
                      <Progress value={it.progress} className="h-1.5 w-24" />
                      <span className="w-10 text-right text-xs text-neutral-500">{it.progress}%</span>
                    </>
                  ) : (
                    <>
                      <span
                        className={cn(
                          "inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium",
                          isDone ? "bg-emerald-50 text-emerald-700" : "bg-rose-50 text-rose-700"
                        )}
                      >
                        {isDone ? <Check className="size-3.5" /> : <AlertCircle className="size-3.5" />}
                        {isDone ? "Pronto" : "Erro"}
                      </span>
                      <button
                        type="button"
                        className="inline-flex size-7 items-center justify-center rounded-full border border-slate-200 bg-white text-neutral-600 transition hover:bg-slate-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-300"
                        aria-label="Remover item"
                        onClick={() => setItems((prev) => prev.filter((item) => item.id !== it.id))}
                      >
                        <X className="size-3.5" />
                      </button>
                    </>
                  )}
                </div>
              </div>
            )
          })}
        </div>
      )}
    </div>
  )
}

function formatBytes(value: number) {
  if (!Number.isFinite(value) || value <= 0) return "0 B";
  const units = ["B", "KB", "MB", "GB", "TB"];
  let size = value;
  let unitIndex = 0;
  while (size >= 1024 && unitIndex < units.length - 1) {
    size /= 1024;
    unitIndex += 1;
  }
  const precision = size >= 10 || unitIndex === 0 ? 0 : 1;
  return `${size.toFixed(precision)} ${units[unitIndex]}`;
}
