// ── One-command Setup ──────────────────────────────────────────────
// Run via: npm run setup
// Does the following in order:
//   1. npm install (Node.js dependencies)
//   2. pip install -r requirements.txt (Python dependencies)
//   3. playwright install firefox chromium (Playwright browsers)
//   4. Copies .env.example to .env.local if not exists
//   5. --self-heal flag: runs build check + auto-generates missing modules
//
// All steps are cross-platform (Windows, Mac, Linux).

import { execSync } from "node:child_process"
import { existsSync, copyFileSync, readFileSync, writeFileSync, mkdirSync } from "node:fs"
import { platform } from "node:os"
import { resolve, dirname } from "node:path"
import { fileURLToPath } from "node:url"

const SEP = platform() === "win32" ? "&" : ";"
const SELF = dirname(fileURLToPath(import.meta.url))
const ROOT = resolve(SELF, "..")

// ── Helpers ────────────────────────────────────────────────────────

function detectPython() {
  const candidates = platform() === "win32" ? ["python", "python3"] : ["python3", "python"]
  for (const cmd of candidates) {
    try {
      execSync(`${cmd} --version`, { stdio: "pipe", timeout: 5000 })
      return cmd
    } catch {}
  }
  console.error("Python not found. Install Python 3 from https://python.org")
  process.exit(1)
}

function detectPip(python) {
  const candidates = platform() === "win32" ? ["pip", "pip3"] : ["pip3", "pip"]
  for (const cmd of candidates) {
    try {
      execSync(`${cmd} --version`, { stdio: "pipe", timeout: 5000 })
      return cmd
    } catch {}
  }
  return `${python} -m pip`
}

function run(cmd, label, opts = {}) {
  console.log(`\n── ${label} ──`)
  try {
    execSync(cmd, { stdio: "inherit", timeout: opts.timeout || 120000, cwd: opts.cwd || ROOT })
  } catch (e) {
    if (opts.optional) {
      console.log(`  ~ Skipped (${e.message})`)
      return false
    }
    console.error(`  ✗ Failed: ${e.message}`)
    process.exit(1)
  }
  return true
}

// ── Self-Healing Module Registry ───────────────────────────────────

