import { invariant } from '../../invariant';
import type { Props } from './droppable-types';
import { warning } from '../../dev-warning';
import checkIsValidInnerRef from '../check-is-valid-inner-ref';
import useDevSetupWarning from '../use-dev-setup-warning';

interface Args {
  props: Props;
  getDroppableRef: () => HTMLElement | null;
  getPlaceholderRef: () => HTMLElement | null;
}

type CheckFn = (args: Args) => void;

function isBoolean(value: unknown): boolean {
  return typeof value === 'boolean';
}

function runChecks(args: Args, checks: CheckFn[]) {
  checks.forEach((check: CheckFn) => check(args));
}

const shared: CheckFn[] = [
  function required({ props }: Args) {
    invariant(props.droppableId, 'A Droppable requires a droppableId prop');
    invariant(
      typeof props.droppableId === 'string',
      `A Droppable requires a [string] droppableId. Provided: [${typeof props.droppableId}]`,
    );
  },
  function boolean({ props }: Args) {
    invariant(
      isBoolean(props.isDropDisabled),
      'isDropDisabled must be a boolean',
    );
    invariant(
      isBoolean(props.isCombineEnabled),
      'isCombineEnabled must be a boolean',
    );
    invariant(
      isBoolean(props.ignoreContainerClipping),
      'ignoreContainerClipping must be a boolean',
    );
  },
  function ref({ getDroppableRef }: Args) {
    checkIsValidInnerRef(getDroppableRef());
  },
];

const standard: CheckFn[] = [
  function placeholder({ props, getPlaceholderRef }: Args) {
    if (!props.placeholder) {
      return;
    }

    const ref: HTMLElement | null = getPlaceholderRef();

    if (ref) {
      return;
    }

    warning(`
      Droppable setup issue [droppableId: "${props.droppableId}"]:
      DroppableProvided > placeholder could not be found.

      Please be sure to add the {provided.placeholder} React Node as a child of your Droppable.
      More information: https://github.com/hello-pangea/dnd/blob/main/docs/api/droppable.md
    `);
  },
];

const virtual: CheckFn[] = [
  function hasClone({ props }: Args) {
    invariant(
      props.renderClone,
      'Must provide a clone render function (renderClone) for virtual lists',
    );
  },
  function hasNoPlaceholder({ getPlaceholderRef }: Args) {
    invariant(
      !getPlaceholderRef(),
      'Expected virtual list to not have a placeholder',
    );
  },
];

export default function useValidation(args: Args) {
  useDevSetupWarning(() => {
    // wrapping entire block for better minification
    runChecks(args, shared);

    if (args.props.mode === 'standard') {
      runChecks(args, standard);
    }

    if (args.props.mode === 'virtual') {
      runChecks(args, virtual);
    }
  });
}
