import { useState, useRef, useCallback } from 'react';
import { Button } from './button';
import { Card } from './card';
import { cn } from '@/lib/utils';
import {
  Upload,
  X,
  Image,
  FileText,
  Video,
  Music,
  CheckCircle,
  AlertCircle,
} from 'lucide-react';

/**
 * FileUploadProps - Propriétés du composant FileUpload
 *
 * @interface FileUploadProps
 */
export interface FileUploadProps {
  /**
   * Fonction appelée lorsque des fichiers sont sélectionnés
   *
   * @param {File[]} files - Tableau de fichiers sélectionnés
   */
  onFileSelect: (files: File[]) => void;

  /**
   * Types de fichiers acceptés (attribut accept HTML)
   *
   * @example
   * ```tsx
   * <FileUpload accept="image/*" />
   * <FileUpload accept=".pdf,.doc,.docx" />
   * ```
   */
  accept?: string;

  /**
   * Si `true`, permet la sélection de plusieurs fichiers
   *
   * @default false
   */
  multiple?: boolean;

  /**
   * Taille maximale d'un fichier en bytes
   *
   * @example
   * ```tsx
   * <FileUpload maxSize={5 * 1024 * 1024} /> // 5MB
   * ```
   */
  maxSize?: number;

  /**
   * Si `true`, affiche une preview des fichiers sélectionnés
   *
   * @default true
   */
  showPreview?: boolean;

  /**
   * Si `true`, désactive le composant
   *
   * @default false
   */
  disabled?: boolean;

  /**
   * Classes CSS personnalisées
   */
  className?: string;
}

/**
 * FileWithPreview - Interface étendue pour les fichiers avec preview
 *
 * @interface FileWithPreview
 * @extends File
 */
interface FileWithPreview extends File {
  /**
   * URL de preview (pour les images)
   */
  preview?: string;

  /**
   * Statut du fichier
   *
   * - `pending`: En attente
   * - `uploading`: En cours d'upload
   * - `success`: Upload réussi
   * - `error`: Erreur d'upload
   */
  status?: 'pending' | 'uploading' | 'success' | 'error';

  /**
   * Progression de l'upload (0-100)
   */
  progress?: number;

  /**
   * Message d'erreur si applicable
   */
  error?: string;
}

const FILE_ICONS = {
  image: Image,
  video: Video,
  audio: Music,
  default: FileText,
};

const formatFileSize = (bytes: number): string => {
  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 `${Math.round((bytes / Math.pow(k, i)) * 100) / 100} ${sizes[i]}`;
};

const getFileIcon = (file: File) => {
  if (file.type && file.type.startsWith('image/')) return FILE_ICONS.image;
  if (file.type && file.type.startsWith('video/')) return FILE_ICONS.video;
  if (file.type && file.type.startsWith('audio/')) return FILE_ICONS.audio;
  return FILE_ICONS.default;
};

const createPreview = (file: File): Promise<string | null> => {
  return new Promise((resolve) => {
    if (file.type && file.type.startsWith('image/')) {
      const reader = new FileReader();
      reader.onload = (e) => resolve(e.target?.result as string);
      reader.onerror = () => resolve(null);
      reader.readAsDataURL(file);
    } else {
      resolve(null);
    }
  });
};

/**
 * FileUpload - Composant d'upload de fichiers avec drag & drop et preview
 *
 * Composant d'upload de fichiers avec support pour :
 * - Drag and drop
 * - Sélection par clic
 * - Preview des fichiers (images)
 * - Validation de taille et type
 * - Affichage des erreurs
 * - Support multi-fichiers
 *
 * @example
 * ```tsx
 * // Upload simple
 * <FileUpload
 *   onFileSelect={(files) => handleUpload(files)}
 *   accept="image/*"
 * />
 * ```
 *
 * @example
 * ```tsx
 * // Upload avec validation et preview
 * <FileUpload
 *   onFileSelect={handleFiles}
 *   accept=".pdf,.doc,.docx"
 *   multiple
 *   maxSize={10 * 1024 * 1024} // 10MB
 *   showPreview={true}
 * />
 * ```
 *
 * @component
 * @param {FileUploadProps} props - Propriétés du composant
 * @returns {JSX.Element} Zone d'upload avec drag-drop et preview
 */
