import { useRef, useState, type CSSProperties, type ReactNode } from "react";
import { UploadCloud } from "lucide-react";

export type FileDropZoneProps = {
  accept?: string;
  multiple?: boolean;
  disabled?: boolean;
  busy?: boolean;
  progress?: number | null;
  label?: ReactNode;
  actionLabel?: ReactNode;
  busyLabel?: ReactNode;
  progressLabel?: ReactNode;
  note?: ReactNode;
  className?: string;
  inputLabel?: string;
  onFiles: (files: File[]) => void | Promise<void>;
};

export default function FileDropZone({
  accept,
  multiple = true,
  disabled = false,
  busy = false,
  progress = null,
  label = "Drop files here",
  actionLabel = "or click to select files",
  busyLabel = "Uploading files",
  progressLabel,
  note,
  className = "",
  inputLabel = "Drop files here or click to select files",
  onFiles
}: FileDropZoneProps) {
  const inputRef = useRef<HTMLInputElement | null>(null);
  const [dragActive, setDragActive] = useState(false);
  const progressValue = typeof progress === "number" && Number.isFinite(progress) ? Math.max(0, Math.min(100, progress)) : null;
  const showProgress = busy || progressValue !== null;
  const interactionDisabled = disabled || busy;
  const zoneClassName = `file-drop-zone ${dragActive ? "is-active" : ""} ${showProgress ? "is-busy" : ""} ${className}`.trim();
  const roundedProgress = progressValue === null ? null : Math.round(progressValue);
  const progressStyle = progressValue === null ? undefined : { "--file-drop-progress": `${progressValue}%` } as CSSProperties;

  async function handleFiles(fileList: FileList | File[]) {
    if (interactionDisabled) return;
    const files = Array.from(fileList);
    if (files.length === 0) return;
    try {
      await onFiles(files);
    } finally {
      if (inputRef.current) inputRef.current.value = "";
    }
  }

  return (
    <>
      <div
        className={zoneClassName}
        role="button"
        tabIndex={interactionDisabled ? -1 : 0}
        aria-label={inputLabel}
        aria-disabled={interactionDisabled}
        aria-busy={showProgress || undefined}
        onClick={() => {
          if (!interactionDisabled) inputRef.current?.click();
        }}
        onKeyDown={(event) => {
          if ((event.key === "Enter" || event.key === " ") && !interactionDisabled) {
            event.preventDefault();
            inputRef.current?.click();
          }
        }}
        onDragOver={(event) => {
          event.preventDefault();
          if (!interactionDisabled) setDragActive(true);
        }}
        onDragLeave={() => setDragActive(false)}
        onDrop={(event) => {
          event.preventDefault();
          setDragActive(false);
          if (!interactionDisabled) void handleFiles(event.dataTransfer.files);
        }}
      >
        {showProgress ? (
          <span
            className={`file-drop-progress ${progressValue === null ? "is-indeterminate" : ""}`.trim()}
            role="progressbar"
            aria-label="Upload progress"
            aria-valuemin={0}
            aria-valuemax={100}
            aria-valuenow={roundedProgress ?? undefined}
            style={progressStyle}
          >
            <span>{roundedProgress === null ? "" : `${roundedProgress}%`}</span>
          </span>
        ) : (
          <UploadCloud size={28} aria-hidden="true" />
        )}
        <strong>{showProgress ? busyLabel : label}</strong>
        <span>{showProgress ? progressLabel ?? (roundedProgress === null ? "Uploading…" : `${roundedProgress}% uploaded`) : actionLabel}</span>
        {note && <span className="muted small-text">{note}</span>}
      </div>
      <input
        ref={inputRef}
        type="file"
        accept={accept}
        multiple={multiple}
        hidden
        onChange={(event) => event.target.files && void handleFiles(event.target.files)}
      />
    </>
  );
}
