#!/usr/bin/env node
import { spawnSync } from 'node:child_process'
import fs from 'node:fs'
import path from 'node:path'
import readline from 'node:readline/promises'
import { fileURLToPath } from 'node:url'

import {
  CLOUDFLARE_DATABASE_NAME,
  CLOUDFLARE_PAGES_PROJECT,
  CLOUDFLARE_R2_BUCKET,
  REQUIRED_NODE_MAJOR,
  cloudflareSeedCommandArgs,
  parseNodeMajor,
  parseWranglerD1DatabaseId,
  parseWranglerPagesUrl,
  parseWranglerWorkerUrl,
  patchWranglerToml,
  sanitizeD1SeedSql,
} from './student-release-utils.mjs'

const __dirname = path.dirname(fileURLToPath(import.meta.url))
const rootDir = path.resolve(__dirname, '..')
const workerDir = path.join(rootDir, 'cf-worker')
const wranglerTomlPath = path.join(workerDir, 'wrangler.toml')
const rl = readline.createInterface({ input: process.stdin, output: process.stdout })

function run(command, args, { cwd = rootDir, env = {}, allowFailure = false, capture = false } = {}) {
  const result = spawnSync(command, args, {
    cwd,
    env: { ...process.env, ...env },
    encoding: 'utf8',
    stdio: capture ? ['inherit', 'pipe', 'pipe'] : 'inherit',
    shell: process.platform === 'win32',
  })
  const output = `${result.stdout || ''}${result.stderr || ''}`
  if (result.status !== 0 && !allowFailure) {
    throw new Error(`${command} ${args.join(' ')} 失败\n${output}`)
  }
  return { ok: result.status === 0, output }
}

async function ask(question) {
  return (await rl.question(question)).trim()
}

function ensureNode() {
  if (parseNodeMajor(process.version) < REQUIRED_NODE_MAJOR) {
    throw new Error(`当前 Node.js 是 ${process.version}，需要 ${REQUIRED_NODE_MAJOR}+`)
  }
}

function ensureDependencies(cwd) {
  if (fs.existsSync(path.join(cwd, 'node_modules'))) return
  const hasLock = fs.existsSync(path.join(cwd, 'package-lock.json'))
  run('npm', [hasLock ? 'ci' : 'install'], { cwd })
}

function readToml() {
  return fs.readFileSync(wranglerTomlPath, 'utf8')
}

function writeToml(text) {
  fs.writeFileSync(wranglerTomlPath, text)
}

function prepareCloudflareSeedFile() {
  const tmpDir = path.join(workerDir, '.tmp')
  const tmpFile = path.join(tmpDir, 'categories.cloudflare.sql')
  const source = fs.readFileSync(path.join(workerDir, 'seed', 'categories.sql'), 'utf8')
  fs.mkdirSync(tmpDir, { recursive: true })
  fs.writeFileSync(tmpFile, sanitizeD1SeedSql(source))
  return './.tmp/categories.cloudflare.sql'
}

function existingDatabaseId(toml) {
  const id = parseWranglerD1DatabaseId(toml)
  return id && !/^0{8}-0{4}-0{4}-0{4}-0{12}$/.test(id) ? id : ''
}

