"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getReplacers = getReplacers;

var _helpers = require("./helpers");

function getReplacers(babel) {
  function asProgram(path, fileOpts) {
    var _result$code;

    const result = babel.transformFromAstSync(path.node, path.getSource(), {
      filename: fileOpts.filename,
      plugins: fileOpts.plugins,
      presets: fileOpts.presets
    }); // istanbul ignore next because this should never happen, but TypeScript needs me to handle it

    const code = (_result$code = result == null ? void 0 : result.code) != null ? _result$code : '';
    const replacement = (0, _helpers.getReplacement)({
      code,
      fileOpts
    }, babel);
    path.node.body = Array.isArray(replacement) ? replacement : [replacement];
  }

  function asImportDeclaration(path, fileOpts) {
    var _path$node$source$lea, _path$node$source$lea2;

    const filename = (0, _helpers.getFilename)(fileOpts);
    const codegenComment = (_path$node$source$lea = path.node.source.leadingComments) == null ? void 0 : (_path$node$source$lea2 = _path$node$source$lea.find(_helpers.isCodegenComment)) == null ? void 0 : _path$node$source$lea2.value.trim(); // istanbul ignore next because we don't even call `asImportDeclaration` if
    // there's not a codegen comment, but TypeScript gets mad otherwise

    if (!codegenComment) return;
    const {
      code,
      resolvedPath
    } = (0, _helpers.resolveModuleContents)({
      filename,
      module: path.node.source.value
    });
    let args;

    if (codegenComment !== 'codegen') {
      args = (0, _helpers.requireFromString)(`module.exports = [${codegenComment.replace(/codegen\((.*)\)/, '$1').trim()}]`, filename);
    }

    (0, _helpers.replace)({
      path,
      code,
      fileOpts: { ...fileOpts,
        filename: resolvedPath
      },
      args
    }, babel);
  } // eslint-disable-next-line complexity


  function asIdentifier(path, fileOpts) {
    const targetPath = path.parentPath;

    switch (targetPath.type) {
      case 'TaggedTemplateExpression':
        {
          return asTag(targetPath, fileOpts);
        }

      case 'CallExpression':
        {
          const isCallee = targetPath.get('callee') === path;

          if (isCallee) {
            return asFunction(targetPath, fileOpts);
          } else {
            return false;
          }
        }

      case 'JSXOpeningElement':
        {
          const jsxElement = targetPath.parentPath;
          return asJSX(jsxElement, fileOpts);
        }

      case 'JSXClosingElement':
        {
          // ignore the closing element
          // but don't mark as unhandled (return false)
          // we already handled the opening element
          return true;
        }

      case 'MemberExpression':
        {
          const callPath = targetPath.parentPath; // istanbul ignore if (don't know when this could happen)

          if (!callPath) return false;
          const isRequireCall = (0, _helpers.isPropertyCall)(callPath, 'require');

          if (isRequireCall) {
            return asImportCall(callPath, fileOpts);
          } else {
            return false;
          }
        }

      default:
        {
          return false;
        }
    }
  }

  function asImportCall(path, fileOpts) {
    const [source, ...args] = path.get('arguments');
    const {
      code,
      resolvedPath
    } = (0, _helpers.resolveModuleContents)({
      filename: (0, _helpers.getFilename)(fileOpts),
      module: source.node.value
    });
    const argValues = args.map(a => {
      const result = a.evaluate();

      if (!result.confident) {
        throw new Error('codegen cannot determine the value of an argument in codegen.require');
      }

      return result.value;
    });
    (0, _helpers.replace)({
      path,
      code,
      fileOpts: { ...fileOpts,
        filename: resolvedPath
      },
      args: argValues
    }, babel);
  }

  function asTag(path, fileOpts) {
    const code = path.get('quasi').evaluate().value;

    if (!code) {
      throw path.buildCodeFrameError('Unable to determine the value of your codegen string', Error);
    }

    (0, _helpers.replace)({
      path,
      code,
      fileOpts
    }, babel);
  }

  function asFunction(path, fileOpts) {
    const argumentsPaths = path.get('arguments');
    const code = argumentsPaths[0].evaluate().value;
    (0, _helpers.replace)({
      path: argumentsPaths[0].parentPath,
      code,
      fileOpts
    }, babel);
  }

  function asJSX(path, fileOpts) {
    const children = path.get('children');
    const container = children[0];
    const expression = container.node.expression; // @ts-expect-error value isn't on all nodes, but we will assume nobody
    // uses this for anything but the right kind of nodes...

    let code = expression.value;

    if (expression.type === 'TemplateLiteral') {
      code = container.get('expression').evaluate().value;
    }

    (0, _helpers.replace)({
      path: container.parentPath,
      code,
      fileOpts
    }, babel);
  }

  return {
    asTag,
    asJSX,
    asFunction,
    asProgram,
    asImportCall,
    asImportDeclaration,
    asIdentifier
  };
}
/*
eslint
  @typescript-eslint/no-explicit-any: "off",
  complexity: ["error", 8],
  max-lines-per-function: ["error", 250],
*/