import type { z } from 'zod'
import type {
  Registry,
  RegistryEntry,
  registryItemTypeSchema,
} from '../src/registry/schema'
// @sts-nocheck
import { existsSync, promises as fs } from 'node:fs'
import path from 'node:path'
import { template } from 'lodash-es'
import { rimraf } from 'rimraf'

import { registry } from '../src/registry'
import { buildRegistry as crawlContent } from '../src/registry/crawl-content'
import { baseColors } from '../src/registry/registry-base-colors'
import { colorMapping, colors } from '../src/registry/registry-colors'
import { iconLibraries, icons } from '../src/registry/registry-icons'
import { styles } from '../src/registry/registry-styles'
import {
  registryEntrySchema,
  registrySchema,
} from '../src/registry/schema'

const REGISTRY_PATH = path.join(process.cwd(), 'src/public/r')

const REGISTRY_INDEX_WHITELIST: z.infer<typeof registryItemTypeSchema>[] = [
  'registry:ui',
  'registry:lib',
  'registry:hook',
  'registry:theme',
  'registry:block',
  'registry:example',
]

// const project = new Project({
//   compilerOptions: {},
// })

// async function createTempSourceFile(filename: string) {
//   const dir = await fs.mkdtemp(path.join(tmpdir(), 'shadcn-'))
//   return path.join(dir, filename)
// }

