import { useCallback, useEffect, useImperativeHandle, useRef } from 'react';
import Debug from 'debug';

import type { FC, RefObject } from 'react';
import type { ClickableProps } from '../Clickable';

import Clickable from '../Clickable';

const debug = Debug('songwhip/FilePicker');

export interface FilePickerApi {
  clear(): void;
  open(): void;
}

export interface FilePickerProps extends ClickableProps {
  onChange: (params: { file?: File }) => void;
  accept?: string[];
  testId?: string;
  isDisabled?: boolean;
  apiRef?: RefObject<FilePickerApi | null>;
  validationMessage?: string;
  name?: string;
  isRequired?: boolean;
}

const FilePicker: FC<FilePickerProps> = ({
  children,
  onChange,
  isDisabled,
  accept,
  testId,
  apiRef,
  tabIndex = 0,
  name,
  isRequired = false,

  validationMessage = '',

  /**
   * The hidden <input type="file"> needs to be wrapped in the <label> so that
   * clicks get passed down to it and browser opens the native UI. If you wish
   * to wrap in your own `<label>` you can set `tag="div"` to avoid invalid
   * double nested <label>s.
   */
  tag = 'label',

  ...clickableProps
}) => {
  const inputRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    inputRef.current?.setCustomValidity(validationMessage);
  }, [isRequired, validationMessage]);

  const clear = useCallback(() => {
    debug('clear', inputRef);

    if (inputRef.current) {
      inputRef.current.value = '';
    }
  }, []);

  // expose clear functionality via `apiRef` prop
  useImperativeHandle(apiRef, () => ({
    clear,
    open: () => {
      inputRef.current?.click();
    },
  }));

  return (
    <Clickable
      {...clickableProps}
      positionRelative
      tag={tag}
      // make the label focusable
      tabIndex={tabIndex}
      // a11y - Trigger the file browser to open when 'Enter'
      // pressed and label focused this doesn't seem to happen
      // automatically perhaps because the <input> is hidden
      onKeyUp={(e) => {
        if (e.key === 'Enter') {
          inputRef.current?.click();
        }
      }}
    >
      {children}
      <input
        ref={inputRef}
        type="file"
        data-testid={testId}
        multiple={false}
        accept={accept?.join(', ')}
        name={name}
        style={{
          opacity: 0,
          height: 1,
          position: 'absolute',
          left: 0,
          right: 0,
        }}
        disabled={isDisabled}
        tabIndex={-1}
        required={isRequired}
        onChange={(event) => {
          const files = event.target.files;
          debug('change', files);

          const file = files?.[0];

          onChange({
            file,
          });
        }}
      />
    </Clickable>
  );
};

export default FilePicker;