function loadRegistry() {
  const registryPath = resolve(ROOT, ".setup-templates", "registry.json")
  const templatesDir = resolve(ROOT, ".setup-templates", "templates")

  const builtInTemplates = {
    "data/stickers": { template: "stickers.ts" },
    "data/constants": { template: "constants.ts" },
    "lib/utils": { template: "utils.ts" },
    "types/index": { template: "types-index.ts" },
    "hooks/use-media": { template: "hooks/use-media.ts" },
    "hooks/use-debounce": { template: "hooks/use-debounce.ts" },
    "hooks/use-local-storage": { template: "hooks/use-local-storage.ts" },
  }

  const builtInFallbacks = {
    "@/data/*": "data-generic.ts",
    "@/lib/*": "lib-generic.ts",
    "@/hooks/*": "hook-generic.ts",
    "@/types/*": "types-generic.ts",
    "@/components/*": "component-generic.tsx",
    "@/utils/*": "utils-generic.ts",
  }

  let templates = { ...builtInTemplates }
  let fallbacks = { ...builtInFallbacks }

  try {
    if (existsSync(registryPath)) {
      const custom = JSON.parse(readFileSync(registryPath, "utf-8"))
      templates = { ...templates, ...(custom.templates || {}) }
      fallbacks = { ...fallbacks, ...(custom.fallbackTemplates || {}) }
    }
  } catch {}

  const templateCache = {}

  function getTemplate(templateName) {
    if (templateCache[templateName]) return templateCache[templateName]
    const candidatePaths = [
      resolve(templatesDir, templateName),
      resolve(templatesDir, "hooks", templateName.replace("hooks/", "")),
    ]
    for (const p of candidatePaths) {
      if (existsSync(p)) {
        const content = readFileSync(p, "utf-8")
        templateCache[templateName] = content
        return content
      }
    }
    return ""
  }

  function matchFallback(internalPath) {
    for (const [pattern, templateFile] of Object.entries(fallbacks)) {
      const regexStr = "^" + pattern
        .replace(/\//g, "\\/").replace(/\./g, "\\.")
        .replace(/\*/g, ".*")
        .replace(/[$^+(){}[\]|]/g, "\\$&") + "$"
      const regex = new RegExp(regexStr)
      if (regex.test(internalPath)) return templateFile
    }
    return null
  }

  function generateModule(internalPath) {
    let templateFile = templates[internalPath]?.template
    if (!templateFile) templateFile = matchFallback(internalPath)
    if (!templateFile) return { success: false, error: `No template for @/${internalPath}` }

    const content = getTemplate(templateFile)
    if (!content) return { success: false, error: `Template file not found: ${templateFile}` }

    const ext = templateFile.endsWith(".tsx") ? ".tsx" : ".ts"
    const filePath = resolve(ROOT, "src", internalPath + ext)
    mkdirSync(dirname(filePath), { recursive: true })
    writeFileSync(filePath, content, "utf-8")
    return { success: true, filePath, message: `Generated @/${internalPath}` }
  }

  return { generateModule }
}

function parseMissingModules(buildOutput) {
  const results = []
  const seen = new Set()

  // Pattern: error TS2307: Cannot find module '@/path' or its corresponding type declarations.
  const errorRegex = /error\s+TS\d+:\s+Cannot find module\s+'([^']+)'/g
  let match
  while ((match = errorRegex.exec(buildOutput)) !== null) {
    const importPath = match[1]
    if (!seen.has(importPath)) {
      seen.add(importPath)
      const isInternal = importPath.startsWith("@/") || importPath.startsWith("~/")
      const internalPath = isInternal ? importPath.replace(/^[@~]\//, "") : undefined
      results.push({ importPath, isInternal, internalPath })
    }
  }

  // Also catch webpack-style: Module not found: Error: Can't resolve '@/path'
  const webpackRegex = /Module not found: Error: Can't resolve\s+'([^']+)'/g
  while ((match = webpackRegex.exec(buildOutput)) !== null) {
    const importPath = match[1]
    if (!seen.has(importPath)) {
      seen.add(importPath)
      const isInternal = importPath.startsWith("@/") || importPath.startsWith("~/")
      const internalPath = isInternal ? importPath.replace(/^[@~]\//, "") : undefined
      results.push({ importPath, isInternal, internalPath })
    }
  }

  return results
}

// ── Main ───────────────────────────────────────────────────────────

const args = process.argv.slice(2)
const doSelfHeal = args.includes("--self-heal") || args.includes("-s")
const PY = detectPython()
const PIP = detectPip(PY)

console.log("=== CoastIT CRM Setup ===\n")

// 1. Node dependencies
run("npm install", "Installing Node.js dependencies")

// 2. Python dependencies
run(`cd browser-use-service ${SEP} ${PIP} install -r requirements.txt`, "Installing Python dependencies")

// 3. Playwright browsers
run(`${PY} -m playwright install firefox chromium`, "Installing Playwright browsers")

// 4. .env file
if (!existsSync(resolve(ROOT, ".env.local"))) {
  console.log("\n── Creating .env.local ──")
  copyFileSync(resolve(ROOT, ".env.example"), resolve(ROOT, ".env.local"))
  console.log("  ✓ Created .env.local from .env.example — edit it with your settings")
} else {
  console.log("\n── .env.local already exists, skipping ──")
}

// 5. Database migrations (auto-applies on fresh install)
console.log("\n── Running Database Migrations ──")
try {
  execSync("node scripts/run-migrations.mjs", { stdio: "inherit", timeout: 30000, cwd: ROOT })
  console.log("  ✓ Migrations complete")
} catch {
  console.log("  ~ Database not available — run migrations later with: npm run dev")
}

// 6. Self-healing build check (--self-heal flag)
if (doSelfHeal) {
  console.log("\n── Self-Healing Build Check ──")
  const { generateModule } = loadRegistry()
  let lastGeneratedCount = -1
  let iteration = 0
  const MAX_ITERATIONS = 5

  while (iteration < MAX_ITERATIONS) {
    iteration++
    console.log(`  Build check pass ${iteration}/${MAX_ITERATIONS}...`)

    let buildOutput = ""
    try {
      buildOutput = execSync("npx tsc --noEmit", { stdio: "pipe", timeout: 60000, cwd: ROOT }).toString()
    } catch (e) {
      buildOutput = e.stdout?.toString() || e.message || ""
    }

    const missing = parseMissingModules(buildOutput)
    const internalMissing = missing
      .filter(m => m.isInternal && m.internalPath)
      .filter((m, i, arr) => arr.findIndex(x => x.internalPath === m.internalPath) === i) // dedup

    if (internalMissing.length === 0) {
      console.log("  ✓ No missing internal modules found!")
      break
    }

    console.log(`  Found ${internalMissing.length} missing internal module(s)`)

    let generated = 0
    for (const mod of internalMissing) {
      const result = generateModule(mod.internalPath)
      if (result.success) {
        console.log(`    ✓ ${result.message}`)
        generated++
      } else {
        console.log(`    ✗ ${result.error}`)
      }
    }

    if (generated === 0 || generated === lastGeneratedCount) {
      console.log("  No new modules could be generated. Stopping.")
      break
    }
    lastGeneratedCount = generated
  }
}

// 7. Final summary
console.log("\n── Final Steps ──")
console.log("  1. Make sure PostgreSQL is running with database 'crm'")
console.log("  2. Pull the Ollama model:  ollama pull dolphin-llama3:8b")
console.log("  3. Edit .env.local with your settings")
console.log("  4. Run:  npm run dev")
console.log("\n=== Setup complete! ===")
