import * as React from 'react';
import { Upload, X, FileIcon, AlertCircle } from 'lucide-react';
import { cn } from '../../shared/utils';
import { Button } from '../button';
import { useFileUpload } from './use-file-upload';

interface FileUploadProps extends Omit<
  React.InputHTMLAttributes<HTMLInputElement>,
  'onChange' | 'onError' | 'size'
> {
  onFilesChange?: (files: File[]) => void;
  maxFiles?: number;
  maxSize?: number; // in bytes
  showPreview?: boolean;
  /** Called when files are rejected due to size or count limits. */
  onError?: (rejectedFiles: File[], reason: 'size' | 'count') => void;
  /**
   * Size variant — `compact` uses a horizontal layout with smaller icons and
   * tighter spacing, for dense forms.
   * @default "default"
   */
  size?: 'default' | 'compact';
  /** Label for the clickable upload action. @default "Click to upload" */
  uploadLabel?: string;
  /** Label for the drag-and-drop hint, shown after `uploadLabel`. @default "or drag and drop" */
  dragAndDropLabel?: string;
  /** File-count hint shown when `maxFiles > 1`. `{count}` is replaced with `maxFiles`. @default "Up to {count} files" */
  multipleFilesLabel?: string;
  /** File-count hint shown when `maxFiles` is 1. @default "1 file" */
  singleFileLabel?: string;
  /** Max-size hint. `{size}` is replaced with the limit in MB. @default "Max {size}MB" */
  maxSizeLabel?: string;
  /**
   * Error shown when one or more files exceed `maxSize`. `{count}` and `{size}`
   * are replaced with the rejected file count and the limit in MB.
   * @default "{count} file(s) exceed the {size}MB limit and were not added."
   */
  sizeErrorLabel?: string;
  /**
   * Error shown when more files are selected than `maxFiles` allows. `{max}`
   * and `{count}` are replaced with the file limit and the rejected count.
   * @default "Only {max} file(s) allowed. {count} file(s) were not added."
   */
  countErrorLabel?: string;
}

/**
 * Drag-and-drop file input with preview list.
 *
 * @description
 * Manages selected file state and provides a standardized, accessible interface
 * for file uploads. Supports click-to-browse and drag-and-drop. Shows a preview
 * list of selected files with per-file remove buttons.
 *
 * Headless logic is available via `useFileUpload` for custom drop-zone UIs.
 *
 * @ai-rules
 * 1. Use `onFilesChange` to capture the selected files array in your form state.
 * 2. `maxFiles` defaults to 1 (single file). Set to a higher number for multi-upload.
 * 3. `maxSize` is in bytes (default: 5MB). Files exceeding the limit trigger `onError` and show an inline error.
 * 4. Set `showPreview={false}` if you manage the file list externally.
 * 5. Use `onError` to handle rejected files (e.g., show a toast notification).
 * 6. Use `size="compact"` for dense forms; override any label prop (e.g. `uploadLabel`) to localize the UI text.
 */
const dropzoneClasses = {
  default: 'flex-col items-center justify-center p-8',
  compact: 'flex-row items-center gap-3 p-3 text-left',
};
const uploadIconClasses = {
  default: 'mb-4 h-10 w-10',
  compact: 'h-5 w-5 shrink-0',
};
const previewContainerClasses = {
  default: 'mt-4 space-y-2',
  compact: 'mt-2 space-y-1',
};
const previewRowClasses = {
  default: 'p-3',
  compact: 'p-2',
};
const previewIconClasses = {
  default: 'h-5 w-5',
  compact: 'h-4 w-4',
};

const FileUpload = React.forwardRef<HTMLDivElement, FileUploadProps>(
  (
    {
      className,
      onFilesChange,
      maxFiles = 1,
      maxSize = 5 * 1024 * 1024, // 5MB default
      showPreview = true,
      onError,
      accept,
      disabled,
      size = 'default',
      uploadLabel = 'Click to upload',
      dragAndDropLabel = 'or drag and drop',
      multipleFilesLabel = 'Up to {count} files',
      singleFileLabel = '1 file',
      maxSizeLabel = 'Max {size}MB',
      sizeErrorLabel,
      countErrorLabel,
      ...props
    },
    ref
  ) => {
    const {
      files,
      dragActive,
      errorMessage,
      inputRef,
      handleDrag,
      handleDrop,
      handleChange,
      removeFile,
      openFileDialog,
    } = useFileUpload({
      maxFiles,
      maxSize,
      onFilesChange,
      onError,
      disabled,
      sizeErrorLabel,
      countErrorLabel,
    });

    const limitMB = (maxSize / 1024 / 1024).toFixed(0);
    const filesCountLabel =
      maxFiles > 1
        ? multipleFilesLabel.replace('{count}', String(maxFiles))
        : singleFileLabel;

    return (
      <div ref={ref} className={cn('w-full', className)}>
        <div
          onDragEnter={handleDrag}
          onDragLeave={handleDrag}
          onDragOver={handleDrag}
          onDrop={handleDrop}
          onClick={openFileDialog}
          className={cn(
            'relative flex cursor-pointer rounded-[var(--radius)] border-2 border-dashed border-border bg-background transition-colors hover:bg-muted/50',
            dropzoneClasses[size],
            dragActive && 'border-primary bg-primary/5',
            errorMessage && 'border-destructive/50',
            disabled && 'cursor-not-allowed opacity-50'
          )}
        >
          <Upload className={cn('text-muted-foreground', uploadIconClasses[size])} />
          <div>
            <p className={cn('text-foreground', size === 'default' && 'mb-2')}>
              <span className="text-primary">{uploadLabel}</span> {dragAndDropLabel}
            </p>
            <p className="text-muted-foreground">
              {filesCountLabel} • {maxSizeLabel.replace('{size}', limitMB)}
            </p>
          </div>
          <input
            {...props}
            ref={inputRef}
            type="file"
            className="hidden"
            onChange={handleChange}
            multiple={maxFiles > 1}
            accept={accept}
            disabled={disabled}
          />
        </div>

        {errorMessage && (
          <div className="mt-2 flex items-center gap-2 rounded-[var(--radius)] border border-destructive/30 bg-destructive/5 px-3 py-2 text-sm text-destructive">
            <AlertCircle className="h-4 w-4 shrink-0" />
            <span>{errorMessage}</span>
          </div>
        )}

        {showPreview && files.length > 0 && (
          <div className={previewContainerClasses[size]}>
            {files.map((file, index) => (
              <div
                key={index}
                className={cn(
                  'flex items-center justify-between rounded-[var(--radius)] border border-border bg-card',
                  previewRowClasses[size]
                )}
              >
                <div className="flex items-center gap-3">
                  <FileIcon className={cn('text-muted-foreground', previewIconClasses[size])} />
                  <div>
                    <p className="text-foreground">{file.name}</p>
                    <p className="text-muted-foreground">{(file.size / 1024).toFixed(2)} KB</p>
                  </div>
                </div>
                <Button
                  type="button"
                  variant="ghost"
                  size="sm"
                  onClick={e => {
                    e.stopPropagation();
                    removeFile(index);
                  }}
                  disabled={disabled}
                >
                  <X className="h-4 w-4" />
                </Button>
              </div>
            ))}
          </div>
        )}
      </div>
    );
  }
);
FileUpload.displayName = 'FileUpload';

export { FileUpload };
export type { FileUploadProps };
