import { useRef, useState, type ChangeEvent, type DragEvent } from "react";
import "./fileUpload.css";

export type FileUploadSize = "lg" | "sm";
export type FileUploadItemStatus = "uploaded" | "uploading" | "error";

export type FileUploadFile = {
  errorLabel?: string;
  id: string;
  name: string;
  sizeLabel: string;
  status: FileUploadItemStatus;
  versionLabel?: string;
};

export type FileUploadProps = {
  accept?: string;
  browseLabel?: string;
  dir?: "ltr" | "rtl" | "auto";
  disabled?: boolean;
  files?: FileUploadFile[];
  helperText?: string;
  multiple?: boolean;
  onBrowse?: () => void;
  onCancel?: (fileId: string) => void;
  onDrop?: (files: FileList) => void;
  onRemove?: (fileId: string) => void;
  onRetry?: (fileId: string) => void;
  prompt?: string;
  size?: FileUploadSize;
};

export function FileUpload({
  accept,
  browseLabel,
  dir = "ltr",
  disabled = false,
  files = [],
  helperText,
  multiple = false,
  onBrowse,
  onCancel,
  onDrop,
  onRemove,
  onRetry,
  prompt,
  size = "lg",
}: FileUploadProps) {
  const inputRef = useRef<HTMLInputElement | null>(null);
  const [isDragging, setIsDragging] = useState(false);

  function browse() {
    if (disabled) return;
    onBrowse?.();
    inputRef.current?.click();
  }

  function handleInputChange(event: ChangeEvent<HTMLInputElement>) {
    const selectedFiles = event.currentTarget.files;
    if (selectedFiles?.length) {
      onDrop?.(selectedFiles);
    }
    event.currentTarget.value = "";
  }

  function handleDragOver(event: DragEvent<HTMLDivElement>) {
    if (disabled) return;
    event.preventDefault();
    setIsDragging(true);
  }

  function handleDrop(event: DragEvent<HTMLDivElement>) {
    if (disabled) return;
    event.preventDefault();
    setIsDragging(false);
    if (event.dataTransfer.files.length) {
      onDrop?.(event.dataTransfer.files);
    }
  }

  return (
    <div className={`ds-file-upload ds-file-upload--${size}`} dir={dir}>
      <div
        aria-disabled={disabled || undefined}
        className={isDragging ? "ds-file-upload__dropzone is-dragging" : "ds-file-upload__dropzone"}
        onDragLeave={() => setIsDragging(false)}
        onDragOver={handleDragOver}
        onDrop={handleDrop}
      >
        <input
          accept={accept}
          className="ds-file-upload__input"
          disabled={disabled}
          multiple={multiple}
          onChange={handleInputChange}
          ref={inputRef}
          type="file"
        />
        <span className="ds-file-upload__icon" aria-hidden="true">
          <UploadIcon />
        </span>
        {(prompt || browseLabel) && (
          <p>
            {prompt}{" "}
            {browseLabel && (
              <button className="ds-file-upload__browse" disabled={disabled} onClick={browse} type="button">
                {browseLabel}
              </button>
            )}
          </p>
        )}
        {helperText && <span className="ds-file-upload__helper">{helperText}</span>}
      </div>

      {files.length > 0 && (
        <ul className="ds-file-upload__list" aria-label="Queued files">
          {files.map((file) => (
            <FileUploadRow
              file={file}
              key={file.id}
              onCancel={onCancel}
              onRemove={onRemove}
              onRetry={onRetry}
              size={size}
            />
          ))}
        </ul>
      )}
    </div>
  );
}

function FileUploadRow({
  file,
  onCancel,
  onRemove,
  onRetry,
  size,
}: {
  file: FileUploadFile;
  onCancel?: (fileId: string) => void;
  onRemove?: (fileId: string) => void;
  onRetry?: (fileId: string) => void;
  size: FileUploadSize;
}) {
  return (
    <li className={`ds-file-upload-row ds-file-upload-row--${file.status} ds-file-upload-row--${size}`}>
      <span className="ds-file-upload-row__file-icon" aria-hidden="true">
        <FileIcon />
      </span>
      <div className="ds-file-upload-row__copy">
        <strong title={file.name}>{file.name}</strong>
        <span>
          {file.sizeLabel}
          {file.versionLabel ? ` - ${file.versionLabel}` : ""}
        </span>
        {file.status === "error" && file.errorLabel && <em>{file.errorLabel}</em>}
      </div>
      <StatusIcon status={file.status} />
      <div className="ds-file-upload-row__actions">
        {file.status === "uploading" && (
          <button aria-label={`Cancel upload for ${file.name}`} onClick={() => onCancel?.(file.id)} type="button">
            Cancel
          </button>
        )}
        {file.status === "error" && (
          <button aria-label={`Retry upload for ${file.name}`} onClick={() => onRetry?.(file.id)} type="button">
            Retry
          </button>
        )}
        {file.status !== "uploading" && (
          <button aria-label={`Remove ${file.name}`} onClick={() => onRemove?.(file.id)} type="button">
            Remove
          </button>
        )}
      </div>
    </li>
  );
}

function StatusIcon({ status }: { status: FileUploadItemStatus }) {
  return (
    <span className={`ds-file-upload-row__status ds-file-upload-row__status--${status}`} aria-label={status}>
      {status === "uploaded" && <CheckIcon />}
      {status === "uploading" && <SpinnerIcon />}
      {status === "error" && <WarningIcon />}
    </span>
  );
}

function UploadIcon() {
  return (
    <svg viewBox="0 0 24 24">
      <path d="M12 16V5m0 0 4 4m-4-4-4 4M5 16v2.5A1.5 1.5 0 0 0 6.5 20h11a1.5 1.5 0 0 0 1.5-1.5V16" fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.8" />
    </svg>
  );
}

function FileIcon() {
  return (
    <svg viewBox="0 0 24 24">
      <path d="M7 3.5h6.8L18 7.7v12.8H7V3.5Z" fill="none" stroke="currentColor" strokeLinejoin="round" strokeWidth="1.8" />
      <path d="M13.5 3.8v4.4h4.2" fill="none" stroke="currentColor" strokeLinejoin="round" strokeWidth="1.8" />
    </svg>
  );
}

function CheckIcon() {
  return (
    <svg viewBox="0 0 20 20">
      <path d="m4.5 10 3.4 3.4 7.6-7.8" fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.9" />
    </svg>
  );
}

function SpinnerIcon() {
  return (
    <svg viewBox="0 0 20 20">
      <path d="M10 3a7 7 0 1 1-6.2 3.8" fill="none" stroke="currentColor" strokeLinecap="round" strokeWidth="1.8" />
    </svg>
  );
}

function WarningIcon() {
  return (
    <svg viewBox="0 0 20 20">
      <path d="M10 3.2 18 17H2l8-13.8Z" fill="none" stroke="currentColor" strokeLinejoin="round" strokeWidth="1.6" />
      <path d="M10 8v4m0 2.5h.1" fill="none" stroke="currentColor" strokeLinecap="round" strokeWidth="1.9" />
    </svg>
  );
}
