import React, { useMemo, useCallback, useState } from 'react';
import { ImageUp, Loader2 } from 'lucide-react';
import {FileRejection, useDropzone} from 'react-dropzone';
import { FormFieldProps } from 'react-compose-form';
import { Card, CardContent } from '@/components/ui/card';
import {
  FormFieldContext,
  FormItem,
  FormControl
} from '@/components/ui/form';
import { FormLabel } from "@/components/form/form-label";
import { FormMessage } from "@/components/form/form-message";
import { MediaPreview } from "@/components/dashboard/media/media-preview";
import { cn } from "@/lib/utils/cn";

export type InputFileFieldProps = FormFieldProps<
  {
    label?: string;
    loading?: boolean;
    maxFiles?: number;
    maxFileSize?: number;
    acceptedFileTypes?: string[];
  } & (
    | {
        multiple?: false;
        preview?: {
            alt?: string;
            mimeType?: string;
            url?: string;
        };
        value?: File | string;
        onChange?: (file?: File) => void;
      }
    | {
        multiple: true;
        preview?: {
            alt?: string;
            mimeType?: string;
            url?: string;
        }[];
        value?: (File | string)[];
        onChange?: (file: File[]) => void;
      }
  )
>;

export const FileField: React.FC<InputFileFieldProps> = (props) => {
  const {
    label,
    name,
    disabled,
    maxFiles = 5,
    multiple,
    maxFileSize = 10 * 1024 * 1024, // 50MB
    loading: isLoading,
    preview,
    acceptedFileTypes = ["image/*"],
    value,
    onChange
  } = props;

  const [fileRejections, setFileRejections] = useState<FileRejection[]>([]);

  const values = useMemo(() => {
      if(!value) {
          return [];
      }

      const values = Array.isArray(value) ? value : [value];

      return values.map((value) => {
          return value;
      });
  }, [value]);

  const previews = useMemo(() => {
      if(!preview) {
          return [];
      }

      return Array.isArray(preview) ? preview : [preview];
  }, [preview]);

  const acceptObject = useMemo(() => {
    const obj: Record<string, string[]> = {};

    acceptedFileTypes.forEach((type) => {
      obj[type] = [];
    });

    return obj;
  }, [acceptedFileTypes]);

  const handleDrop = useCallback(
    (acceptedFiles: File[], rejectedFiles: FileRejection[]) => {
      setFileRejections(rejectedFiles);

      if (!onChange) {
        return;
      }

      if (multiple) {
        onChange(acceptedFiles);
      }
      else if (acceptedFiles.length > 0) {
        const [file] = acceptedFiles;
        onChange(file);
      }
      else {
        onChange(undefined);
      }
    },
    [multiple, onChange]
  );

  const handleRemove = useCallback((index: number) => {
      if(!onChange) {
          return;
      }

      if (multiple) {
          const newFiles = [...(values as File[])];
          newFiles.splice(index, 1);

          onChange(newFiles);
      }
      else {
          onChange(undefined);
      }
  }, [multiple, values, onChange]);

  const { getRootProps, getInputProps, isDragActive } = useDropzone({
    accept: acceptObject,
    maxSize: maxFileSize,
    maxFiles: maxFiles,
    disabled: disabled || isLoading,
    onDrop: handleDrop
  });

  return (
    <FormFieldContext value={{ name: name as string }}>
      <FormItem>
        {label && name && (
          <FormLabel name={name}>{label}</FormLabel>
        )}

        <FormControl className='p-0'>
          <Card>
            <CardContent className='p-6'>
              <div
                {...getRootProps()}
                className={cn(
                  'border-muted-foreground/25 hover:border-primary hover:bg-primary/5 cursor-pointer rounded-lg border-2 border-dashed p-8 text-center transition-colors',
                  {
                    'border-primary bg-primary/10': isDragActive,
                    'cursor-not-allowed opacity-50': disabled || isLoading
                  }
                )}
              >
                <input {...getInputProps()} disabled={disabled} />

                {isLoading ? (
                  <div className='flex flex-col items-center space-y-2'>
                    <Loader2 className='text-primary h-8 w-8 animate-spin' />
                    <p className='text-muted-foreground text-sm'>
                      Uploading...
                    </p>
                  </div>
                ) : (
                  <div className='flex flex-col items-center space-y-2'>
                    <ImageUp className='text-muted-foreground h-6 w-6' />
                  </div>
                )}
              </div>

              {values.length > 0 && (
                  <div className='mt-4 flex flex-wrap gap-2'>
                      {values.map((value, index) => {
                          return (
                              <MediaPreview
                                key={index}
                                file={value}
                                onDelete={() => handleRemove(index)} />
                          );
                      })}
                  </div>
              )}

              {values.length === 0 && previews.length > 0 && (
                  <div className='mt-4 flex flex-wrap gap-2'>
                      {previews.map((preview, index) => {
                          return (
                              <MediaPreview
                                key={index}
                                mimeType={preview.mimeType}
                                url={preview.url} />
                          );
                      })}
                  </div>
              )}
            </CardContent>
          </Card>
        </FormControl>

        {fileRejections.length > 0 && (
          <div className="mt-2 space-y-1">
            {fileRejections.map(({ file, errors }) => (
              <div key={file.name} className="text-destructive text-xs font-medium">
                {file.name}: {errors.map(e => e.message).join(', ')}
              </div>
            ))}
          </div>
        )}

        {name && (
          <FormMessage absolute name={name} />
        )}
      </FormItem>
    </FormFieldContext>
  );
};
