#!/usr/bin/env node
/**
 * Consumer shell upgrader for `@exxatdesignux/ui`.
 *
 * Why this exists:
 * - `npm/pnpm add @exxatdesignux/ui@...` only updates `node_modules/`
 * - `exxat-ui sync-extras` only refreshes rules / skills / docs / hooks
 * - customer apps still own copied shell files like `src/routes.tsx`
 *   and `components/sidebar/app-sidebar.tsx`
 *
 * This command closes that gap:
 * - copies low-risk package-owned shell files from `template-vite/`
 * - creates missing higher-risk shell files from the template
 * - reports app-owned files that still require a manual merge
 *
 * It deliberately does NOT touch mock data, domain hubs, API modules, or
 * tenant copy.
 */

import {
  copyFileSync,
  existsSync,
  mkdirSync,
  readFileSync,
  writeFileSync,
} from "node:fs"
import { dirname, join, relative, resolve } from "node:path"
import { execFileSync } from "node:child_process"
import { fileURLToPath } from "node:url"

const __dirname = dirname(fileURLToPath(import.meta.url))
const pkgRoot = resolve(__dirname, "..")
const cwd = process.cwd()
const templateRoot = join(pkgRoot, "template-vite")

const CHECK_ONLY = process.argv.includes("--check") || process.argv.includes("--dry-run")
const QUIET = process.argv.includes("--quiet")
const BACKUP_STAMP = new Date().toISOString().replace(/[-:TZ.]/g, "").slice(0, 14)

function log(...args) {
  if (!QUIET) console.log(...args)
}

function readJson(path) {
  return JSON.parse(readFileSync(path, "utf8"))
}

function fileText(path) {
  return readFileSync(path, "utf8")
}

function ensureParent(path) {
  mkdirSync(dirname(path), { recursive: true })
}

function sameFile(a, b) {
  if (!existsSync(a) || !existsSync(b)) return false
  return fileText(a) === fileText(b)
}

function isConsumerApp() {
  const pkgPath = join(cwd, "package.json")
  if (!existsSync(pkgPath)) return false
  try {
    const pkg = readJson(pkgPath)
    const dep =
      pkg?.dependencies?.["@exxatdesignux/ui"] ??
      pkg?.devDependencies?.["@exxatdesignux/ui"] ??
      pkg?.peerDependencies?.["@exxatdesignux/ui"]
    return Boolean(dep) && !String(dep).includes("workspace:")
  } catch {
    return false
  }
}

const AUTO_PORT_FILES = [
  "stores/app-store.ts",
  "contexts/product-context.tsx",
  "contexts/product-route-sync.tsx",
  "contexts/product-root-gate.tsx",
  "contexts/default-product-redirect.tsx",
  "lib/product-brand.ts",
  "lib/product-routing.ts",
  "lib/product-ref.ts",
  "lib/brand-accent-color.ts",
  "lib/brand-color-match.ts",
  "components/product-switcher.tsx",
  "components/product-switch-overlay.tsx",
  "components/settings-appearance-card.tsx",
  "components/brand-color-picker.tsx",
  "components/exxat-product-logo.tsx",
  "src/pages/_product-dashboard.tsx",
  "src/pages/_dashboard-loading.tsx",
  "src/styles/globals.css",
]

const MANUAL_REVIEW_FILES = [
  "src/App.tsx",
  "src/routes.tsx",
  "src/pages/settings.tsx",
  "lib/mock/navigation.tsx",
  "components/sidebar/app-sidebar.tsx",
  "package.json",
]

function backupFile(destPath, relPath) {
  const backupPath = join(cwd, ".exxat-ui", "backups", BACKUP_STAMP, relPath)
  ensureParent(backupPath)
  copyFileSync(destPath, backupPath)
  return relative(cwd, backupPath)
}

