"use client";

import { useCallback, useRef, useState } from "react";
import { UploadCloud } from "lucide-react";
import { toast } from "sonner";
import type { FileDto } from "@use2pdf/shared-types";
import { cn } from "@/lib/cn";
import { apiFetch, ApiRequestError } from "@/lib/api-client";

const MAX_UPLOAD_SIZE_MB = 50;

export type AcceptKind = "pdf" | "image" | "txt" | "pdf-or-image" | "any";

const ACCEPT_SPECS: Record<AcceptKind, { exts: string[]; inputAccept: string; label: string }> = {
  pdf: { exts: [".pdf"], inputAccept: "application/pdf,.pdf", label: "PDF" },
  image: { exts: [".png", ".jpg", ".jpeg"], inputAccept: "image/png,image/jpeg,.png,.jpg,.jpeg", label: "PNG or JPG" },
  txt: { exts: [".txt"], inputAccept: "text/plain,.txt", label: "TXT" },
  "pdf-or-image": {
    exts: [".pdf", ".png", ".jpg", ".jpeg"],
    inputAccept: "application/pdf,image/png,image/jpeg,.pdf,.png,.jpg,.jpeg",
    label: "PDF, PNG or JPG",
  },
  any: {
    exts: [".pdf", ".png", ".jpg", ".jpeg", ".txt"],
    inputAccept: "application/pdf,image/png,image/jpeg,text/plain,.pdf,.png,.jpg,.jpeg,.txt",
    label: "PDF, PNG, JPG or TXT",
  },
};

interface FileUploadDropzoneProps {
  onUploaded: (files: FileDto[]) => void;
  accept?: AcceptKind;
  multiple?: boolean;
  className?: string;
}

export function FileUploadDropzone({
  onUploaded,
  accept = "pdf",
  multiple = true,
  className,
}: FileUploadDropzoneProps) {
  const inputRef = useRef<HTMLInputElement>(null);
  const [isDragOver, setIsDragOver] = useState(false);
  const [isUploading, setIsUploading] = useState(false);
  const spec = ACCEPT_SPECS[accept];

  const uploadFiles = useCallback(
    async (fileList: FileList | File[]) => {
      let files = Array.from(fileList);
      if (!multiple) files = files.slice(0, 1);
      if (files.length === 0) return;

      for (const file of files) {
        const lower = file.name.toLowerCase();
        if (!spec.exts.some((ext) => lower.endsWith(ext))) {
          toast.error(`${file.name}: only ${spec.label} files are accepted here`);
          return;
        }
        if (file.size > MAX_UPLOAD_SIZE_MB * 1024 * 1024) {
          toast.error(`${file.name} exceeds the ${MAX_UPLOAD_SIZE_MB} MB limit`);
          return;
        }
      }

      const formData = new FormData();
      files.forEach((file) => formData.append("files", file));

      setIsUploading(true);
      try {
        const result = await apiFetch<{ files: FileDto[] }>("/files/upload", {
          method: "POST",
          body: formData,
        });
        toast.success(
          result.files.length === 1
            ? `Uploaded ${result.files[0].originalName}`
            : `Uploaded ${result.files.length} files`,
        );
        onUploaded(result.files);
      } catch (err) {
        const message =
          err instanceof ApiRequestError ? err.message : "Upload failed. Please try again.";
        toast.error(message);
      } finally {
        setIsUploading(false);
        if (inputRef.current) inputRef.current.value = "";
      }
    },
    [onUploaded, multiple, spec],
  );

  return (
    <div
      role="button"
      tabIndex={0}
      aria-label={`Upload ${spec.label} files`}
      onClick={() => inputRef.current?.click()}
      onKeyDown={(e) => {
        if (e.key === "Enter" || e.key === " ") inputRef.current?.click();
      }}
      onDragOver={(e) => {
        e.preventDefault();
        setIsDragOver(true);
      }}
      onDragLeave={() => setIsDragOver(false)}
      onDrop={(e) => {
        e.preventDefault();
        setIsDragOver(false);
        uploadFiles(e.dataTransfer.files);
      }}
      className={cn(
        "flex cursor-pointer flex-col items-center justify-center gap-3 rounded-lg border-2 border-dashed border-slate-300 bg-white px-6 py-10 text-center transition-colors hover:border-brand-500 hover:bg-brand-50/50 dark:border-slate-600 dark:bg-slate-800 dark:hover:border-brand-500 dark:hover:bg-slate-800",
        isDragOver && "border-brand-500 bg-brand-50 dark:bg-slate-700",
        isUploading && "pointer-events-none opacity-60",
        className,
      )}
    >
      <UploadCloud className="h-10 w-10 text-brand-600" />
      <div>
        <p className="font-medium text-slate-900 dark:text-slate-100">
          {isUploading
            ? "Uploading…"
            : `Drop ${spec.label} file${multiple ? "s" : ""} here or click to browse`}
        </p>
        <p className="mt-1 text-sm text-slate-500 dark:text-slate-400">
          Up to {MAX_UPLOAD_SIZE_MB} MB per file
        </p>
      </div>
      <input
        ref={inputRef}
        type="file"
        accept={spec.inputAccept}
        multiple={multiple}
        className="hidden"
        onChange={(e) => e.target.files && uploadFiles(e.target.files)}
      />
    </div>
  );
}