try {
  ensureNode()
  console.log('AIPROS Lite Cloudflare 部署向导\n')
  console.log('如果浏览器弹出 Cloudflare 授权页，请登录或注册账号，并点击授权。')

  ensureDependencies(rootDir)
  ensureDependencies(workerDir)

  const whoami = run('npx', ['wrangler', 'whoami'], { cwd: workerDir, allowFailure: true, capture: true })
  if (!whoami.ok) {
    console.log('\n==> 打开 Cloudflare 登录授权')
    run('npx', ['wrangler', 'login'], { cwd: workerDir })
  } else {
    console.log('✓ Cloudflare 已登录')
  }

  let toml = readToml()
  let databaseId = existingDatabaseId(toml)
  if (!databaseId) {
    console.log(`\n==> 创建 D1 数据库：${CLOUDFLARE_DATABASE_NAME}`)
    const created = run('npx', ['wrangler', 'd1', 'create', CLOUDFLARE_DATABASE_NAME], {
      cwd: workerDir,
      allowFailure: true,
      capture: true,
    })
    databaseId = parseWranglerD1DatabaseId(created.output)
    if (!databaseId) {
      console.log(created.output)
      databaseId = await ask('请从上方输出或 Cloudflare 控制台复制 database_id 粘贴到这里：')
    }
    if (!databaseId) throw new Error('未获得 D1 database_id，无法继续')
    toml = patchWranglerToml(toml, { databaseId })
    writeToml(toml)
    console.log('✓ 已回填 cf-worker/wrangler.toml 的 database_id')
  } else {
    console.log(`✓ D1 database_id 已配置：${databaseId}`)
  }

  console.log(`\n==> 创建或确认 R2 桶：${CLOUDFLARE_R2_BUCKET}`)
  run('npx', ['wrangler', 'r2', 'bucket', 'create', CLOUDFLARE_R2_BUCKET], {
    cwd: workerDir,
    allowFailure: true,
  })

  console.log('\n==> 执行 D1 迁移')
  run('npx', ['wrangler', 'd1', 'migrations', 'apply', CLOUDFLARE_DATABASE_NAME, '--remote'], { cwd: workerDir })

  console.log('\n==> 灌入流程架构种子数据')
  const seedFile = prepareCloudflareSeedFile()
  run('npx', cloudflareSeedCommandArgs(CLOUDFLARE_DATABASE_NAME, seedFile), { cwd: workerDir })

  console.log('\n==> 部署 Worker API')
  const workerDeploy = run('npx', ['wrangler', 'deploy'], { cwd: workerDir, capture: true })
  process.stdout.write(workerDeploy.output)
  let workerUrl = parseWranglerWorkerUrl(workerDeploy.output)
  if (!workerUrl) {
    workerUrl = await ask('请粘贴 Worker URL（形如 https://xxx.workers.dev）：')
  }
  if (!workerUrl) throw new Error('未获得 Worker URL，无法构建前端')

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

  console.log(`\n==> 创建或确认 Pages 项目：${CLOUDFLARE_PAGES_PROJECT}`)
  run('npx', ['wrangler', 'pages', 'project', 'create', CLOUDFLARE_PAGES_PROJECT, '--production-branch', 'main'], {
    cwd: rootDir,
    allowFailure: true,
  })

  console.log('\n==> 部署 Cloudflare Pages')
  const pagesDeploy = run('npx', [
    'wrangler',
    'pages',
    'deploy',
    'dist',
    '--project-name',
    CLOUDFLARE_PAGES_PROJECT,
    '--branch',
    'main',
    '--commit-dirty=true',
  ], { cwd: rootDir, capture: true })
  process.stdout.write(pagesDeploy.output)
  let pagesUrl = parseWranglerPagesUrl(pagesDeploy.output)
  if (!pagesUrl) {
    pagesUrl = await ask('请粘贴 Pages URL（形如 https://xxx.pages.dev，可直接回车跳过自动 CORS 回填）：')
  }

  if (pagesUrl) {
    console.log('\n==> 回填 Pages 域名到 Worker CORS 并重新部署 Worker')
    const allowOrigins = `${pagesUrl},http://localhost:5180,http://localhost:4000`
    writeToml(patchWranglerToml(readToml(), { allowOrigins }))
    run('npx', ['wrangler', 'deploy'], { cwd: workerDir })
  }

  console.log('\n部署流程完成。')
  console.log(`Worker API：${workerUrl}`)
  if (pagesUrl) console.log(`Pages 前端：${pagesUrl}`)
  console.log('进入页面后，用任意邮箱 + 中国大陆手机号格式登录；AI 能力需要在“智能体配置”里配置 AgentRun。')
} catch (err) {
  console.error(`\n[error] ${err.message}`)
  process.exitCode = 1
} finally {
  rl.close()
}