// ----------------------------------------------------------------------------
// Build __registry__/index.ts.
// ----------------------------------------------------------------------------
async function buildRegistry(registry: Registry) {
  let index = `// @ts-nocheck
// This file is autogenerated by scripts/build-registry.ts
// Do not edit this file directly.

export const Index: Record<string, any> = {
`

  for (const style of styles) {
    index += `  "${style.name}": {`

    // Build style index.
    for (const item of registry) {
      const resolveFiles = item.files?.map(
        file =>
          `registry/${style.name}/${
            typeof file === 'string' ? file : file.path
          }`,
      )
      if (!resolveFiles) {
        continue
      }

      const type = item.type.split(':')[1]
      const sourceFilename = ''

      // const chunks: any = []
      // if (item.type === 'registry:block') {
      //   const file = resolveFiles[0]
      //   let raw: string
      //   try {
      //     if (file) {
      //       const filename = path.basename(file)
      //     }
      //     raw = await fs.readFile(file, 'utf8')
      //   }
      //   catch (error) {
      //     continue
      //   }
      //   // const tempFile = await createTempSourceFile(filename)
      //   // const sourceFile = project.createSourceFile(tempFile, raw, {
      //   //   scriptKind: ScriptKind.TS,
      //   // })

      //   // const description = sourceFile
      //   //   .getVariableDeclaration('description')
      //   //   ?.getInitializerOrThrow()
      //   //   .asKindOrThrow(SyntaxKind.StringLiteral)
      //   //   .getLiteralValue()

      //   // item.description = description ?? ''

      //   // // Find all imports.
      //   // const imports = new Map<
      //   //   string,
      //   //   {
      //   //     module: string
      //   //     text: string
      //   //     isDefault?: boolean
      //   //   }
      //   // >()
      //   // sourceFile.getImportDeclarations().forEach((node) => {
      //   //   const module = node.getModuleSpecifier().getLiteralValue()
      //   //   node.getNamedImports().forEach((item) => {
      //   //     imports.set(item.getText(), {
      //   //       module,
      //   //       text: node.getText(),
      //   //     })
      //   //   })

      //   //   const defaultImport = node.getDefaultImport()
      //   //   if (defaultImport) {
      //   //     imports.set(defaultImport.getText(), {
      //   //       module,
      //   //       text: defaultImport.getText(),
      //   //       isDefault: true,
      //   //     })
      //   //   }
      //   // })

      //   // Find all opening tags with x-chunk attribute.
      //   // const components = sourceFile
      //   //   .getDescendantsOfKind(SyntaxKind.JsxOpeningElement)
      //   //   .filter((node) => {
      //   //     return node.getAttribute('x-chunk') !== undefined
      //   //   })

      //   // chunks = await Promise.all(
      //   //   components.map(async (component, index) => {
      //   //     const chunkName = `${item.name}-chunk-${index}`

      //   //     // Get the value of x-chunk attribute.
      //   //     const attr = component
      //   //       .getAttributeOrThrow('x-chunk')
      //   //       .asKindOrThrow(SyntaxKind.JsxAttribute)

      //   //     const description = attr
      //   //       .getInitializerOrThrow()
      //   //       .asKindOrThrow(SyntaxKind.StringLiteral)
      //   //       .getLiteralValue()

      //   //     // Delete the x-chunk attribute.
      //   //     attr.remove()

      //   //     // Add a new attribute to the component.
      //   //     component.addAttribute({
      //   //       name: 'x-chunk',
      //   //       initializer: `"${chunkName}"`,
      //   //     })

      //   //     // Get the value of x-chunk-container attribute.
      //   //     const containerAttr = component
      //   //       .getAttribute('x-chunk-container')
      //   //       ?.asKindOrThrow(SyntaxKind.JsxAttribute)

      //   //     const containerClassName = containerAttr
      //   //       ?.getInitializer()
      //   //       ?.asKindOrThrow(SyntaxKind.StringLiteral)
      //   //       .getLiteralValue()

      //   //     containerAttr?.remove()

      //   //     const parentJsxElement = component.getParentIfKindOrThrow(
      //   //       SyntaxKind.JsxElement,
      //   //     )

      //   //     // Find all opening tags on component.
      //   //     const children = parentJsxElement
      //   //       .getDescendantsOfKind(SyntaxKind.JsxOpeningElement)
      //   //       .map((node) => {
      //   //         return node.getTagNameNode().getText()
      //   //       })
      //   //       .concat(
      //   //         parentJsxElement
      //   //           .getDescendantsOfKind(SyntaxKind.JsxSelfClosingElement)
      //   //           .map((node) => {
      //   //             return node.getTagNameNode().getText()
      //   //           }),
      //   //       )

      //   //     const componentImports = new Map<
      //   //       string,
      //   //       string | string[] | Set<string>
      //   //     >()
      //   //     children.forEach((child) => {
      //   //       const importLine = imports.get(child)
      //   //       if (importLine) {
      //   //         const imports = componentImports.get(importLine.module) || []

      //   //         const newImports = importLine.isDefault
      //   //           ? importLine.text
      //   //           : new Set([...imports, child])

      //   //         componentImports.set(
      //   //           importLine.module,
      //   //           importLine?.isDefault ? newImports : Array.from(newImports),
      //   //         )
      //   //       }
      //   //     })

      //   //     const componnetImportLines = Array.from(
      //   //       componentImports.keys(),
      //   //     ).map((key) => {
      //   //       const values = componentImports.get(key)
      //   //       const specifier = Array.isArray(values)
      //   //         ? `{${values.join(',')}}`
      //   //         : values

      //   //       return `import ${specifier} from "${key}"`
      //   //     })

      //   //     const code = `
      //   //     'use client'

      //   //     ${componnetImportLines.join('\n')}

      //   //     export default function Component() {
      //   //       return (${parentJsxElement.getText()})
      //   //     }`

      //   //     const targetFile = file.replace(item.name, `${chunkName}`)
      //   //     const targetFilePath = path.join(
      //   //       cwd(),
      //   //       `registry/${style.name}/${type}/${chunkName}.ts`,
      //   //     )

      //   //     // Write component file.
      //   //     rimraf.sync(targetFilePath)
      //   //     await writeFile(targetFilePath, code, 'utf8')

      //   //     return {
      //   //       name: chunkName,
      //   //       description,
      //   //       component: `React.lazy(() => import("@/registry/${style.name}/${type}/${chunkName}")),`,
      //   //       file: targetFile,
      //   //       container: {
      //   //         className: containerClassName,
      //   //       },
      //   //     }
      //   //   }),
      //   // )

      //   // // Write the source file for blocks only.
      //   sourceFilename = `__registry__/${style.name}/${type}/${item.name}.ts`

      //   if (item.files) {
      //     const files = item.files.map(file =>
      //       typeof file === 'string'
      //         ? { type: 'registry:page', path: file }
      //         : file,
      //     )
      //     if (files?.length) {
      //       sourceFilename = `__registry__/${style.name}/${files[0].path}`
      //     }
      //   }

      //   const sourcePath = path.join(process.cwd(), sourceFilename)
      //   if (!existsSync(sourcePath)) {
      //     await fs.mkdir(sourcePath, { recursive: true })
      //   }

      //   rimraf.sync(sourcePath)
      //   // await writeFile(sourcePath, sourceFile.getText())
      //   await writeFile(sourcePath, raw)
      // }

      let componentPath = `@/registry/${style.name}/${type}/${item.name}`

      if (item.files) {
        const files = item.files.map(file =>
          typeof file === 'string'
            ? { type: 'registry:page', path: file }
            : file,
        )
        if (files?.length) {
          componentPath = `@/registry/${style.name}/${files[0].path}`
        }
      }

      index += `
    "${item.name}": {
      name: "${item.name}",
      description: "${item.description ?? ''}",
      type: "${item.type}",
      registryDependencies: ${JSON.stringify(item.registryDependencies)},
      files: [${item.files?.map((file) => {
        const filePath = `registry/${style.name}/${
          typeof file === 'string' ? file : file.path
        }`
        const resolvedFilePath = path.resolve(filePath)
        return typeof file === 'string'
          ? `"${resolvedFilePath}"`
          : `{
        path: "${filePath}",
        type: "${file.type}",
        target: "${file.target ?? ''}"
      }`
      })}],
      component: () => import("${componentPath}").then((m) => m.default),
      source: "${sourceFilename}",
      category: "${item.category ?? ''}",
      subcategory: "${item.subcategory ?? ''}"
    },`
    }

    //  TODO: maybe implement chunk?
    // chunks: [${chunks.map(
    //   chunk => `{
    //   name: "${chunk.name}",
    //   description: "${chunk.description ?? 'No description'}",
    //   component: ${chunk.component}
    //   file: "${chunk.file}",
    //   container: {
    //     className: "${chunk.container.className}"
    //   }
    // }`,
    // )}]

    index += `
  },`
  }

  index += `
}
`

  // ----------------------------------------------------------------------------
  // Build registry/index.json.
  // ----------------------------------------------------------------------------
  const items = registry
    .filter(item => ['registry:ui'].includes(item.type))
    .map((item) => {
      return {
        ...item,
        files: item.files?.map((_file) => {
          const file = { path: _file.path, type: item.type }
          return file
        }),
      }
    })
  const registryJson = JSON.stringify(items, null, 2)
  rimraf.sync(path.join(REGISTRY_PATH, 'index.json'))
  await writeFile(
    path.join(REGISTRY_PATH, 'index.json'),
    registryJson,
  )

  // Write style index.
  rimraf.sync(path.join(process.cwd(), '__registry__/index.ts'))
  await writeFile(path.join(process.cwd(), '__registry__/index.ts'), index)
}

