#!/usr/bin/env node
import { spawn, spawnSync } from 'node:child_process'
import fs from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import { fileURLToPath } from 'node:url'

import {
  DEFAULT_LOCAL_PORT,
  REQUIRED_NODE_MAJOR,
  findAvailablePort,
  parseNodeMajor,
  renderRootEnv,
  renderWorkerEnv,
} from './student-release-utils.mjs'
import { parseEnvValue } from './apply-local-runtime-patch.mjs'

const __dirname = path.dirname(fileURLToPath(import.meta.url))
const rootDir = path.resolve(__dirname, '..')
const workerDir = path.join(rootDir, 'cf-worker')
const args = new Set(process.argv.slice(2))
const setupOnly = args.has('--setup-only')
const noBrowser = args.has('--no-browser')

function log(message = '') {
  console.log(message)
}

function ensureNode() {
  const major = parseNodeMajor(process.version)
  if (major < REQUIRED_NODE_MAJOR) {
    console.error(`\n[error] 当前 Node.js 是 ${process.version}，本项目需要 Node.js ${REQUIRED_NODE_MAJOR}+。`)
    console.error('请让 AI coding 工具先安装 Node.js 22 或 24，然后重新运行：npm run local\n')
    process.exit(1)
  }
}

function run(command, commandArgs, options = {}) {
  const result = spawnSync(command, commandArgs, {
    cwd: options.cwd || rootDir,
    env: { ...process.env, ...(options.env || {}) },
    stdio: 'inherit',
    shell: process.platform === 'win32',
  })
  if (result.status !== 0) {
    throw new Error(`${command} ${commandArgs.join(' ')} 失败`)
  }
}

function ensureDependencies(cwd, label) {
  if (fs.existsSync(path.join(cwd, 'node_modules'))) {
    log(`✓ ${label} 依赖已存在`)
    return
  }
  log(`\n==> 安装 ${label} 依赖`)
  const hasLock = fs.existsSync(path.join(cwd, 'package-lock.json'))
  run('npm', [hasLock ? 'ci' : 'install'], { cwd })
}

function writeEnvFiles(port) {
  const workerEnvPath = path.join(workerDir, '.env')
  const previousWorkerEnv = fs.existsSync(workerEnvPath) ? fs.readFileSync(workerEnvPath, 'utf8') : ''
  const runtimePatch = parseEnvValue(previousWorkerEnv, 'AIPROS_LOCAL_RUNTIME_PATCH')
  fs.writeFileSync(path.join(rootDir, '.env'), renderRootEnv({ port }))
  const workerEnv = runtimePatch
    ? `${renderWorkerEnv({ port, rootDir })}AIPROS_LOCAL_RUNTIME_PATCH=${runtimePatch}\n`
    : renderWorkerEnv({ port, rootDir })
  fs.writeFileSync(workerEnvPath, workerEnv)
}

function openBrowser(url) {
  if (noBrowser) return
  const platform = process.platform
  const command = platform === 'darwin' ? 'open' : platform === 'win32' ? 'cmd' : 'xdg-open'
  const commandArgs = platform === 'win32' ? ['/c', 'start', '', url] : [url]
  const child = spawn(command, commandArgs, {
    detached: true,
    stdio: 'ignore',
    shell: false,
  })
  child.unref()
}

ensureNode()

const port = Number(process.env.PORT || 0) || await findAvailablePort(DEFAULT_LOCAL_PORT)
const localUrl = `http://localhost:${port}`

try {
  log('AIPROS Lite 学员本地启动器')
  log(`工作目录：${rootDir}`)
  log(`Node.js：${process.version} (${os.platform()} ${os.arch()})`)

  writeEnvFiles(port)
  ensureDependencies(rootDir, '前端')
  ensureDependencies(workerDir, '后端')

  log('\n==> 初始化本地数据库')
  run('npm', ['run', 'init-db'], { cwd: workerDir })
  run('node', ['scripts/apply-local-runtime-patch.mjs'], { cwd: rootDir })
  run('node', ['scripts/bundled-skills-import.cjs'], {
    cwd: workerDir,
    env: {
      DB_PATH: path.join(workerDir, 'data', 'aipro.db'),
      UPLOADS_DIR: path.join(workerDir, 'uploads'),
    },
  })

  log('\n==> 构建前端')
  run('npm', ['run', 'build'], {
    cwd: rootDir,
    env: {
      VITE_API_BASE: '/api',
      VITE_PUBLIC_BASE_URL: localUrl,
    },
  })

  if (setupOnly) {
    log('\n✓ 初始化完成。启动服务请运行：npm run local')
    process.exit(0)
  }

  log('\n==> 启动本地服务')
  log(`访问地址：${localUrl}`)
  log('登录时可以使用任意邮箱 + 中国大陆手机号格式。按 Ctrl+C 停止服务。\n')

  const child = spawn('node', ['server.js'], {
    cwd: workerDir,
    env: {
      ...process.env,
      PORT: String(port),
      DB_PATH: path.join(workerDir, 'data', 'aipro.db'),
      UPLOADS_DIR: path.join(workerDir, 'uploads'),
      DIST_DIR: path.join(rootDir, 'dist'),
      ALLOW_ORIGINS: `${localUrl},http://127.0.0.1:${port}`,
    },
    stdio: 'inherit',
  })

  setTimeout(() => openBrowser(localUrl), 1200)
  process.on('SIGINT', () => child.kill('SIGINT'))
  process.on('SIGTERM', () => child.kill('SIGTERM'))
  child.on('exit', (code) => process.exit(code ?? 0))
} catch (err) {
  console.error(`\n[error] ${err.message}`)
  process.exit(1)
}
