const sfc = require('vue/compiler-sfc');
const babel = require('@babel/core');
const esbuild = require('esbuild');
const nodePath = require('path');

const isJsxFile = (path) => /\.(j|t)sx$/.test(path);
const isTsxFile = (path) => /\.tsx$/.test(path);
const isVueFile = (path) => /\.vue$/.test(path);

const transformJsx = (code, path) => {
  const babelResult = babel.transformSync(code, {
    filename: path,
    babelrc: false,
    presets: isTsxFile(path) ? ['@babel/preset-typescript'] : [],
    plugins: [['@vue/babel-plugin-jsx']],
  });
  return babelResult?.code || '';
};

const transformSFC = (code, path) => {
  const parsedPath = nodePath.parse(path);
  const { descriptor, errors } = sfc.parse(code, {
    filename: parsedPath.base,
    sourceRoot: parsedPath.dir,
  });

  if (errors.length) {
    errors.forEach((error) => console.error(error));
    return '';
  }

  const output = [];
  let bindingMetadata = {};

  if (descriptor.script) {
    const content = descriptor.script.content.replace(
      'export default',
      'const script =',
    );
    output.push(content);
  } else if (descriptor.scriptSetup) {
    const result = sfc.compileScript(descriptor, {
      id: 'mock',
    });

    const content = result.content.replace('export default', 'const script =');
    output.push(content);

    if (result.bindings) {
      bindingMetadata = result.bindings;
    }
  } else {
    output.push(`const script = {};`);
  }

  if (descriptor.template) {
    const render = sfc.compileTemplate({
      id: 'mock',
      source: descriptor.template.content,
      filename: path,
      compilerOptions: {
        bindingMetadata,
      },
    }).code;
    output.push(render);
    output.push('script.render = render;');
  }

  output.push('export default script;');

  return output.join('\n');
};

const transformScript = (code) =>
  esbuild.transformSync(code, {
    target: 'es2016',
    format: 'cjs',
    loader: 'ts',
  }).code;

module.exports = {
  canInstrument: true,
  process(code, path) {
    if (isVueFile(path)) {
      code = transformSFC(code, path);
    }
    if (isJsxFile(path)) {
      code = transformJsx(code, path);
    }
    return {
      code: transformScript(code),
    };
  },
};
