import type { Plugin } from 'vite'
import fs from 'node:fs'
import path from 'node:path'
import { codeInspectorPlugin } from 'code-inspector-plugin'
import { injectClientSnippet, normalizeViteModuleId } from './utils'

type CodeInspectorPluginOptions = {
  injectTarget: string
  port?: number
}

type ForceInspectorClientInjectionPluginOptions = CodeInspectorPluginOptions & {
  projectRoot: string
}

export const createCodeInspectorPlugin = ({
  injectTarget,
  port = 5678,
}: CodeInspectorPluginOptions): Plugin => {
  return codeInspectorPlugin({
    bundler: 'vite',
    port,
    injectTo: injectTarget,
    exclude: [/^(?!.*\.(?:js|ts|mjs|mts|jsx|tsx|vue|svelte|html)(?:$|\?)).*/],
  }) as Plugin
}

const getInspectorRuntimeSnippet = (runtimeFile: string): string => {
  if (!fs.existsSync(runtimeFile))
    return ''

  const raw = fs.readFileSync(runtimeFile, 'utf-8')

  // Strip the helper component default export to avoid duplicate default exports after injection.
  return raw.replace(
    /\s*export default function CodeInspectorEmptyElement\(\)\s*\{[\s\S]*$/,
    '',
  )
}

export const createForceInspectorClientInjectionPlugin = ({
  injectTarget,
  port = 5678,
  projectRoot,
}: ForceInspectorClientInjectionPluginOptions): Plugin => {
  const runtimeFile = path.resolve(
    projectRoot,
    `node_modules/code-inspector-plugin/dist/append-code-${port}.js`,
  )
  const clientSnippet = getInspectorRuntimeSnippet(runtimeFile)

  return {
    name: 'vinext-force-code-inspector-client',
    apply: 'serve',
    enforce: 'pre',
    transform(code, id) {
      if (!clientSnippet)
        return null

      const cleanId = normalizeViteModuleId(id)
      if (cleanId !== injectTarget)
        return null

      const nextCode = injectClientSnippet(code, 'code-inspector-component', clientSnippet)
      if (nextCode === code)
        return null

      return { code: nextCode, map: null }
    },
  }
}