// ----------------------------------------------------------------------------
// Build __registry__/block.ts.
// ----------------------------------------------------------------------------
async function buildBlockRegistry(registry: Registry) {
  let index = `// @ts-nocheck
// This file is autogenerated by scripts/build-registry.ts
// Do not edit this file directly.

export const Index: Record<string, any> = {
`

  for (const style of styles) {
    index += `  "${style.name}": {`

    // Build style index.
    for (const item of registry) {
      if (item.type !== 'registry:block')
        continue

      const resolveFiles = item.files?.map(
        file =>
          `registry/${style.name}/${
            typeof file === 'string' ? file : file.path
          }`,
      )
      if (!resolveFiles) {
        continue
      }

      const type = item.type.split(':')[1]

      let componentPath = `@/registry/${style.name}/${type}/${item.name}`

      if (item.files) {
        const files = item.files.map(file =>
          typeof file === 'string'
            ? { type: 'registry:page', path: file }
            : file,
        )
        if (files?.length) {
          componentPath = `@/registry/${style.name}/${files[0].path}`
        }
      }

      index += `
    "${item.name}": {
      name: "${item.name}",
      description: "${item.description ?? ''}",
      type: "${item.type}",
      registryDependencies: ${JSON.stringify(item.registryDependencies)},
      files: [${item.files?.map((file) => {
        const filePath = `registry/${style.name}/${
          typeof file === 'string' ? file : file.path
        }`
        const resolvedFilePath = path.resolve(filePath)
        return typeof file === 'string'
          ? `"${resolvedFilePath}"`
          : `{
        path: "${filePath}",
        type: "${file.type}",
        target: "${file.target ?? ''}",
        raw: () => import("@/${filePath}?raw").then((m) => m.default)
      }`
      })}],
      component: () => import("${componentPath}").then((m) => m.default),
      raw: () => import("${componentPath}?raw").then((m) => m.default),
      source: "",
      category: "${item.category ?? ''}",
      subcategory: "${item.subcategory ?? ''}"
    },`
    }

    index += `
  },`
  }

  index += `
}
`

  // Write style block.
  rimraf.sync(path.join(process.cwd(), '__registry__/block.ts'))
  await writeFile(path.join(process.cwd(), '__registry__/block.ts'), index)
}

