/* eslint-disable @lwc/lwc/no-async-await */
const path = require('path')
const fs = require('fs')
const recursive = require('recursive-readdir')
const compiler = require('vue/compiler-sfc')
const { minify } = require('terser')
const babel = require('@babel/core')
const presetJsx = require('@vue/babel-preset-jsx')

const terserOptions = {
  compress: {
    defaults: false,
  },
  format: {
    comments: (obj, token) => {
      const comment = token.value
      return comment.startsWith(' webpackChunkName')
    },
  },
}

const srcDir = path.join(process.cwd(), 'src')

recursive(
  srcDir,
  [
    (file, stats) => {
      if (stats.isDirectory()) {
        // 忽略自动导入目录
        return (
          path.basename(file) === '_import-components' || path.basename(file) === 'sd-icon-library'
        )
      }
      if (path.extname(file) !== '.vue' && path.extname(file) !== '.js') {
        // 非vue、js文件不处理
        return true
      }
      // if (path.basename(file) !== 'ext-layout.vue') {
      //   // ext-layout.vue sd-data-table.vue sd-advance-search-btn.vue code-export.js sd-collapse-layout-render.vue sd-quill-reader.vue config-common.js sd-user-item.vue sd-webflow.vue sd-child-table-render.vue
      //   return true
      // }
      return false
    },
  ],
  function(_err, files) {
    files.forEach((file) => {
      if (file.endsWith('.vue')) {
        processVue(file)
      } else {
        processJS(file)
      }
    })
  }
)

function logError(path, error) {
  console.log('file:', path)
}

async function compileAndMinifyJS(code, path) {
  try {
    const compiled = babel.transformSync(code, {
      configFile: false,
      presets: [presetJsx],
    }).code
    // 压缩 js
    const minifyResult = await minify(compiled, terserOptions)
    return '\n/* eslint-disable */\n// sd-skip-auto-import\n' + minifyResult.code
  } catch (error) {
    logError(path, error)
    throw error
  }
}

async function processJS(path) {
  const content = fs.readFileSync(path).toString()
  writeFile(path, await compileAndMinifyJS(content, path))
}

async function processVue(path) {
  const content = fs.readFileSync(path).toString()

  const parseResult = compiler.parseComponent(content)
  let script = parseResult.script.content
  let result
  if (parseResult.template) {
    // 有模板
    // functional 组件处理不了
    if (parseResult.template.attrs.functional) return

    let render
    const template = parseResult.template.content
    render = compiler.compileTemplate({
      source: template,
      filename: path,
      compilerOptions: { whitespace: 'condense' },
    }).code
    // render这个变量可能会冲突，改掉
    render = render
      .replace('render = function render', '_render = function _render')
      .replace('render._withStripped = true', '_render._withStripped = true')

    const beforeTemplate = content.substring(0, parseResult.template.start)
    const afterTemplate = content.substring(parseResult.template.end, parseResult.script.start)
    const afterScript = content.substring(parseResult.script.end)

    const isExportDefault = script.indexOf('export default {') !== -1

    let defaultExportName
    if (isExportDefault) {
      script = script.replace(/}[\s;]*$/, 'render:_render,staticRenderFns,}')
    } else {
      const match = script.match(/^export default (.*?)$/m)
      defaultExportName = match[1]
    }
    // 加上 render
    if (isExportDefault) {
      script = script.replace(/export default ?{/, render + '\nexport default {\n')
    } else {
      script = script.replace(
        'export default ' + defaultExportName,
        render +
          '\n' +
          defaultExportName +
          '.render = _render;' +
          defaultExportName +
          '.staticRenderFns = staticRenderFns;export default ' +
          defaultExportName
      )
    }

    result =
      (beforeTemplate + afterTemplate).replace(/^<template.*?><\/template>$/gm, '') +
      (await compileAndMinifyJS(script, path)) +
      afterScript
  } else {
    // 没有模板
    const beforeScript = content.substring(0, parseResult.script.start)
    const afterScript = content.substring(parseResult.script.end)
    result = beforeScript + (await compileAndMinifyJS(script, path)) + afterScript
  }

  writeFile(path, result)
}

function writeFile(jsFile, jsContent) {
  // 生成最终的js文件
  fs.writeFile(jsFile, jsContent, (error) => {
    if (error) {
      // eslint-disable-next-line no-console
      console.error('Error while creating ' + jsFile)
      throw error
    }
  })
}
