import _ from 'lodash';

import stringifyType from './stringifyType';
import T from './types';
import { mapName, stringify } from './utils';

const invalidParamTypes = (types, args) => {
  const requiredTyped = stringify(stringifyType(types));
  const providedArguments = stringify(_.flatten(args));
  throw new TypeError(`Needed ${requiredTyped} but got ${providedArguments}`);
};

const invalidReturnType = (returnValue, returnType) => {
  const providedReturnValue = typeof returnValue;
  const typeName = mapName(returnType);
  throw new TypeError(`Function returned a ${providedReturnValue} but needed a ${typeName}`);
};

const TypeArray = T.Array(T.Union(T.Type, T.Object()));

const returnsHandler = (types = [], typedFunction) => {
  return (type) => {
    return func(types, type).of(typedFunction);
  };
};

const func = (types = [], returnType) => {
  return {
    of(typedFunction) {
      const funcChecker = function(...args) {
        let returnValue;
        let validTypes;

        if (!TypeArray(types)){
          types = [ types ];
        }

        validTypes = types
                     .every((x, i) => x(args[i]));

        if (!validTypes) {
          invalidParamTypes(types, args);
        }
        if (this) { args.push(this); }

        returnValue = typedFunction.bind(this)(...args);

        if (returnType && !returnType(returnValue)) {
          invalidReturnType(returnValue, returnType);
        }

        return returnValue;
      };

      funcChecker.returns = returnsHandler(types, typedFunction);

      return funcChecker;
    }
  };
};

export default func;

@@ -840,32 +840,40 @@
 = %5B%5D, returnType
+ = T.Any
 ) =%3E %7B%0A  return 
@@ -1338,22 +1338,8 @@
 if (
-returnType && 
 !ret

refactor func