
/* <%- lintDisableUnused %> */
// Define GraphQL resolvers using Feathers services and BatchLoaders. (Can be re-generated.)
<%- tplJsOnly([
   `const { getByDot, setByDot } = require('feathers-hooks-common')${sc}`,
    '',
]) -%>
<%- tplTsOnly([
  `import { App } from '../../app.interface'${sc}`,
  `import { Paginated, Params } from '@feathersjs/feathers'${sc}`,
  `import { getByDot, setByDot, FGraphQLResolverMap } from 'feathers-hooks-common'${sc}`,
  `import { GraphQLFieldResolver } from 'graphql'${sc}`,
  `import { GraphQLResolveInfo } from 'graphql/type/definition'${sc}`,
  `import { ArgMap, ResolverContext } from './graphql.interfaces'${sc}`,
  '',
  `export interface BatchloaderResolverOptions {`,
  `  convertArgsToParams: any${sc}`,
  `  convertArgsToFeathers: (args: any[]) => (...args: any[]) => Params${sc}`,
  `  extractAllItems: any${sc}`,
  `  extractFirstItem: any${sc}`,
  `  feathersBatchLoader: {`,
  `    feathersBatchLoader: any,`,
  `  }${sc}`,
  '}',
  '',
]) -%>
<%- insertFragment('imports') %>
<%- insertFragment('init') %>

