import { uuidv4 } from '@firebase/util';
import { css, CSSProperties } from 'glamor';
import { get } from '../../utils/obj/get';
import { observer } from 'mobx-react';
import { useRef } from 'react';
import { Button } from '../Button';
import {
  BoxSizeStyles,
  ButtonStyleVariant
} from '../../styles/defaults/themes.interface';
import React from 'react';
import { merge } from '../../utils/obj/merge';

/**
 * Interface for the styles to be applied to the ButtonGetFile component
 */
export interface ButtonGetFileStyle {
  container?: CSSProperties;
  button?: CSSProperties;
  input?: CSSProperties;
}

/**
 * Interface for the props of the ButtonGetFile component
 */
export interface ButtonGetFileProps {
  /**
   * @default "primary"
   */
  variant?: keyof ButtonStyleVariant;
  /**
   * @default "Choose file"
   */
  label?: React.ReactNode;
  /**
   * Overwrites for the styles of the component
   */
  styleOverwrites?: ButtonGetFileStyle;
  /**
   * the default onChange event from the input
   * @param file
   * @returns
   */
  onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void;
  /**
   * Will only be called if parseFileContentOnLoad is false
   * @param file
   * @returns
   */
  onFileAvailable?: (file: File) => void;
  /**
   * Will only be called if parseFileContentOnLoad is false and multiple is true
   * @param file
   * @returns
   */
  onFilesAvailable?: (fileList: FileList) => void;
  /**
   * Will only be called if parseFileContentOnLoad is true
   * otherwise the file will be returned via the onChange
   * @param file
   * @returns
   */
  onFileContentAvailable?: (file: File) => void;
  /**
   * @default accepts all file types
   */
  acceptedFileTypes?: string[];
  onError?: (error: Error) => void;
  /**
   * if true it will parse the contents of the file and return it
   * via onFileContentAvailable
   * @default false
   */
  parseFileContentOnLoad?: boolean;
  /**
   * if true it will allow multiple files to be selected
   * @default false
   */
  multiple?: boolean;
  /**
   * The size of the button
   * @default "m"
   */
  size?: keyof BoxSizeStyles;
}

export const ButtonGetFile: React.FC<ButtonGetFileProps> = observer(
  ({
    acceptedFileTypes = [],
    label,
    multiple = false,
    onChange,
    onError,
    onFilesAvailable,
    onFileAvailable,
    onFileContentAvailable,
    parseFileContentOnLoad = false,
    styleOverwrites,
    variant = 'primary',
    size = 'm'
  }) => {
    const inputRef = useRef<HTMLInputElement>(null);
    const componentStyles = {
      container: {},
      button: {},
      input: {
        position: 'absolute',
        cursor: 'pointer',
        left: 0,
        top: 0,
        opacity: 0,
        width: 0,
        height: 0
      }
    };

    const handleChosen = (e: React.ChangeEvent<HTMLInputElement>) => {
      const files = get(e, 'dataTransfer.files') || get(e, 'target.files');

      if (!files) {
        return;
      }
      if (
        !parseFileContentOnLoad &&
        onFileAvailable &&
        files.length > 0 &&
        !multiple
      ) {
        onFileAvailable(files[0]);
      }

      if (!parseFileContentOnLoad && onFilesAvailable && files && multiple) {
        onFilesAvailable(files);
      }

      if (onChange) {
        onChange(e);
      }

      if (!parseFileContentOnLoad) {
        // we don't need to parse the file content lets just return
        return;
      }

      const normalizedFileTypes = acceptedFileTypes?.map((fileType) =>
        getFileTypePrefix(fileType)
      );

      const acceptedFilesList: Array<any> = [];
      const rejectedFilesList: Array<any> = [];

      const filesList = Object.keys(files);

      filesList.forEach((key: any) => {
        const file = files[key];
        const fileType = file.type;
        if (
          normalizedFileTypes.includes(fileType) ||
          acceptedFileTypes.length === 0
        ) {
          acceptedFilesList.push(file);
        } else {
          rejectedFilesList.push(file);
        }
      });

      const hasRejected = rejectedFilesList.length > 0;

      const fr = new FileReader();
      fr.onload = function () {
        const file = JSON.parse(JSON.stringify(fr.result));
        const theme = JSON.parse(file);
        onFileContentAvailable && onFileContentAvailable(theme);
      };

      fr.readAsText(acceptedFilesList[0]);

      if (hasRejected) {
        onError && onError(new Error('File type not accepted'));
      }
    };

    const localStyles = merge({}, componentStyles, styleOverwrites);
    return (
      <div {...css({ position: 'relative' })}>
        <Button
          size={size}
          variant={variant}
          onClick={() => {
            if (inputRef.current) {
              inputRef.current.click();
              inputRef.current.value = '';
            }
          }}
          styleOverwrites={localStyles.button}
        >
          {label || <p>'Select file'</p>}
        </Button>
        <input
          ref={inputRef}
          {...css(localStyles.input, { backgroundColor: 'green' })}
          type="file"
          multiple={multiple}
          id={uuidv4()}
          name={uuidv4()}
          onChange={(e) => handleChosen(e)}
          accept={acceptedFileTypes
            .map((ft) => getFileTypePrefix(ft))
            .join(',')}
        />
      </div>
    );
  }
);

const getFileTypePrefix = (fileExt: string) => {
  switch (fileExt) {
    case 'png':
    case 'jpg':
    case 'jpeg':
      return `image/${fileExt}`;
    case 'mp4':
      return `video/*`;
    case 'mov':
    case 'quicktime':
      return `video/quicktime`;
    case 'mp3':
    case 'wav':
    case 'ac3':
      return `audio/${fileExt}`;
    case 'm4a':
      return `audio/*`;
    case 'json':
      return `application/json`;
    default:
      return `application/${fileExt}`;
  }
};
