#!/usr/bin/env node
/**
 * myhi 守护进程
 * 负责 fork/管理 server 子进程，处理升级和重启
 * daemon 本身不会被升级影响，保证服务连续性
 */
import { fork, execSync, spawn } from 'child_process';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
import { writeFileSync, readFileSync, mkdirSync, unlinkSync } from 'fs';
import { homedir } from 'os';

const __filename = fileURLToPath(import.meta.url);
const __dirname  = dirname(__filename);
const configDir  = join(homedir(), '.myhi');
const pidFile    = join(configDir, 'daemon.pid');
const serverEntry = join(__dirname, '../dist/index.js');

mkdirSync(configDir, { recursive: true });

// 单例守卫：如果已有活 daemon，先 SIGTERM 它再启动。否则双 daemon 会让各自的
// server child 因互读 server.pid 而互杀，触发"连续崩溃 5 次"熔断（pc41 1.3.51 升级踩到）
(function killOldDaemon() {
  let oldPid;
  try { oldPid = parseInt(readFileSync(pidFile, 'utf8').trim(), 10); } catch { return; }
  if (!oldPid || oldPid === process.pid) return;
  let alive = false;
  try { process.kill(oldPid, 0); alive = true; } catch {}
  if (!alive) return;
  process.stderr.write(`[daemon] 检测到已运行的 daemon (PID ${oldPid})，先停止...\n`);
  try {
    if (process.platform === 'win32') execSync(`taskkill /PID ${oldPid} /F /T`, { stdio: 'pipe', timeout: 5000 });
    else process.kill(oldPid, 'SIGTERM');
  } catch {}
  // 等最多 5 秒
  const deadline = Date.now() + 5000;
  while (Date.now() < deadline) {
    try { process.kill(oldPid, 0); } catch { return; }
    const end = Date.now() + 200;
    while (Date.now() < end) {} // busy-wait, 此处无事件循环可用
  }
  let stillAlive = false;
  try { process.kill(oldPid, 0); stillAlive = true; } catch {}
  if (stillAlive) {
    process.stderr.write(`[daemon] 无法停止旧 daemon (PID ${oldPid})，本进程退出避免冲突\n`);
    process.exit(1);
  }
})();

writeFileSync(pidFile, String(process.pid));

let child = null;
let restarting = false;
let stopping = false;
let crashCount = 0;
let lastCrash = 0;
const startTime = Date.now();

function log(msg) {
  const ts = new Date().toISOString().slice(11, 19);
  process.stderr.write(`[daemon ${ts}] ${msg}\n`);
}

function startServer() {
  log('启动 server 子进程...');
  child = fork(serverEntry, process.argv.slice(2), {
    stdio: ['ignore', 'inherit', 'inherit', 'ipc'],
    env: { ...process.env, MYHI_DAEMON: '1', MYHI_DAEMON_PID: String(process.pid) },
    windowsHide: true,
  });

  child.on('message', (msg) => {
    if (msg?.type === 'upgrade') doUpgrade();
    else if (msg?.type === 'restart') doRestart();
  });

  const thisChild = child;
  child.on('exit', (code, signal) => {
    if (child === thisChild) child = null; // 只清自己的引用
    if (restarting || stopping) return; // 主动重启或正在停止，不自动恢复

    // 防止崩溃循环：10秒内连续崩溃超过5次则退出
    const now = Date.now();
    if (now - lastCrash < 10000) crashCount++;
    else crashCount = 1;
    lastCrash = now;

    if (crashCount >= 5) {
      log(`server 连续崩溃 ${crashCount} 次，daemon 退出`);
      cleanup();
      process.exit(1);
    }

    log(`server 退出 (code=${code}, signal=${signal})，${1}秒后重启...`);
    setTimeout(startServer, 1000);
  });
}

function doRestart() {
  log('收到重启请求');
  restarting = true;
  if (child) {
    const oldChild = child;
    child = null; // 清引用，防止 startServer 里的 exit handler 再次触发
    // 等旧进程退出后启动新的
    const onExit = () => {
      restarting = false;
      setTimeout(startServer, 500);
    };
    oldChild.once('exit', onExit);
    oldChild.kill('SIGTERM');
    // 5秒后强制杀
    setTimeout(() => {
      try { oldChild.kill('SIGKILL'); } catch {}
    }, 5000);
  } else {
    restarting = false;
    startServer();
  }
}

async function doUpgrade() {
  log('收到升级请求，执行 npm install...');
  try {
    const cmd = process.platform === 'win32'
      ? 'npm.cmd install -g @wendongfly/myhi@latest'
      : 'npm install -g @wendongfly/myhi@latest 2>&1 || sudo npm install -g @wendongfly/myhi@latest 2>&1';
    const output = execSync(cmd, { timeout: 120000, encoding: 'utf8' });
    log('升级完成: ' + output.trim().split('\n').pop());
  } catch (e) {
    log('升级失败: ' + e.message);
    // 即使升级失败也重启，用现有代码继续运行
  }
  doRestart();
}

function cleanup() {
  try {
    const savedPid = parseInt(readFileSync(pidFile, 'utf8').trim(), 10);
    if (savedPid === process.pid) unlinkSync(pidFile);
  } catch {}
}

// 信号处理
process.on('SIGTERM', () => {
  log('收到 SIGTERM，停止...');
  stopping = true;
  if (child) child.kill('SIGTERM');
  cleanup();
  setTimeout(() => process.exit(0), 2000);
});

process.on('SIGINT', () => {
  log('收到 SIGINT，停止...');
  stopping = true;
  if (child) child.kill('SIGTERM');
  cleanup();
  setTimeout(() => process.exit(0), 2000);
});

// Windows: 通过 stdin 消息（无 SIGUSR2 支持）
if (process.platform === 'win32') {
  process.on('message', (msg) => {
    if (msg === 'upgrade') doUpgrade();
    else if (msg === 'restart') doRestart();
  });
}

log(`daemon 启动 (PID ${process.pid})`);
startServer();