// ----------------------------------------------------------------------------
// Build registry/styles/[style]/[name].json.
// ----------------------------------------------------------------------------
async function buildStyles(registry: Registry) {
  for (const style of styles) {
    const targetPath = path.join(REGISTRY_PATH, 'styles', style.name)

    // Create directory if it doesn't exist.
    if (!existsSync(targetPath)) {
      await fs.mkdir(targetPath, { recursive: true })
    }

    for (const item of registry) {
      if (!REGISTRY_INDEX_WHITELIST.includes(item.type)) {
        continue
      }

      let files
      if (item.files) {
        files = await Promise.all(
          item.files.map(async (_file) => {
            const file = {
              path: _file.path,
              type: _file.type,
              content: '',
              target: _file.target ?? '',
            }

            let content: string
            try {
              content = await fs.readFile(
                path.join(process.cwd(), 'src', 'registry', style.name, file.path),
                'utf8',
              )
            }
            catch (error) {
              return
            }

            // TODO: remove meta content
            // const tempFile = await createTempSourceFile(file.path)
            // const sourceFile = project.createSourceFile(tempFile, content, {
            //   scriptKind: ScriptKind.TS,
            // })

            // sourceFile.getVariableDeclaration('iframeHeight')?.remove()
            // sourceFile.getVariableDeclaration('containerClassName')?.remove()
            // sourceFile.getVariableDeclaration('description')?.remove()

            const target = file.target || ''

            // if ((!target || target === '') && item.name.startsWith('v0-')) {
            //   const fileName = file.path.split('/').pop()
            //   if (
            //     file.type === 'registry:block'
            //     || file.type === 'registry:component'
            //     || file.type === 'registry:example'
            //   ) {
            //     target = `components/${fileName}`
            //   }

            //   if (file.type === 'registry:ui') {
            //     target = `components/ui/${fileName}`
            //   }

            //   if (file.type === 'registry:hook') {
            //     target = `hooks/${fileName}`
            //   }

            //   if (file.type === 'registry:lib') {
            //     target = `lib/${fileName}`
            //   }
            // }

            return {
              path: file.path,
              type: file.type,
              // content: sourceFile.getText(),
              content,
              target,
            }
          }),
        )
      }

      // if (item.type === 'registry:block' && item.name === 'Sidebar01')
      //   console.log(item.name, item.files?.[0], files?.[0])

      const payload = registryEntrySchema
        .omit({
          // source: true,
          category: true,
          subcategory: true,
          // chunks: true,
        })
        .safeParse({
          ...item,
          files,
        })

      if (payload.success) {
        await writeFile(
          path.join(targetPath, `${item.name}.json`),
          JSON.stringify(payload.data, null, 2),
        )
      }
    }
  }

  // ----------------------------------------------------------------------------
  // Build registry/styles/index.json.
  // ----------------------------------------------------------------------------
  const stylesJson = JSON.stringify(styles, null, 2)
  await writeFile(
    path.join(REGISTRY_PATH, 'styles/index.json'),
    stylesJson,

  )
}

// ----------------------------------------------------------------------------
// Build registry/styles/[name]/index.json.
// ----------------------------------------------------------------------------
async function buildStylesIndex() {
  for (const style of styles) {
    const targetPath = path.join(REGISTRY_PATH, 'styles', style.name)

    const dependencies = [
      'tailwindcss-animate',
      'class-variance-authority',
      'lucide-vue-next',
    ]

    // TODO: Remove this when we migrate to lucide-vue-next.
    // if (style.name === "new-york") {
    //   dependencies.push("@radix-ui/react-icons")
    // }

    const payload: RegistryEntry = {
      name: style.name,
      type: 'registry:style',
      dependencies,
      registryDependencies: ['utils'],
      tailwind: {
        config: {
          plugins: [`require("tailwindcss-animate")`],
        },
      },
      cssVars: {},
      files: [],
    }

    await writeFile(
      path.join(targetPath, 'index.json'),
      JSON.stringify(payload, null, 2),

    )
  }
}

