import { exec } from "child_process"
import { promises as fs } from "fs"
import path from "path"
import { rimraf } from "rimraf"

import { getAllBlocks } from "@/lib/blocks"
import { registry } from "@/registry/index"

async function buildRegistryIndex() {
  let index = `/* eslint-disable @typescript-eslint/ban-ts-comment */
/* eslint-disable @typescript-eslint/no-explicit-any */
// @ts-nocheck
// This file is autogenerated by scripts/build-registry.ts
// Do not edit this file directly.
import * as React from "react"

export const Index: Record<string, any> = {`
  for (const item of registry.items) {
    const resolveFiles = item.files?.map(
      (file) => `registry/new-york-v4/${file.path}`
    )
    if (!resolveFiles) {
      continue
    }

    const componentPath = item.files?.[0]?.path
      ? `@/registry/new-york-v4/${item.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/new-york-v4/${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: ${
      componentPath
        ? `React.lazy(async () => {
      const mod = await import("${componentPath}")
      const exportName = Object.keys(mod).find(key => typeof mod[key] === 'function' || typeof mod[key] === 'object') || item.name
      return { default: mod.default || mod[exportName] }
    })`
        : "null"
    },
    categories: ${JSON.stringify(item.categories)},
    meta: ${JSON.stringify(item.meta)},
  },`
  }

  index += `
  }`

  console.log(`#️⃣  ${Object.keys(registry.items).length} components found`)

  // Write style index.
  rimraf.sync(path.join(process.cwd(), "registry/__index__.tsx"))
  await fs.writeFile(path.join(process.cwd(), "registry/__index__.tsx"), index)
}

async function buildRegistryJsonFile() {
  // 1. Fix the path for registry items.
  const fixedRegistry = {
    ...registry,
    items: registry.items.map((item) => {
      const files = item.files?.map((file) => {
        return {
          ...file,
          path: `registry/new-york-v4/${file.path}`,
        }
      })

      return {
        ...item,
        files,
      }
    }),
  }

  // 2. Write the content of the registry to `registry.json`
  rimraf.sync(path.join(process.cwd(), `registry.json`))
  await fs.writeFile(
    path.join(process.cwd(), `registry.json`),
    JSON.stringify(fixedRegistry, null, 2)
  )
}

async function buildRegistry() {
  return new Promise((resolve, reject) => {
    const process = exec(
      `pnpm dlx shadcn build registry.json --output ../www/public/r/styles/new-york-v4`
    )

    process.on("exit", (code) => {
      if (code === 0) {
        resolve(undefined)
      } else {
        reject(new Error(`Process exited with code ${code}`))
      }
    })
  })
}

async function syncRegistry() {
  // Store the current registry content
  const registryDir = path.join(process.cwd(), "registry")
  const registryIndexPath = path.join(registryDir, "__index__.tsx")
  let registryContent = null

  try {
    registryContent = await fs.readFile(registryIndexPath, "utf8")
  } catch {
    // File might not exist yet, that's ok
  }

  // 1. Call pnpm registry:build for www.
  await exec("pnpm --filter=www registry:build")

  // 2. Copy the www/public/r directory to v4/public/r.
  rimraf.sync(path.join(process.cwd(), "public/r"))
  await fs.cp(
    path.resolve(process.cwd(), "../www/public/r"),
    path.resolve(process.cwd(), "public/r"),
    { recursive: true }
  )

  // 3. Restore the registry content if we had it
  if (registryContent) {
    await fs.writeFile(registryIndexPath, registryContent, "utf8")
  }
}

async function buildBlocksIndex() {
  const blocks = await getAllBlocks(["registry:block"])

  const payload = blocks.map((block) => ({
    name: block.name,
    description: block.description,
    categories: block.categories,
  }))

  rimraf.sync(path.join(process.cwd(), "registry/__blocks__.json"))
  await fs.writeFile(
    path.join(process.cwd(), "registry/__blocks__.json"),
    JSON.stringify(payload, null, 2)
  )
}

try {
  console.log("🗂️ Building registry/__index__.tsx...")
  await buildRegistryIndex()

  console.log("🗂️ Building registry/__blocks__.json...")
  await buildBlocksIndex()

  console.log("💅 Building registry.json...")
  await buildRegistryJsonFile()

  console.log("🏗️ Building registry...")
  await buildRegistry()

  console.log("🔄 Syncing registry...")
  await syncRegistry()
} catch (error) {
  console.error(error)
  process.exit(1)
}
