import { countBy, isEqual, uniqWith } from 'lodash';
import outmatch from 'outmatch';

import { DMMF } from '../types';

/**
 * Find input type for graphql field decorator.
 */
export function getGraphqlInputType(
  inputTypes: DMMF.SchemaArgInputType[],
  pattern?: string,
) {
  let result: DMMF.SchemaArgInputType | undefined;

  inputTypes = inputTypes.filter(t => !['null', 'Null'].includes(String(t.type)));
  inputTypes = uniqWith(inputTypes, isEqual);

  if (inputTypes.length === 1) {
    return inputTypes[0];
  }

  const countTypes = countBy(inputTypes, x => x.location);
  const isOneType = Object.keys(countTypes).length === 1;

  if (isOneType) {
    result = inputTypes.find(x => x.isList);
    if (result) {
      return result;
    }
  }

  if (pattern) {
    if (pattern.startsWith('matcher:') || pattern.startsWith('match:')) {
      const { 1: patternValue } = pattern.split(':', 2);
      const isMatch = outmatch(patternValue, { separator: false });
      result = inputTypes.find(x => isMatch(String(x.type)));
      if (result) {
        return result;
      }
    }
    result = inputTypes.find(x => String(x.type).includes(pattern));
    if (result) {
      return result;
    }
  }

  result = inputTypes.find(x => x.location === 'inputObjectTypes');
  if (result) {
    return result;
  }

  if (
    countTypes.enumTypes &&
    countTypes.scalar &&
    inputTypes.some(x => x.type === 'Json' && x.location === 'scalar')
  ) {
    result = inputTypes.find(x => x.type === 'Json' && x.location === 'scalar');
    if (result) {
      return result;
    }
  }

  throw new TypeError(
    // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
    `Cannot get matching input type from ${
      inputTypes.map(x => x.type).join(', ') || 'zero length inputTypes'
    }`,
  );
}