// ----------------------------------------------------------------------------
// Build registry/colors/index.json.
// ----------------------------------------------------------------------------
async function buildThemes() {
  const colorsTargetPath = path.join(REGISTRY_PATH, 'colors')
  rimraf.sync(colorsTargetPath)
  if (!existsSync(colorsTargetPath)) {
    await fs.mkdir(colorsTargetPath, { recursive: true })
  }

  const colorsData: Record<string, any> = {}
  for (const [color, value] of Object.entries(colors)) {
    if (typeof value === 'string') {
      colorsData[color] = value
      continue
    }

    if (Array.isArray(value)) {
      colorsData[color] = value.map(item => ({
        ...item,
        rgbChannel: item.rgb.replace(/^rgb\((\d+),(\d+),(\d+)\)$/, '$1 $2 $3'),
        hslChannel: item.hsl.replace(
          /^hsl\(([\d.]+),([\d.]+%),([\d.]+%)\)$/,
          '$1 $2 $3',
        ),
      }))
      continue
    }

    if (typeof value === 'object') {
      colorsData[color] = {
        ...value,
        rgbChannel: value.rgb.replace(/^rgb\((\d+),(\d+),(\d+)\)$/, '$1 $2 $3'),
        hslChannel: value.hsl.replace(
          /^hsl\(([\d.]+),([\d.]+%),([\d.]+%)\)$/,
          '$1 $2 $3',
        ),
      }
      continue
    }
  }

  await writeFile(
    path.join(colorsTargetPath, 'index.json'),
    JSON.stringify(colorsData, null, 2),

  )

  // ----------------------------------------------------------------------------
  // Build registry/colors/[base].json.
  // ----------------------------------------------------------------------------
  const BASE_STYLES = `@tailwind base;
@tailwind components;
@tailwind utilities;
  `

  const BASE_STYLES_WITH_VARIABLES = `@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  :root {
    --background: <%- colors.light["background"] %>;
    --foreground: <%- colors.light["foreground"] %>;
    --card: <%- colors.light["card"] %>;
    --card-foreground: <%- colors.light["card-foreground"] %>;
    --popover: <%- colors.light["popover"] %>;
    --popover-foreground: <%- colors.light["popover-foreground"] %>;
    --primary: <%- colors.light["primary"] %>;
    --primary-foreground: <%- colors.light["primary-foreground"] %>;
    --secondary: <%- colors.light["secondary"] %>;
    --secondary-foreground: <%- colors.light["secondary-foreground"] %>;
    --muted: <%- colors.light["muted"] %>;
    --muted-foreground: <%- colors.light["muted-foreground"] %>;
    --accent: <%- colors.light["accent"] %>;
    --accent-foreground: <%- colors.light["accent-foreground"] %>;
    --destructive: <%- colors.light["destructive"] %>;
    --destructive-foreground: <%- colors.light["destructive-foreground"] %>;
    --border: <%- colors.light["border"] %>;
    --input: <%- colors.light["input"] %>;
    --ring: <%- colors.light["ring"] %>;
    --radius: 0.5rem;
    --chart-1: <%- colors.light["chart-1"] %>;
    --chart-2: <%- colors.light["chart-2"] %>;
    --chart-3: <%- colors.light["chart-3"] %>;
    --chart-4: <%- colors.light["chart-4"] %>;
    --chart-5: <%- colors.light["chart-5"] %>;
  }

  .dark {
    --background: <%- colors.dark["background"] %>;
    --foreground: <%- colors.dark["foreground"] %>;
    --card: <%- colors.dark["card"] %>;
    --card-foreground: <%- colors.dark["card-foreground"] %>;
    --popover: <%- colors.dark["popover"] %>;
    --popover-foreground: <%- colors.dark["popover-foreground"] %>;
    --primary: <%- colors.dark["primary"] %>;
    --primary-foreground: <%- colors.dark["primary-foreground"] %>;
    --secondary: <%- colors.dark["secondary"] %>;
    --secondary-foreground: <%- colors.dark["secondary-foreground"] %>;
    --muted: <%- colors.dark["muted"] %>;
    --muted-foreground: <%- colors.dark["muted-foreground"] %>;
    --accent: <%- colors.dark["accent"] %>;
    --accent-foreground: <%- colors.dark["accent-foreground"] %>;
    --destructive: <%- colors.dark["destructive"] %>;
    --destructive-foreground: <%- colors.dark["destructive-foreground"] %>;
    --border: <%- colors.dark["border"] %>;
    --input: <%- colors.dark["input"] %>;
    --ring: <%- colors.dark["ring"] %>;
    --chart-1: <%- colors.dark["chart-1"] %>;
    --chart-2: <%- colors.dark["chart-2"] %>;
    --chart-3: <%- colors.dark["chart-3"] %>;
    --chart-4: <%- colors.dark["chart-4"] %>;
    --chart-5: <%- colors.dark["chart-5"] %>;
  }
}

@layer base {
  * {
    @apply border-border;
  }
  body {
    @apply bg-background text-foreground;
  }
}`

  for (const baseColor of ['slate', 'gray', 'zinc', 'neutral', 'stone']) {
    const base: Record<string, any> = {
      inlineColors: {},
      cssVars: {},
    }
    for (const [mode, values] of Object.entries(colorMapping)) {
      base.inlineColors[mode] = {}
      base.cssVars[mode] = {}
      for (const [key, value] of Object.entries(values)) {
        if (typeof value === 'string') {
          // Chart colors do not have a 1-to-1 mapping with tailwind colors.
          if (key.startsWith('chart-')) {
            base.cssVars[mode][key] = value
            continue
          }

          const resolvedColor = value.replace(/\{\{base\}\}-/g, `${baseColor}-`)
          base.inlineColors[mode][key] = resolvedColor

          const [resolvedBase, scale] = resolvedColor.split('-')
          const color = scale
            ? colorsData[resolvedBase].find(
              (item: any) => item.scale === Number.parseInt(scale),
            )
            : colorsData[resolvedBase]
          if (color) {
            base.cssVars[mode][key] = color.hslChannel
          }
        }
      }
    }

    // Build css vars.
    base.inlineColorsTemplate = template(BASE_STYLES)({})
    base.cssVarsTemplate = template(BASE_STYLES_WITH_VARIABLES)({
      colors: base.cssVars,
    })

    await writeFile(
      path.join(REGISTRY_PATH, `colors/${baseColor}.json`),
      JSON.stringify(base, null, 2),
    )

    // ----------------------------------------------------------------------------
    // Build registry/themes.css
    // ----------------------------------------------------------------------------
    const THEME_STYLES_WITH_VARIABLES = `
.theme-<%- theme %> {
  --background: <%- colors.light["background"] %>;
  --foreground: <%- colors.light["foreground"] %>;

  --muted: <%- colors.light["muted"] %>;
  --muted-foreground: <%- colors.light["muted-foreground"] %>;

  --popover: <%- colors.light["popover"] %>;
  --popover-foreground: <%- colors.light["popover-foreground"] %>;

  --card: <%- colors.light["card"] %>;
  --card-foreground: <%- colors.light["card-foreground"] %>;

  --border: <%- colors.light["border"] %>;
  --input: <%- colors.light["input"] %>;

  --primary: <%- colors.light["primary"] %>;
  --primary-foreground: <%- colors.light["primary-foreground"] %>;

  --secondary: <%- colors.light["secondary"] %>;
  --secondary-foreground: <%- colors.light["secondary-foreground"] %>;

  --accent: <%- colors.light["accent"] %>;
  --accent-foreground: <%- colors.light["accent-foreground"] %>;

  --destructive: <%- colors.light["destructive"] %>;
  --destructive-foreground: <%- colors.light["destructive-foreground"] %>;

  --ring: <%- colors.light["ring"] %>;

  --radius: <%- colors.light["radius"] %>;
}

.dark .theme-<%- theme %> {
  --background: <%- colors.dark["background"] %>;
  --foreground: <%- colors.dark["foreground"] %>;

  --muted: <%- colors.dark["muted"] %>;
  --muted-foreground: <%- colors.dark["muted-foreground"] %>;

  --popover: <%- colors.dark["popover"] %>;
  --popover-foreground: <%- colors.dark["popover-foreground"] %>;

  --card: <%- colors.dark["card"] %>;
  --card-foreground: <%- colors.dark["card-foreground"] %>;

  --border: <%- colors.dark["border"] %>;
  --input: <%- colors.dark["input"] %>;

  --primary: <%- colors.dark["primary"] %>;
  --primary-foreground: <%- colors.dark["primary-foreground"] %>;

  --secondary: <%- colors.dark["secondary"] %>;
  --secondary-foreground: <%- colors.dark["secondary-foreground"] %>;

  --accent: <%- colors.dark["accent"] %>;
  --accent-foreground: <%- colors.dark["accent-foreground"] %>;

  --destructive: <%- colors.dark["destructive"] %>;
  --destructive-foreground: <%- colors.dark["destructive-foreground"] %>;

  --ring: <%- colors.dark["ring"] %>;
}`

    const themeCSS = []
    for (const theme of baseColors) {
      themeCSS.push(
        template(THEME_STYLES_WITH_VARIABLES)({
          colors: theme.cssVars,
          theme: theme.name,
        }),
      )
    }

    await writeFile(
      path.join(REGISTRY_PATH, `themes.css`),
      themeCSS.join('\n'),
    )

    // ----------------------------------------------------------------------------
    // Build registry/themes/[theme].json
    // ----------------------------------------------------------------------------
    rimraf.sync(path.join(REGISTRY_PATH, 'themes'))
    for (const baseColor of ['slate', 'gray', 'zinc', 'neutral', 'stone']) {
      const payload: Record<string, any> = {
        name: baseColor,
        label: baseColor.charAt(0).toUpperCase() + baseColor.slice(1),
        cssVars: {},
      }
      for (const [mode, values] of Object.entries(colorMapping)) {
        payload.cssVars[mode] = {}
        for (const [key, value] of Object.entries(values)) {
          if (typeof value === 'string') {
            const resolvedColor = value.replace(/\{\{base\}\}-/g, `${baseColor}-`)
            payload.cssVars[mode][key] = resolvedColor

            const [resolvedBase, scale] = resolvedColor.split('-')
            const color = scale
              ? colorsData[resolvedBase].find(
                (item: any) => item.scale === Number.parseInt(scale),
              )
              : colorsData[resolvedBase]
            if (color) {
              payload.cssVars[mode][key] = color.hslChannel
            }
          }
        }
      }

      const targetPath = path.join(REGISTRY_PATH, 'themes')

      // Create directory if it doesn't exist.
      if (!existsSync(targetPath)) {
        await fs.mkdir(targetPath, { recursive: true })
      }

      await writeFile(
        path.join(targetPath, `${payload.name}.json`),
        JSON.stringify(payload, null, 2),
      )
    }
  }
}

