'use client';

import { useCallback, useRef, useState, useMemo } from 'react';
import { useUploady } from '@rpldy/uploady';
import { Upload } from 'lucide-react';
import { cn } from '@djangocfg/ui-core/lib';
import { useT } from '@djangocfg/i18n';
import { buildAcceptString, logger } from '../utils';
import { useClipboardPaste } from '../hooks/useClipboardPaste';
import type { UploadDropzoneProps } from '../types';

function useOptionalUploady(uploadFn?: (files: File[]) => void) {
  try {
    // eslint-disable-next-line react-hooks/rules-of-hooks
    const { upload } = useUploady();
    return uploadFn ?? upload;
  } catch {
    // Not inside UploadProvider — use uploadFn if provided, otherwise noop
    return uploadFn ?? (() => {});
  }
}

export function UploadDropzone({
  accept = ['image', 'audio', 'video', 'document'],
  multiple = true,
  maxSizeMB = 100,
  compact = false,
  disabled = false,
  className,
  children,
  onFilesSelected,
  uploadFn,
  pasteEnabled = true,
  onPasteNoMatch,
}: UploadDropzoneProps) {
  const t = useT();
  const upload = useOptionalUploady(uploadFn);
  const inputRef = useRef<HTMLInputElement>(null);
  const [isDragging, setIsDragging] = useState(false);
  const dragCounter = useRef(0);

  // Prepare data
  const acceptString = useMemo(() => buildAcceptString(accept), [accept]);
  const maxBytes = maxSizeMB * 1024 * 1024;

  // Prepare labels
  const labels = useMemo(() => ({
    dragDrop: t('tools.upload.dragDrop'),
    dropHere: t('tools.upload.dropHere'),
    maxSize: t('tools.upload.maxSize', { size: maxSizeMB }),
  }), [t, maxSizeMB]);

  // Prepare styles
  const containerClassName = useMemo(() => cn(
    'relative flex flex-col items-center justify-center',
    'border-2 border-dashed rounded-lg cursor-pointer',
    'transition-colors duration-200',
    compact ? 'p-4' : 'p-8',
    isDragging
      ? 'border-primary bg-primary/5'
      : 'border-muted-foreground/25 hover:border-muted-foreground/50',
    disabled && 'opacity-50 cursor-not-allowed',
    className
  ), [compact, isDragging, disabled, className]);

  const iconClassName = cn('text-muted-foreground mb-2', compact ? 'h-6 w-6' : 'h-10 w-10');
  const textClassName = cn('text-muted-foreground text-center', compact ? 'text-sm' : 'text-base');

  // Handlers
  const handleFiles = useCallback((files: FileList | File[]) => {
    const fileArray = Array.from(files);

    const validFiles = fileArray.filter(file => {
      if (file.size > maxBytes) {
        logger.warn(`File ${file.name} exceeds max size of ${maxSizeMB}MB`);
        return false;
      }
      return true;
    });

    if (validFiles.length > 0) {
      onFilesSelected?.(validFiles);
      upload(validFiles);
    }
  }, [upload, maxBytes, maxSizeMB, onFilesSelected]);

  // Build accept MIME types for clipboard paste (e.g. ['image', 'video'] → ['image', 'video'])
  const pasteAcceptTypes = useMemo(
    () => accept.map((assetType) => (assetType === 'document' ? '' : assetType)).filter(Boolean),
    [accept],
  );

  useClipboardPaste({
    enabled: pasteEnabled && !disabled,
    acceptTypes: pasteAcceptTypes.length ? pasteAcceptTypes : undefined,
    maxBytes: maxBytes,
    onFiles: (files) => {
      const toUpload = multiple ? files : files.slice(0, 1);
      onFilesSelected?.(toUpload);
      upload(toUpload);
    },
    onNoMatch: onPasteNoMatch,
  });

  const handleDragEnter = useCallback((e: React.DragEvent) => {
    e.preventDefault();
    e.stopPropagation();
    dragCounter.current++;
    if (e.dataTransfer.items?.length) {
      setIsDragging(true);
    }
  }, []);

  const handleDragLeave = useCallback((e: React.DragEvent) => {
    e.preventDefault();
    e.stopPropagation();
    dragCounter.current--;
    if (dragCounter.current === 0) {
      setIsDragging(false);
    }
  }, []);

  const handleDragOver = useCallback((e: React.DragEvent) => {
    e.preventDefault();
    e.stopPropagation();
  }, []);

  const handleDrop = useCallback((e: React.DragEvent) => {
    e.preventDefault();
    e.stopPropagation();
    dragCounter.current = 0;
    setIsDragging(false);

    if (!disabled && e.dataTransfer.files?.length) {
      handleFiles(e.dataTransfer.files);
    }
  }, [disabled, handleFiles]);

  const handleClick = useCallback(() => {
    if (!disabled) {
      inputRef.current?.click();
    }
  }, [disabled]);

  const handleInputChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
    if (e.target.files?.length) {
      handleFiles(e.target.files);
    }
    e.target.value = '';
  }, [handleFiles]);

  // Prepare content
  const displayText = isDragging ? labels.dropHere : labels.dragDrop;
  const showMaxSize = !compact;

  return (
    <div
      onClick={handleClick}
      onDragEnter={handleDragEnter}
      onDragLeave={handleDragLeave}
      onDragOver={handleDragOver}
      onDrop={handleDrop}
      className={containerClassName}
    >
      <input
        ref={inputRef}
        type="file"
        accept={acceptString}
        multiple={multiple}
        onChange={handleInputChange}
        className="hidden"
        disabled={disabled}
      />

      {children || (
        <>
          <Upload className={iconClassName} />
          <p className={textClassName}>{displayText}</p>
          {showMaxSize && (
            <p className="text-xs text-muted-foreground/60 mt-1">{labels.maxSize}</p>
          )}
        </>
      )}
    </div>
  );
}