let moduleExports = function batchLoaderResolvers<%- tplJsOrTs('(app, options)', '(app: App, options: BatchloaderResolverOptions )') %> {
  // <%- lintDisableNextLine %>
  let { convertArgsToParams, convertArgsToFeathers, extractAllItems, extractFirstItem,
    feathersBatchLoader: { feathersBatchLoader } } = options<%- sc %>

  <%- insertFragment('max-batch-size', [
    `  let defaultPaginate = app.get(\'paginate\')${sc}`,
    '  let maxBatchSize = defaultPaginate && typeof defaultPaginate.max === \'number\' ?',
    `    defaultPaginate.max : undefined${sc}`,
  ]) %>

  <%- insertFragment('extra_auth_props', [
    `  const convertArgs = convertArgsToFeathers([])${sc}`,
  ]) %>

<% let __temp = Object.keys(mapping.feathers).map(serviceName =>
  `  let ${serviceName} = app.service('${mapping.feathers[serviceName].path}')${sc}`
); -%>
  <%- insertFragment('services', __temp) %>

  <%- insertFragment('get-result', [
  '  // Given a fieldName in the parent record, return the result from a BatchLoader',
    '  // The result will be an object (or null), or an array of objects (possibly empty).',
    isJs ?
      '  function getResult(batchLoaderName, fieldName, isArray) {' : [
        '  function getResult(',
        '    batchLoaderName: string, fieldName: string, isArray?: boolean',
        '  ): GraphQLFieldResolver<any, ResolverContext> {'
      ].join(EOL),
    '    const contentByDot = `batchLoaders.${batchLoaderName}`' + `${sc}`,
  '',
  '    // `content.app = app` is the Feathers app object.',
  '    // `content.batchLoaders = {}` is where the BatchLoaders for a request are stored.',
  isJs ?
    '    return (parent, args, content, ast) => {' :
    '    return (parent: any, args: ArgMap, content: ResolverContext, ast: GraphQLResolveInfo) => {',
  `      let batchLoader = getByDot(content, contentByDot)${sc}`,
  '',
  '      if (!batchLoader) {',
  `        batchLoader = getBatchLoader(batchLoaderName, parent, args, content, ast)${sc}`,
  `        setByDot(content, contentByDot, batchLoader)${sc}`,
  '      }',
  '',
  `      const returns1 = batchLoader.load(parent[fieldName])${sc}`,
  isJs ?
    `      return !isArray ? returns1 : returns1.then(result => result || [])${sc}` :
    `      return !isArray ? returns1 : returns1.then((result: any) => result || [])${sc}`,
  `    }${sc}`,
  '  }',
  ]) %>
<%# -%>
<%# === BatchLoaders =========================================================================== -%>

  // A transient BatchLoader can be created only when (one of) its resolver has been called,
  // as the BatchLoader loading function may require data from the 'args' passed to the resolver.
  // Note that each resolver's 'args' are static throughout a GraphQL call.
  <%- tplJsOrTs(
    `function getBatchLoader(dataLoaderName, parent, args, content, ast) {`, [
      'function getBatchLoader(',
      '    dataLoaderName: string, parent: any, args: ArgMap, content: ResolverContext, ast: GraphQLResolveInfo',
      '  ): FGraphQLResolverMap {'
    ].join(EOL)
  ) %>
    let feathersParams<%- sc %>

    switch (dataLoaderName) {
    /* Persistent BatchLoaders. Stored in `content.batchLoaders._persisted`. */
    <%- insertFragment('bl-persisted', [
      '    // case \'_persisted.user.one.id\': // service user, returns one object, key is field id',
    ]) %>

    /* Transient BatchLoaders shared among resolvers. Stored in `content.batchLoaders._shared`. */
    <%- insertFragment('bl-shared', [
        '    // *.*: User',
        '    // case \'_shared.user.one.id\': // service user, returns one object, key is field id',
    ]) %>

    /* Transient BatchLoaders used by only one resolver. Stored in `content.batchLoaders`. */
<%# -%>
<%# --- forEach-a starts below. Loop thru the schemas of GraphQL enabled services. -%>
<% Object.keys(mapping.graphqlService).forEach(graphqlName => {
  __serviceName = mapping.graphqlService[graphqlName].service;
  __add = feathersSpecs[__serviceName]._extensions.graphql.add;
-%>
<%# -%>
<%# --- forEach-b starts below. Loop thru the fields to be renamed. -%>
<% Object.keys(__add).forEach(fieldName => {
  __addField = __add[fieldName];
-%>
<%# -%>
<%# --- if-1c starts below. -%>
<% if (__addField.serviceName) {
  __code = '[!]';
  if (!__addField.isArray) {
    __code = __addField.isNullable ? '' : '!';
  }
  __temp = [
    `    case '${graphqlName}.${fieldName}':`,
    `      return feathersBatchLoader(dataLoaderName, '${__code}', '${__addField.relation.otherTable}',`,
    isJs ?
      '        keys => {' :
      '        (keys: string[]) => {',
    '          feathersParams = convertArgs(args, content, null, {',
    `            query: { ${__addField.relation.otherTable}: { $in: keys }, $sort: undefined },`,
    '            _populate: \'skip\', paginate: false',
    `          })${sc}`,
    `          return ${__addField.serviceName}.find(feathersParams)${sc}`,
    '        },',
    '        maxBatchSize // Max #keys in a BatchLoader func call.',
    `      )${sc}`,
  ];
-%>

    // <%- `${graphqlName}.${fieldName}${__addField.args}: ${__addField.type}` %>
    <%- insertFragment(`bl-${graphqlName}-${fieldName}`, __temp, undefined, { indentEnd: 2 }) %>
<% } -%>
<%# --- if-c ends above. -%>
<% }); -%>
<%# --- forEach-b ends above. -%>
<% }); -%>
<%# --- forEach-a ends above. -%>

    /* Throw on unknown BatchLoader name. */
    default:
      <%- insertFragment('bl-default', [
        '      throw new Error(`GraphQL query requires BatchLoader named \'${dataLoaderName}\' but no definition exists for it.`)' + `${sc}`,
      ]) %>
    }
  }