// ----------------------------------------------------------------------------
// Build registry/icons/index.json.
// ----------------------------------------------------------------------------
async function buildIcons() {
  const iconsTargetPath = path.join(REGISTRY_PATH, 'icons')
  rimraf.sync(iconsTargetPath)
  if (!existsSync(iconsTargetPath)) {
    await fs.mkdir(iconsTargetPath, { recursive: true })
  }

  const iconsData = icons

  await writeFile(
    path.join(iconsTargetPath, 'index.json'),
    JSON.stringify(iconsData, null, 2),
  )
}

// ----------------------------------------------------------------------------
// Build __registry__/icons.ts.
// ----------------------------------------------------------------------------
async function buildRegistryIcons() {
  let index = `// @ts-nocheck
// This file is autogenerated by scripts/build-registry.ts
// Do not edit this file directly.
import * as React from "react"

export const Icons = {
`

  for (const [icon, libraries] of Object.entries(icons)) {
    index += `  "${icon}": {`
    for (const [library, componentName] of Object.entries(libraries)) {
      const packageName = iconLibraries[library as keyof typeof iconLibraries].package
      if (packageName) {
        index += `
  ${library}:  () => import("${packageName}").then(mod => ({
    default: mod.${componentName}
  })),`
      }
    }
    index += `
},`
  }

  index += `
}
`

  // Write style index.
  rimraf.sync(path.join(process.cwd(), '__registry__/icons.ts'))
  await writeFile(
    path.join(process.cwd(), '__registry__/icons.ts'),
    index,
  )
}

try {
  const content = await crawlContent()
  const result = registrySchema.safeParse([...registry, ...content])

  await writeFile(
    path.join(REGISTRY_PATH, 'temp.json'),
    JSON.stringify(result.data ?? '', null, 2),
  )

  if (!result.success) {
    console.error(result.error)
    process.exit(1)
  }

  await buildRegistry(result.data)
  await buildBlockRegistry(result.data)
  await buildStyles(result.data)
  await buildStylesIndex()
  await buildThemes()

  // await buildRegistryIcons()
  // await buildIcons()

  // eslint-disable-next-line no-console
  console.log('✅ Done!')
}
catch (error) {
  console.error(error)
  process.exit(1)
}

async function writeFile(path: string, payload: any) {
  return fs.writeFile(
    path,
    `${payload}\r\n`,
    'utf8',
  )
}