export function FileUpload({
  onFileSelect,
  accept,
  multiple = false,
  maxSize,
  showPreview = true,
  disabled = false,
  className,
}: FileUploadProps) {
  const [dragActive, setDragActive] = useState(false);
  const [files, setFiles] = useState<FileWithPreview[]>([]);
  const [errors, setErrors] = useState<string[]>([]);
  const fileInputRef = useRef<HTMLInputElement>(null);

  const validateFile = (file: File): string | null => {
    // Validation du type
    if (accept) {
      const acceptedTypes = accept.split(',').map((type) => type.trim());
      const fileExtension = `.${file.name.split('.').pop()?.toLowerCase()}`;
      const fileType = file.type.toLowerCase();

      const isAccepted = acceptedTypes.some((type) => {
        if (type.startsWith('.')) {
          return type.toLowerCase() === fileExtension;
        }
        if (type.includes('/')) {
          return (
            fileType === type.toLowerCase() ||
            fileType.startsWith(`${type.toLowerCase().split('/')[0]}/`)
          );
        }
        return false;
      });

      if (!isAccepted) {
        return `File type ${file.type || 'unknown'} is not allowed. Accepted types: ${accept}`;
      }
    }

    // Validation de la taille
    if (maxSize && file.size > maxSize) {
      return `File size ${formatFileSize(file.size)} exceeds maximum size ${formatFileSize(maxSize)}`;
    }

    return null;
  };

  const processFiles = useCallback(
    async (fileList: File[]) => {
      const newErrors: string[] = [];
      const validFiles: FileWithPreview[] = [];
      const filesToProcess: File[] = [];

      // Valider tous les fichiers d'abord
      fileList.forEach((file) => {
        const error = validateFile(file);
        if (error) {
          newErrors.push(`${file.name}: ${error}`);
        } else {
          filesToProcess.push(file);
        }
      });

      // Créer les previews pour les fichiers valides
      for (const file of filesToProcess) {
        // Create FileWithPreview by adding properties to the File object
        // Since File is a class, we need to add properties directly
        const fileWithPreview = file as FileWithPreview;
        fileWithPreview.status = 'pending';
        fileWithPreview.progress = 0;

        if (showPreview) {
          const preview = await createPreview(file);
          if (preview) {
            fileWithPreview.preview = preview;
          }
        }

        validFiles.push(fileWithPreview);
      }

      setErrors(newErrors);

      if (validFiles.length > 0) {
        const updatedFiles = multiple ? [...files, ...validFiles] : validFiles;
        setFiles(updatedFiles);
        // Pass files to parent
        // FileWithPreview extends File, so we can pass it as File[]
        // The internal properties (preview, status, progress, error) are not enumerable
        // so they won't interfere with normal File usage
        onFileSelect(updatedFiles.slice() as File[]);
      }
    },
    [files, multiple, accept, maxSize, showPreview, onFileSelect],
  );

  const handleDrag = useCallback((e: React.DragEvent) => {
    e.preventDefault();
    e.stopPropagation();
    if (e.type === 'dragenter' || e.type === 'dragover') {
      setDragActive(true);
    } else if (e.type === 'dragleave') {
      setDragActive(false);
    }
  }, []);

  const handleDrop = useCallback(
    (e: React.DragEvent) => {
      e.preventDefault();
      e.stopPropagation();
      setDragActive(false);

      if (disabled) return;

      if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
        const fileList = Array.from(e.dataTransfer.files);
        processFiles(fileList);
      }
    },
    [disabled, processFiles],
  );

  const handleFileInput = useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      if (e.target.files && e.target.files.length > 0) {
        const fileList = Array.from(e.target.files);
        processFiles(fileList);
        // Réinitialiser l'input pour permettre la sélection du même fichier
        if (fileInputRef.current) {
          fileInputRef.current.value = '';
        }
      }
    },
    [processFiles],
  );

  const handleRemoveFile = useCallback(
    (index: number) => {
      const updatedFiles = files.filter((_, i) => i !== index);
      setFiles(updatedFiles);
      onFileSelect(
        updatedFiles.map((f) => {
          // Destructure to remove internal properties before passing to parent
          // eslint-disable-next-line @typescript-eslint/no-unused-vars
          const { preview, status, progress, error, ...file } = f;
          return file;
        }),
      );
    },
    [files, onFileSelect],
  );

  const handleClick = () => {
    if (!disabled && fileInputRef.current) {
      fileInputRef.current.click();
    }
  };

  return (
    <div className={cn('w-full', className)}>
      {/* Zone de drop */}
      <Card
        className={cn(
          'border-2 border-dashed transition-colors cursor-pointer',
          dragActive && 'border-primary bg-primary/5',
          disabled && 'opacity-50 cursor-not-allowed',
          !disabled && 'hover:border-primary/50',
        )}
        onDragEnter={handleDrag}
        onDragLeave={handleDrag}
        onDragOver={handleDrag}
        onDrop={handleDrop}
        onClick={handleClick}
      >
        <div className="flex flex-col items-center justify-center p-8 text-center">
          <Upload className="h-12 w-12 mb-4 text-muted-foreground" />
          <p className="text-lg font-medium mb-2">
            Drag & drop files here, or click to select
          </p>
          <p className="text-sm text-muted-foreground mb-4">
            {accept && `Accepted types: ${accept}`}
            {maxSize && ` • Max size: ${formatFileSize(maxSize)}`}
            {multiple && ' • Multiple files allowed'}
          </p>
          <Button type="button" variant="outline" disabled={disabled}>
            Select Files
          </Button>
        </div>
      </Card>

      {/* Input file caché */}
      <input
        ref={fileInputRef}
        type="file"
        accept={accept}
        multiple={multiple}
        onChange={handleFileInput}
        disabled={disabled}
        className="hidden"
      />

      {/* Messages d'erreur */}
      {errors.length > 0 && (
        <div className="mt-4 space-y-2">
          {errors.map((error, index) => (
            <div
              key={index}
              className="flex items-center gap-2 p-3 text-sm text-destructive bg-destructive/10 border border-destructive/20 rounded-md"
            >
              <AlertCircle className="h-4 w-4 shrink-0" />
              <span>{error}</span>
            </div>
          ))}
        </div>
      )}

      {/* Liste des fichiers avec preview */}
      {showPreview && files.length > 0 && (
        <div className="mt-4 space-y-3">
          {files.map((file, index) => {
            const IconComponent = getFileIcon(file);
            const isImage = file.type && file.type.startsWith('image/');

            return (
              <Card key={index} className="p-4">
                <div className="flex items-start gap-4">
                  {/* Preview */}
                  <div className="shrink-0">
                    {file.preview && isImage ? (
                      <img
                        src={file.preview}
                        alt={file.name}
                        className="w-16 h-16 object-cover rounded-md"
                      />
                    ) : (
                      <div className="w-16 h-16 flex items-center justify-center bg-muted rounded-md">
                        <IconComponent className="h-8 w-8 text-muted-foreground" />
                      </div>
                    )}
                  </div>

                  {/* Info fichier */}
                  <div className="flex-1 min-w-0">
                    <div className="flex items-center justify-between mb-1">
                      <p className="text-sm font-medium truncate">
                        {file.name}
                      </p>
                      <Button
                        type="button"
                        variant="ghost"
                        size="icon"
                        className="h-6 w-6 shrink-0"
                        onClick={(e) => {
                          e.stopPropagation();
                          handleRemoveFile(index);
                        }}
                      >
                        <X className="h-4 w-4" />
                      </Button>
                    </div>
                    <p className="text-xs text-muted-foreground mb-2">
                      {formatFileSize(file.size)} •{' '}
                      {file.type || 'Unknown type'}
                    </p>

                    {/* Barre de progression */}
                    {file.status === 'uploading' && (
                      <div className="w-full bg-muted rounded-full h-2">
                        <div
                          className="bg-primary h-2 rounded-full transition-all"
                          style={{ width: `${file.progress || 0}%` }}
                        />
                      </div>
                    )}

                    {/* Status */}
                    {file.status === 'success' && (
                      <div className="flex items-center gap-1 text-xs text-green-600">
                        <CheckCircle className="h-3 w-3" />
                        <span>Uploaded successfully</span>
                      </div>
                    )}
                    {file.status === 'error' && file.error && (
                      <div className="flex items-center gap-1 text-xs text-destructive">
                        <AlertCircle className="h-3 w-3" />
                        <span>{file.error}</span>
                      </div>
                    )}
                  </div>
                </div>
              </Card>
            );
          })}
        </div>
      )}
    </div>
  );
}