<%# -%>
<%# === resolvers ============================================================================== -%>

  let returns<%- tplTsOnly(': FGraphQLResolverMap') -%> = {
<%# -%>
<%# --- forEach-1 starts below. Loop thru the schemas of GraphQL enabled services. -%>
<% Object.keys(mapping.graphqlService).forEach(graphqlName => {
  __serviceName = mapping.graphqlService[graphqlName].service;
  __add = feathersSpecs[__serviceName]._extensions.graphql.add;
-%>

    <%- graphqlName %>: {
<%# -%>
<%# --- forEach-2 starts below. Loop thru the fields to be renamed. -%>
<% Object.keys(__add).forEach(fieldName => {
  __addField = __add[fieldName];
-%>

      // <%- fieldName %><%- __addField.args %>: <%- __addField.type %>
<%# -%>
<%# --- if/else-1 starts below. -%>
<% if (__addField.serviceName) {
  __temp = [
    `      ${fieldName}: getResult('${graphqlName}.${fieldName}', '${__addField.relation.ourTable}'${__addField.isArray ? ', true' : ''}),`,
  ];
-%>
      <%- insertFragment(`resolver-${graphqlName}-${fieldName}`, __temp) %>
<% } else { -%>
      <%- insertFragment(`resolver-${graphqlName}-${fieldName}-non`, [
        `      ${fieldName}: (parent, args, content, ast) => { throw Error('GraphQL fieldName ${graphqlName}.${fieldName} is not calculated.')${sc} },`,
      ]) %>
<% } -%>
<%# --- if/else-1 ends above. -%>
<% }); -%>
<%# --- forEach-2 ends above. -%>
    },
<% }); -%>
<%# --- forEach-1 ends above. -%>

    <%- insertFragment('resolver_field_more') %>
<%# -%>
<%# === Query resolvers ======================================================================== -%>
    Query: {
<%# -%>
<%# --- forEach-3 starts below. Loop thru the schemas of GraphQL enabled services. -%>
<% Object.keys(mapping.graphqlService).forEach(graphqlName => {
  __serviceName = mapping.graphqlService[graphqlName].service;
  __graphql = feathersSpecs[__serviceName]._extensions.graphql;
  __temp = [
    `      // get${graphqlName}(query: JSON, params: JSON, key: JSON): ${graphqlName}`,
    `      get${graphqlName}(parent, args, content, ast) {`,
    `        const feathersParams = convertArgs(args, content, ast)${sc}`,
    `        return ${__serviceName}.get(args.key, feathersParams).then(extractFirstItem)${sc}`,
    '      },',
    '',
    `      // find${graphqlName}(query: JSON, params: JSON): [${graphqlName}!]`,
    `      find${graphqlName}(parent, args, content, ast) {`,
    `        const feathersParams = convertArgs(args, content, ast${__graphql.serviceSortParams})${sc}`,
    `        return ${__serviceName}.find(feathersParams).then(paginate(content)).then(extractAllItems)${sc}`,
    '      },',
  ];
-%>

      <%- insertFragment(`query-${graphqlName}`, __temp) %>
<% }); -%>
<%# --- forEach-3 ends above. -%>
      <%- insertFragment('resolver_query_more') %>
    },
  }<%- sc %>

  <%- insertFragment('func_return') %>
  return returns<%- sc %>
}<%- sc %>

<%- insertFragment('more') %>

<%- insertFragment('exports') %>
<%- `${tplExport('moduleExports')}${sc}` %>

function paginate(content<%- tplTsOnly(': any') -%>) {
  return <%- tplJsOrTs('result', '(result: any[] | Paginated<any>)') %> => {
<%- tplJsOrTs(
`    content.pagination = !result.data ? undefined : {`,
`    content.pagination = !isPaginated(result) ? undefined : {`
) %>
      total: result.total,
      limit: result.limit,
      skip: result.skip,
    }<%- sc %>

    return result<%- sc %>
  }<%- sc %>
}

<%- tplTsOnly([
  'function isPaginated<T>(it: T[] | Paginated<T>): it is Paginated<T> {',
  '  return !!(it as any).data;',
  '}',
  '',
]) -%>
<%- insertFragment('funcs') %>
<%- insertFragment('end') %>
