import fs from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'

import esbuild from 'esbuild'

const __dirname = path.dirname(fileURLToPath(import.meta.url))
const VENDOR_DIR = path.join(__dirname, '../src/vendor/shiki')

async function buildModule(inputPath, outputPath, name) {
  try {
    const result = await esbuild.build({
      entryPoints: [inputPath],
      bundle: true,
      write: false,
      format: 'cjs',
      platform: 'node',
      external: ['react', 'react-native'],
      define: {
        'process.env.NODE_ENV': '"production"',
      },
      banner: {
        js: `"use strict";\nObject.defineProperty(exports, "__esModule", { value: true });\n`,
      },
    })

    let outputText = result.outputFiles[0].text

    // Helper function to clean up escaped strings
    const cleanEscapedStrings = (str) => {
      return str
        .replace(/\\\\/g, '\\') // Convert double backslashes to single
        .replace(/\\"/g, '"') // Convert escaped quotes to regular quotes
        .replace(/\r\n/g, '\n') // Normalize line endings
    }

    // Process themes
    if (inputPath.includes('/themes/')) {
      const themePatterns = [/export\s+default\s+(\{[\s\S]*?\});?\s*$/, /JSON\.parse\('(.+?)'\)/, /exports\.default\s*=\s*(\{[\s\S]*?\});?\s*$/, /module\.exports\s*=\s*(\{[\s\S]*?\});?\s*$/]

      let themeData = null
      for (const pattern of themePatterns) {
        const match = outputText.match(pattern)
        if (match) {
          themeData = cleanEscapedStrings(match[1])
          break
        }
      }

      if (themeData) {
        outputText = `"use strict";\nObject.defineProperty(exports, "__esModule", { value: true });\nexports.default = ${themeData};`
      }
    }
    // Process languages
    else if (inputPath.includes('/langs/')) {
      // First try to load the original source
      const originalSource = await fs.promises.readFile(inputPath, 'utf8')
      const directExportMatch = originalSource.match(/export\s+default\s+(\[[\s\S]*?\])/)

      if (directExportMatch) {
        // Use the original source if we can find the export
        const langData = cleanEscapedStrings(directExportMatch[1])
        outputText = `"use strict";\nObject.defineProperty(exports, "__esModule", { value: true });\nexports.default = ${langData};`
      }
      else {
        // Fall back to processing the bundle
        const langPatterns = [/export\s+default\s+(\[[\s\S]*?\]);?\s*$/m, /exports\.default\s*=\s*(\[[\s\S]*?\]);?\s*$/m, /module\.exports\s*=\s*(\[[\s\S]*?\]);?\s*$/m]

        let langData = null
        for (const pattern of langPatterns) {
          const match = outputText.match(pattern)
          if (match) {
            langData = cleanEscapedStrings(match[1])
            break
          }
        }

        if (langData) {
          outputText = `"use strict";\nObject.defineProperty(exports, "__esModule", { value: true });\nexports.default = ${langData};`
        }
      }
    }

    fs.writeFileSync(outputPath, outputText)
    createDeclarationFile(outputPath, name || path.basename(outputPath, '.js'))
    return true
  }
  catch (error) {
    console.error(`Failed to build ${inputPath}:`, error)
    return false
  }
}

function createDeclarationFile(outputPath, name) {
  if (!outputPath.includes('/themes/') && !outputPath.includes('/langs/') && !outputPath.includes('/core/')) {
    return
  }

  const dtsPath = outputPath.replace('.js', '.d.ts')
  const safeName = name.replace(/-/g, '_')
  const reservedWords = ['for', 'switch', 'case', 'break', 'default', 'else', 'var', 'const', 'let', 'function', 'class']
  const finalName = reservedWords.includes(safeName) ? `_${safeName}` : safeName

  let dtsContent = ''

  if (outputPath.includes('/core/')) {
    if (outputPath.includes('core.js')) {
      dtsContent = `\
import type { LanguageInput, ThemeInput } from 'shiki'

export interface HighlighterCoreOptions {
  engine: any
  langs: LanguageInput[]
  themes: ThemeInput[]
}

export function createHighlighterCore(options: HighlighterCoreOptions): Promise<{
  codeToTokens: (code: string, options: { lang: string; theme: string }) => any
}>`
    }
    else if (outputPath.includes('engine-javascript.js')) {
      dtsContent = `\
export function createJavaScriptRegexEngine(): {
  grammar: any
  getRuleStack: (line: string) => any
  tokenizeLine: (line: string, stack: any) => any
}`
    }
  }
  else {
    dtsContent = `\
import type { ${outputPath.includes('/themes/') ? 'ThemeInput' : 'LanguageInput'} } from 'shiki'

declare const ${finalName}: ${outputPath.includes('/themes/') ? 'ThemeInput' : 'LanguageInput'}
export default ${finalName}`
  }

  fs.writeFileSync(dtsPath, dtsContent)
}

const shikiDir = path.join(__dirname, '../node_modules/shiki/dist')
const langFiles = fs
  .readdirSync(path.join(shikiDir, 'langs'))
  .filter(file => file.endsWith('.mjs'))
  .map(file => ({
    input: `shiki/dist/langs/${file}`,
    output: `src/vendor/shiki/langs/${file.replace('.mjs', '.js')}`,
    name: file.replace('.mjs', ''),
  }))

const themeFiles = fs
  .readdirSync(path.join(shikiDir, 'themes'))
  .filter(file => file.endsWith('.mjs'))
  .map(file => ({
    input: `shiki/dist/themes/${file}`,
    output: `src/vendor/shiki/themes/${file.replace('.mjs', '.js')}`,
    name: file.replace('.mjs', ''),
  }))

const modules = [
  {
    input: 'shiki/dist/core.mjs',
    output: 'src/vendor/shiki/core/core.js',
  },
  {
    input: 'shiki/dist/engine-javascript.mjs',
    output: 'src/vendor/shiki/core/engine-javascript.js',
  },
  ...langFiles,
  ...themeFiles,
]

// Initialize manifest
const manifest = {
  version: JSON.parse(fs.readFileSync(path.join(__dirname, '../node_modules/shiki/package.json'), 'utf8')).version,
  buildDate: new Date().toISOString(),
  core: [],
  langs: {},
  themes: {},
}

// Ensure vendor directory exists
if (!fs.existsSync(VENDOR_DIR)) {
  fs.mkdirSync(VENDOR_DIR, { recursive: true })
}

// Create necessary subdirectories
;['core', 'langs', 'themes'].forEach((dir) => {
  const fullPath = path.join(VENDOR_DIR, dir)
  if (!fs.existsSync(fullPath)) {
    fs.mkdirSync(fullPath, { recursive: true })
  }
})

// Build modules
for (const module of modules) {
  const inputPath = path.join(__dirname, '../node_modules', module.input)
  const outputPath = path.join(__dirname, '..', module.output)

  const dir = path.dirname(outputPath)
  if (!fs.existsSync(dir)) {
    fs.mkdirSync(dir, { recursive: true })
  }

  await buildModule(inputPath, outputPath, module.name)

  // Update manifest
  if (module.input.includes('/langs/')) {
    manifest.langs[module.name] = {
      file: path.relative(VENDOR_DIR, outputPath),
      built: new Date().toISOString(),
    }
  }
  else if (module.input.includes('/themes/')) {
    manifest.themes[module.name] = {
      file: path.relative(VENDOR_DIR, outputPath),
      built: new Date().toISOString(),
    }
  }
  else {
    manifest.core.push({
      name: module.name || path.basename(module.output, '.js'),
      file: path.relative(VENDOR_DIR, outputPath),
      built: new Date().toISOString(),
    })
  }
}

// Write manifest
fs.writeFileSync(path.join(VENDOR_DIR, 'manifest.json'), JSON.stringify(manifest, null, 2))