function copyTemplateFile(relPath, { createOnly = false } = {}) {
  const srcPath = join(templateRoot, relPath)
  const destPath = join(cwd, relPath)
  const destExists = existsSync(destPath)

  if (!existsSync(srcPath)) {
    throw new Error(`Template file missing: ${srcPath}`)
  }

  if (destExists && sameFile(srcPath, destPath)) {
    return { status: "unchanged", relPath }
  }

  if (createOnly && destExists) {
    return { status: "manual", relPath }
  }

  if (CHECK_ONLY) {
    return { status: destExists ? "would_update" : "would_create", relPath }
  }

  let backupRel = null
  if (destExists) backupRel = backupFile(destPath, relPath)
  ensureParent(destPath)
  writeFileSync(destPath, fileText(srcPath))
  return { status: destExists ? "updated" : "created", relPath, backupRel }
}

function runSyncExtras() {
  if (CHECK_ONLY) return
  execFileSync(process.execPath, [resolve(__dirname, "sync-extras.mjs"), "--quiet"], {
    stdio: "inherit",
    cwd,
    env: {
      ...process.env,
      EXXAT_SYNC_EXTRAS_SKIP_UPGRADE_NOTICE: "1",
    },
  })
}

function main() {
  if (!isConsumerApp()) {
    console.error("exxat-ui upgrade is for consumer apps only.")
    console.error("Run it from the customer app root that depends on @exxatdesignux/ui.")
    process.exit(1)
  }

  const pkgVersion = readJson(join(pkgRoot, "package.json")).version
  const autoResults = []
  const manualResults = []

  for (const relPath of AUTO_PORT_FILES) {
    autoResults.push(copyTemplateFile(relPath))
  }

  for (const relPath of MANUAL_REVIEW_FILES) {
    manualResults.push(copyTemplateFile(relPath, { createOnly: true }))
  }

  runSyncExtras()

  const autoChanged = autoResults.filter((r) => !["unchanged", "manual"].includes(r.status))
  const manualNeeded = manualResults.filter((r) => r.status === "manual" || r.status === "would_update")
  const manualCreated = manualResults.filter((r) => r.status === "created" || r.status === "would_create")
  const pending = autoChanged.length > 0 || manualCreated.length > 0 || manualNeeded.length > 0

  log("")
  log(`[upgrade] @exxatdesignux/ui template shell upgrade (${pkgVersion})`)
  log(`[upgrade] cwd: ${cwd}`)
  log(`[upgrade] mode: ${CHECK_ONLY ? "check / dry-run" : "apply"}`)
  log("")

  if (!CHECK_ONLY) {
    log("[upgrade] Auto-ported package-owned shell files:")
  } else {
    log("[upgrade] Package-owned shell files that would be ported:")
  }
  for (const item of autoChanged) {
    const suffix = item.backupRel ? ` (backup: ${item.backupRel})` : ""
    log(`  - ${item.relPath} — ${item.status.replace(/^would_/, "would ")}${suffix}`)
  }
  if (autoChanged.length === 0) log("  - none")

  if (manualCreated.length > 0) {
    log("")
    log(`[upgrade] Missing shell files ${CHECK_ONLY ? "would be created" : "were created"} from template:`)
    for (const item of manualCreated) {
      log(`  - ${item.relPath} — ${item.status.replace(/^would_/, "would ")}`)
    }
  }

  if (manualNeeded.length > 0) {
    log("")
    log("[upgrade] Manual merge still required for app-owned shell files:")
    for (const item of manualNeeded) {
      log(`  - ${item.relPath}`)
      log(`    template: node_modules/@exxatdesignux/ui/template-vite/${item.relPath}`)
    }
  }

  log("")
  if (manualNeeded.length === 0) {
    log(`[upgrade] ${CHECK_ONLY ? "No manual shell drift detected." : "Shell upgrade applied."}`)
  } else {
    log("[upgrade] Next:")
    log("  1. Merge the app-owned files above against template-vite.")
    log("  2. Run `pnpm typecheck` (or your package-manager equivalent).")
    log("  3. Smoke-test product switcher, settings color, and one hub.")
  }

  if (CHECK_ONLY && pending) process.exitCode = 10
}

main()
