import fs from "fs"
import { extname } from "path"
import * as babel from "@babel/core"
import type * as esbuild from "esbuild"
import { fileName } from "./babel-filename"
import { dynamicImport } from "./babel-dynamic-import"
import { inlineCSSVariables } from "./babel-inline-css-vars"
import { componentName } from "./babel-component-name"
// import { whyDidYouRender } from "./babel-why-did-you-render";
// import constantElement from "./babel-constant-element";

const __PROD__ = process.env.NODE_ENV === "production"

export type BabelPlugin<T = unknown> = (
  Babel: typeof babel,
) => babel.PluginObj<T & babel.PluginPass>

export interface BabelPluginData {
  path: string
}

export const babelPlugin = (extraPlugins: babel.PluginItem[] = []): esbuild.Plugin => ({
  name: "babel",
  setup(build) {
    function* getBabelPlugins(
      path: string,
      content: string,
    ): Generator<babel.PluginItem, void, unknown> {
      const hasFileName = content.includes("__filename")
      const hasDirName = content.includes("__dirname")
      if (hasFileName || hasDirName) {
        yield fileName({ hasFileName, hasDirName, path })
      }
      if (!__PROD__ && /<\w/.test(content)) {
        yield componentName()
      }
      // if (!__PROD__ && content.includes("use")) {
      //   yield whyDidYouRender({
      //     hookName: "useWhyDidYouUpdate",
      //     hookPath: resolve(__dirname, "../../src/shared/hooks/useWhy.ts"),
      //     ignoredHooks: ["useTreeContext"],
      //   });
      // }
      if (content.includes("vars.") || content.includes("token.")) {
        yield inlineCSSVariables()
      }
      if (content.includes("await import(`")) {
        yield dynamicImport(path)
      }
      if (content.includes('.macro"') || content.includes("/macro")) {
        yield ["babel-plugin-macros", { typeGraphQL: { useParameterDecorator: true } }]
      }
      if (content.includes("@emotion")) {
        yield [require("@emotion/babel-plugin"), { sourceMap: false }]
      }

      if (__PROD__ && content.includes("gql`")) {
        yield [require("babel-plugin-transform-minify-gql-template-literals")]
      }
    }

    build.onLoad({ filter: /\.tsx?$/ }, args => {
      if (args.path.includes("node_modules/")) {
        return null
      }

      const { path } = args
      const file = fs.readFileSync(path, "utf-8")

      const plugins: babel.PluginItem[] = Array.from(getBabelPlugins(path, file)).concat(
        extraPlugins,
      )

      let code = file
      const pluginData: BabelPluginData = { path }

      if (plugins.length) {
        const res = babel.transformSync(file, {
          filename: path,
          babelrc: false,
          configFile: false,
          parserOpts: {
            plugins: ["typescript", "decorators-legacy", "jsx", "importAssertions"],
          },
          generatorOpts: { decoratorsBeforeExport: true },
          plugins,
        })!

        code = res.code!
      } else {
        return null
      }

      return {
        contents: code,
        loader: extname(path).slice(1) as any,
        pluginData,
      }
    })
  },
})
