import { addVitePlugin, defineNuxtModule, getLayerDirectories } from '@nuxt/kit'
import { getLayerFiles, getLayerContent } from '../app/utils/content-files'
import {
  getRoutes,
  getExpandedRoutes,
  getRouteShortcuts,
  getConfig,
} from '../app/utils/content-routes'
import { join, relative, dirname, basename, extname } from 'path'
import { existsSync } from 'fs'

/*  Rationale:
    1) Every project file containing useInjectedContent() is transformed and virtual modules are imported
    2) Upon request the virtual modules are compiled from ts/yaml files
    3) If any of the files that back a virtual module change the module is invalidated
 */

export default defineNuxtModule({
  setup() {
    addVitePlugin(() => ({
      name: 'content-import-injector',
      resolveId(id) {
        if (id.startsWith('virtual:content/')) {
          return '\0' + id // convention, hide from further processing
        }
      },
      async load(id) {
        if (id.startsWith('\0' + 'virtual:content/')) {
          const slug = id.slice(17)

          if (slug === 'shortcuts') {
            const routes = await getRoutes()
            const shortcuts = getRouteShortcuts(routes)

            return `export default ${JSON.stringify(shortcuts)}`
          } else if (slug === 'content.config') {
            const file = getLayerDirectories()
              .map((d) => join(d.app, 'content.config.ts'))
              .find((f) => existsSync(f))

            if (file) {
              this.addWatchFile(file)
              return `export {default} from '${file}'`
            }
          } else {
            const files = getLayerFiles(slug)
            const content = await getLayerContent(files)

            files.filter((f) => existsSync(f)).forEach((f) => this.addWatchFile(f))

            return `export const content = ${JSON.stringify(content)}`
          }
        }
      },
      hotUpdate({ file, server }) {
        const root = getLayerDirectories()
          .map((d) => d.app)
          .find((r) => file.startsWith(r))

        if (root !== undefined) {
          const path = relative(root, file)
          const slug = join(dirname(path), basename(path, extname(path)))

          const module = server.moduleGraph.getModuleById('\0' + `virtual:content/${slug}`)

          if (module) {
            server.moduleGraph.invalidateModule(module)
            this.environment.hot.send({ type: 'full-reload' })
          }
        }

        return []
      },
      transform(code, id) {
        let slug: string | undefined = undefined

        for (const root of getLayerDirectories().map((d) => d.app)) {
          if (id.startsWith(root)) {
            const path = relative(root, id).split('?')[0]!
            slug = join(dirname(path), basename(path, extname(path)))
            break
          }
        }

        if (slug === undefined) {
          return null
        }

        const expression = /\buseContentInjected\s*\(\s*\)/g
        if (!expression.test(code)) return null

        const transformed = code.replace(
          expression,
          `useContentInjected('${slug}', a23631da455de90c5a945da16897d0bd, d61397115e513f2bb1d8e872b7e7788f)`
        )

        return `
          import {content as a23631da455de90c5a945da16897d0bd} from 'virtual:content/content.global';
          import {content as d61397115e513f2bb1d8e872b7e7788f} from 'virtual:content/${slug}';
          ${transformed}
        `
      },
    }))
  },
})
