text: const { buildSchema, printSchema, parse } = require('graphql');
  const { codegen } = require('@graphql-codegen/core');
  const typescriptPlugin = require('@graphql-codegen/typescript');
  const { camelize } = require('inflection');
  
  /*Utils*/
  
  const getWrappingTypeMetadata = (_type) => {
    let type = JSON.parse(JSON.stringify(_type));
    const wrapperStack = [];
    while (type.kind !== 'NamedType') {
      if (type.kind === 'ListType') {
        wrapperStack.push('l');
      }
      if(type.kind === 'NonNullType') {
        wrapperStack.push('n');
      }
      type = type.type;
    }
    const typename = type.name.value;
    return {
      typename,
      stack: wrapperStack.reverse()
    }
  }
  
  const getTypescriptTypename = (_typename, wrapperStack) => {
    let typename = _typename;
    if (!wrapperStack.length || wrapperStack[0] === 'l') {
      typename = `Maybe<${typename}>`
    }
    wrapperStack.forEach((w, i) => {
      if (w === 'l') {
        if (wrapperStack[i+1] === 'n') {
          typename = `Array <${typename}>`
        } else {
          typename = `Maybe <Array<${typename}>>`
        }
      }
    });
    return typename;
  }
  
  /*Templater*/
  
  const templater = async (
    actionName,
    actionsSdl,
    derive
  ) => {
    const ast = parse(`${actionsSdl}`);
    const typesAst = {
      ...ast,
      definitions: ast.definitions.filter(d => d.name.value !== 'Mutation')
    };
  
    const codegenConfig = {
      schema: typesAst,
      plugins: [
        {
          typescript: {},
        },
      ],
      pluginMap: {
        typescript: typescriptPlugin
      }
    }
    const typesCodegen = await codegen(codegenConfig);
  
    let mutationDef;
    const mutationAst = {
      ...ast,
      definitions: ast.definitions.filter(d => {
        if (d.name.value === 'Mutation') {
          if (mutationDef) return false
          mutationDef = d.fields.find(f => f.name.value === actionName);
          if (!mutationDef) {
            return false;
          } {
            return true;
          }
        }
        return false;
      })
    }
    const mutationName = mutationDef.name.value;
    const mutationArguments = mutationDef.arguments;
  
    const typesFileMetadata = {
      content: typesCodegen,
      name: `hasuraCustomTypes.ts`
    }
  
    const requiredTypesToImport = { 'Scalars': true, 'Maybe': true };
  
    const getHandlerFileContent = () => {
      const argumentType = {
        name: camelize(`Action_${mutationName}_input`),
        fields: []
      };
      mutationArguments.forEach(ma => {
        const argTypeMetadata = getWrappingTypeMetadata(ma.type);
        const existingType = typesAst.definitions.find(d => d.name.value === argTypeMetadata.typename)
        if (existingType) {
          if (existingType.kind === 'ScalarTypeDefinition') {
            argumentType.fields.push({
              name: ma.name.value,
              type: getTypescriptTypename(`Scalar['${argTypeMetadata.typename}']`, argTypeMetadata.stack)
            })
          } else {
            argumentType.fields.push({
              name: ma.name.value,
              type: getTypescriptTypename(argTypeMetadata.typename, argTypeMetadata.stack)
            });
            requiredTypesToImport[argTypeMetadata.typename] = true;
          }
        } else { 
          argumentType.fields.push({
            name: ma.name.value,
            type: getTypescriptTypename(`Scalar['${argTypeMetadata.typename}']`, argTypeMetadata.stack)
          })
        }
      });
  
  
  
      const getImports = () => {
        return `
  import { NowRequest, NowResponse } from '@now/node'
  
  import {
  ${Object.keys(requiredTypesToImport).map(rt => `  ${rt}`).join(',\n')}
  } from './hasuraCustomTypes';
  `;  
      };
  
      const getMutationInputType = () => {
        return `
  type ${argumentType.name} = {
  ${argumentType.fields.map(f => `  ${f.name}: ${f.type}`).join(',\n')} 
  }
  `;
      }
  
      const getHandler = () => {
        return `
  const handler = async (request: NowRequest, response: NowResponse) => {
    const mutationInput: ${argumentType.name} = request.body.input;
  
    // perform your business logic here
  
    In case of error,
  
    return response.status(400).json({
      errors: {
        code: '<error code>',
        message: 'error happened'
      }
    })
  
    */
  
    // return the output type
    return response.json({
      data: {}
    });
  }
  `;
  
      }
  
      return `
  ${getImports()}
  ${getMutationInputType()}
  ${getHandler()}
  
  export default handler;
  `;
  
    }
  
    const handlerFileMetadata = {
      name: `${mutationName}.ts`,
      content: getHandlerFileContent()
    }
  
    return [typesFileMetadata, handlerFileMetadata];
  
  }
  
  module.exports = templater
  