import { execSync } from "node:child_process"
import fs from "node:fs/promises"
import path from "node:path"

import type * as TF from "type-fest"

// Packages whose overrides are derived from this package's own dependency versions.
const managedPackages = ["@types/node", "@types/react", "@types/react-dom", "type-fest"]

const getMajor = (range: string): string | null => {
  const major = range.match(/\d+/)?.[0]
  return major ?? null
}

const buildManagedOverrides = async (): Promise<Record<string, string>> => {
  const ownPkgPath = new URL("../package.json", import.meta.url).pathname
  const ownPkg: TF.PackageJson = JSON.parse(await fs.readFile(ownPkgPath, `utf8`))
  const deps = { ...ownPkg.devDependencies, ...ownPkg.dependencies }

  const overrides: Record<string, string> = {}
  for (const name of managedPackages) {
    const range = deps[name]
    if (!range) {
      console.warn(`Skipping override for "${name}": not found in @10stars/config dependencies`)
      continue
    }
    const major = getMajor(range)
    if (!major) {
      console.warn(`Skipping override for "${name}": cannot parse major version from "${range}"`)
      continue
    }
    overrides[name] = `^${major}.0.0`
  }
  return overrides
}

export const setupOverrides = async (cwd: string) => {
  const pkgPath = path.join(cwd, `package.json`)

  let pkgJson: TF.PackageJson
  try {
    pkgJson = JSON.parse(await fs.readFile(pkgPath, `utf8`))
  } catch (error) {
    console.error(`Error reading or parsing package.json:`, error)
    return
  }

  const managedOverrides = await buildManagedOverrides()

  // Merge: managed values win, any existing unmanaged override keys are preserved.
  const merged = { ...pkgJson.overrides, ...managedOverrides }
  const sorted = Object.fromEntries(Object.entries(merged).sort(([a], [b]) => a.localeCompare(b)))

  const changed = JSON.stringify(pkgJson.overrides ?? {}) !== JSON.stringify(sorted)
  if (!changed) return

  pkgJson.overrides = sorted

  await fs.writeFile(pkgPath, JSON.stringify(pkgJson, null, 2) + `\n`)
  console.info(`Written: ${pkgPath}`)
  execSync(`bunx oxfmt ${pkgPath}`, { cwd, stdio: `inherit` })

  console.info(`Overrides changed, running "bun install"...`)
  execSync(`bun install`, { cwd, stdio: `inherit` })
}
