"use client";

import { useState, useRef, useCallback } from "react";
import {
  Card,
  CardBody,
  Divider,
  Button as BaseButton,
} from "@nextui-org/react";
import { FaUpload, FaFile } from "react-icons/fa";
import { FaXmark } from "react-icons/fa6";

type FileWithId = {
  file: File;
  id: string;
};

type FileUploadProps = {
  accept?: string;
  isMultiple?: boolean;
  maxSize?: number; // in bytes
  className?: string;
  onFilesChange?: (files: File[]) => void;
  label?: string;
  description?: string;
  isRequired?: boolean;
  isDisabled?: boolean;
};

export default function FileUpload({
  accept = "*/*",
  isMultiple = true,
  maxSize = 10 * 1024 * 1024, // 10MB default
  className = "",
  onFilesChange,
  label,
  description,
  isRequired = false,
  isDisabled = false,
}: FileUploadProps) {
  const [files, setFiles] = useState<FileWithId[]>([]);
  const [isDragOver, setIsDragOver] = useState(false);
  const [rejectedFiles, setRejectedFiles] = useState<string[]>([]);
  const fileInputRef = useRef<HTMLInputElement>(null);

  const formatFileSize = useCallback((bytes: number) => {
    if (bytes === 0) return "0 Bytes";
    const k = 1024;
    const sizes = ["Bytes", "KB", "MB", "GB"];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return (
      Number.parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i]
    );
  }, []);

  const validateFile = useCallback(
    (file: File) => {
      if (file.size > maxSize) {
        return `File size exceeds ${formatFileSize(maxSize)}`;
      }
      return null;
    },
    [maxSize, formatFileSize]
  );

  const isFileTypeAccepted = useCallback(
    (file: File) => {
      if (!accept || accept === "*/*") return true;
      const acceptedTypes = accept
        .split(",")
        .map((type) => type.trim().toLowerCase());

      const fileType = file.type.toLowerCase();
      const fileName = file.name.toLowerCase();

      return acceptedTypes.some((type) => {
        if (type.startsWith(".")) return fileName.endsWith(type);
        return fileType === type;
      });
    },
    [accept]
  );

  const addFiles = useCallback(
    (newFiles: File[]) => {
      if (isDisabled) return;

      const validFiles: FileWithId[] = [];
      const rejected: string[] = [];

      newFiles.forEach((file) => {
        if (!isFileTypeAccepted(file)) {
          rejected.push(`${file.name} (unsupported file type)`);
          return;
        }
        const error = validateFile(file);
        if (error) {
          rejected.push(`${file.name} (${error})`);
          return;
        }
        validFiles.push({
          file,
          id: crypto.randomUUID(),
        });
      });

      setRejectedFiles(rejected);

      setFiles((prevFiles) => {
        const updated = isMultiple
          ? [...prevFiles, ...validFiles]
          : validFiles.slice(0, 1);
        if (onFilesChange) onFilesChange(updated.map((f) => f.file));
        return updated;
      });
    },
    [isMultiple, onFilesChange, validateFile, isFileTypeAccepted, isDisabled]
  );

  const removeFile = (id: string) => {
    if (isDisabled) return;
    setFiles((prev) => {
      const updated = prev.filter((f) => f.id !== id);
      if (onFilesChange) onFilesChange(updated.map((f) => f.file));
      return updated;
    });
  };

  const handleFileSelect = (event: React.ChangeEvent<HTMLInputElement>) => {
    if (isDisabled) return;
    const selectedFiles = Array.from(event.target.files || []);
    addFiles(selectedFiles);
    if (fileInputRef.current) {
      fileInputRef.current.value = "";
    }
  };

  const handleDragOver = (event: React.DragEvent) => {
    event.preventDefault();
    if (isDisabled) return;
    setIsDragOver(true);
  };

  const handleDragLeave = (event: React.DragEvent) => {
    event.preventDefault();
    if (isDisabled) return;
    setIsDragOver(false);
  };

  const handleDrop = (event: React.DragEvent) => {
    event.preventDefault();
    if (isDisabled) return;
    setIsDragOver(false);
    const droppedFiles = Array.from(event.dataTransfer.files);
    addFiles(droppedFiles);
  };

  const inputId = `file-upload-${crypto.randomUUID()}`;
  const descriptionId = `${inputId}-description`;
  const combinedAriaDescribedBy = description
    ? `${descriptionId} ${inputId}-desc`
    : descriptionId;

  return (
    <div className={`w-full max-w-2xl mx-auto ${className}`}>
      {label && (
        <label
          aria-required={isRequired}
          className="block text-sm mb-1 font-bold font-inter tracking-tight"
          htmlFor={inputId}
        >
          {label}{" "}
          {isRequired && (
            <span aria-hidden="true" className="text-danger">
              *
            </span>
          )}
        </label>
      )}
      {description && (
        <p className="text-xs text-gray-500 mb-2" id={`${inputId}-desc`}>
          {description}
        </p>
      )}
      <Card className="w-full shadow-none bg-gray-400/10 text-black">
        <CardBody className="p-6">
          <div
            aria-describedby={combinedAriaDescribedBy}
            className={`border-2 border-dashed rounded-lg p-4 text-center transition-colors ${
              isDisabled
                ? "border-gray-300 bg-gray-200 text-gray-500 cursor-not-allowed"
                : isDragOver
                ? "border-pfrBasicBlue bg-pfrBasicBlue/5 cursor-pointer"
                : "border-gray-300 hover:border-gray-400 cursor-pointer"
            }`}
            id={descriptionId}
            role="button"
            tabIndex={isDisabled ? -1 : 0}
            onClick={() => !isDisabled && fileInputRef.current?.click()}
            onDragLeave={handleDragLeave}
            onDragOver={handleDragOver}
            onDrop={handleDrop}
            onKeyDown={(event) => {
              if (!isDisabled && (event.key === "Enter" || event.key === " ")) {
                event.preventDefault();
                fileInputRef.current?.click();
              }
            }}
          >
            <FaUpload className="size-8 mx-auto my-4 text-gray-400" />
            <h3 className="font-medium mb-2">
              Drop your files here or click to browse.
            </h3>
            <p className="text-gray-500 mb-4 text-sm">
              {isMultiple ? "Select multiple files" : "Select a file"} up to{" "}
              {formatFileSize(maxSize)}.
            </p>
            <input
              ref={fileInputRef}
              accept={accept}
              aria-describedby={combinedAriaDescribedBy}
              aria-required={isRequired}
              className="hidden"
              disabled={isDisabled}
              id={inputId}
              multiple={isMultiple}
              type="file"
              onChange={handleFileSelect}
            />
          </div>

          {rejectedFiles.length > 0 && (
            <div className="mt-4 p-3 border border-red-400 rounded bg-red-50 text-sm text-red-700">
              <p className="font-medium pb-1">Rejected files:</p>
              <ul className="list-disc list-inside">
                {rejectedFiles.map((msg, idx) => (
                  <li key={idx}>{msg}</li>
                ))}
              </ul>
            </div>
          )}

          {files.length > 0 && (
            <>
              <Divider className="my-6" />
              <div>
                <h4 className="font-medium text-sm">Selected files:</h4>
                {files.map(({ file, id }) => (
                  <Card
                    key={id}
                    className="my-2 p-1 shadow-none bg-gray-600/5 dark:bg-gray-400/10 text-black"
                  >
                    <div className="flex items-center justify-between">
                      <div className="flex items-center space-x-3 flex-1 ps-1">
                        <FaFile className="w-4 h-4" />
                        <p className="text-xs font-medium truncate flex-1 min-w-0">
                          {file.name}
                        </p>
                        <p className="text-xs text-gray-500">
                          {formatFileSize(file.size)}
                        </p>
                      </div>
                      <BaseButton
                        isIconOnly
                        className="ms-1"
                        color="danger"
                        disabled={isDisabled}
                        size="sm"
                        variant="light"
                        onPress={() => removeFile(id)}
                      >
                        <FaXmark className="size-4" />
                      </BaseButton>
                    </div>
                  </Card>
                ))}
              </div>
            </>
          )}
        </CardBody>
      </Card>
    </div>
  );
}
