/**
 * 本地服务管理器
 * 在 Electron 主进程中自动启动和管理 Node 服务端与 Python 发布服务
 *
 * 路径策略：
 *   开发模式：直接引用源码目录 (project-root/server/)
 *   打包模式：引用 extraResources 打包进来的 resources/server/
 */
const { spawn, execSync } = require('child_process');
const path = require('path');
const fs = require('fs');
const http = require('http');
const { app } = require('electron');

interface ServiceProcess {
  process: ReturnType<typeof spawn> | null;
  name: string;
  port: number;
  ready: boolean;
}

const NODE_PORT = 3000;
const PYTHON_PORT = 5005;

const services: { node: ServiceProcess; python: ServiceProcess } = {
  node: { process: null, name: 'Node Server', port: NODE_PORT, ready: false },
  python: { process: null, name: 'Python Service', port: PYTHON_PORT, ready: false },
};

// ==================== 文件日志 ====================
let logFilePath = '';

function getLogFilePath(): string {
  if (!logFilePath) {
    try {
      const logDir = app.getPath('userData');
      logFilePath = path.join(logDir, 'local-services.log');
    } catch {
      logFilePath = path.join(process.cwd(), 'local-services.log');
    }
  }
  return logFilePath;
}

function log(level: string, ...args: unknown[]): void {
  const ts = new Date().toISOString();
  const msg = `[${ts}] [${level}] ${args.map(a => typeof a === 'string' ? a : JSON.stringify(a)).join(' ')}`;

  if (level === 'ERROR') {
    console.error(msg);
  } else {
    console.log(msg);
  }

  try {
    fs.appendFileSync(getLogFilePath(), msg + '\n', 'utf-8');
  } catch { /* ignore */ }
}

// ==================== 核心工具 ====================

function isPackaged(): boolean {
  return app.isPackaged;
}

function getServerDir(): string {
  if (isPackaged()) {
    return path.join(process.resourcesPath, 'server');
  }
  return path.resolve(__dirname, '..', '..', 'server');
}

function getPythonDir(): string {
  return path.join(getServerDir(), 'python');
}

function findPython(): string {
  const pythonDir = getPythonDir();
  const isWin = process.platform === 'win32';

  const venvPython = isWin
    ? path.join(pythonDir, 'venv', 'Scripts', 'python.exe')
    : path.join(pythonDir, 'venv', 'bin', 'python');

  if (fs.existsSync(venvPython)) return venvPython;

  return isWin ? 'python' : 'python3';
}

function findNodeRunner(): { cmd: string; args: string[]; useElectronAsNode?: boolean } {
  const serverDir = getServerDir();
  const distEntry = path.join(serverDir, 'dist', 'app.js');
  const srcEntry = path.join(serverDir, 'src', 'app.ts');
  const isWin = process.platform === 'win32';

  if (isPackaged()) {
    return { cmd: process.execPath, args: [distEntry], useElectronAsNode: true };
  }

  if (fs.existsSync(distEntry)) {
    return { cmd: 'node', args: [distEntry] };
  }

  const tsxBin = path.join(serverDir, 'node_modules', '.bin', isWin ? 'tsx.cmd' : 'tsx');
  if (fs.existsSync(tsxBin)) {
    return { cmd: tsxBin, args: [srcEntry] };
  }

  return { cmd: isWin ? 'npx.cmd' : 'npx', args: ['tsx', srcEntry] };
}

function checkPort(port: number): Promise<boolean> {
  return new Promise((resolve) => {
    const req = http.get(`http://127.0.0.1:${port}/`, { timeout: 2000 }, (res: any) => {
      res.resume();
      resolve(true);
    });
    req.on('error', () => resolve(false));
    req.on('timeout', () => { req.destroy(); resolve(false); });
  });
}

async function waitForService(port: number, timeoutMs = 30000): Promise<boolean> {
  const start = Date.now();
  while (Date.now() - start < timeoutMs) {
    if (await checkPort(port)) return true;
    await new Promise((r) => setTimeout(r, 1000));
  }
  return false;
}

function getEnvFilePath(): string {
  return path.join(getServerDir(), '.env');
}

function getPlaywrightBrowsersPath(): string | null {
  if (isPackaged()) {
    const bundled = path.join(process.resourcesPath, 'playwright');
    if (fs.existsSync(bundled)) return bundled;
  }
  return null;
}

// ==================== 诊断：列出 resources 目录结构 ====================

function dumpResourcesDir(): void {
  if (!isPackaged()) return;
  try {
    const resDir = process.resourcesPath;
    log('INFO', `resources 目录: ${resDir}`);

    const topEntries = fs.readdirSync(resDir);
    log('INFO', `resources/ 内容: ${topEntries.join(', ')}`);

    const serverDir = path.join(resDir, 'server');
    if (fs.existsSync(serverDir)) {
      const serverEntries = fs.readdirSync(serverDir);
      log('INFO', `resources/server/ 内容: ${serverEntries.join(', ')}`);

      const distDir = path.join(serverDir, 'dist');
      if (fs.existsSync(distDir)) {
        const distEntries = fs.readdirSync(distDir).slice(0, 20);
        log('INFO', `resources/server/dist/ 内容(前20): ${distEntries.join(', ')}`);
      } else {
        log('ERROR', 'resources/server/dist/ 不存在！');
      }

      const pyDir = path.join(serverDir, 'python');
      if (fs.existsSync(pyDir)) {
        const pyEntries = fs.readdirSync(pyDir);
        log('INFO', `resources/server/python/ 内容: ${pyEntries.join(', ')}`);
      } else {
        log('ERROR', 'resources/server/python/ 不存在！');
      }

      const nmDir = path.join(serverDir, 'node_modules');
      log('INFO', `resources/server/node_modules/ 存在: ${fs.existsSync(nmDir)}`);
    } else {
      log('ERROR', 'resources/server/ 目录不存在！');
    }
  } catch (e: any) {
    log('ERROR', `诊断目录结构失败: ${e.message}`);
  }
}

// ==================== 启动服务 ====================

function startNodeServer(): void {
  const serverDir = getServerDir();
  if (!fs.existsSync(serverDir)) {
    log('ERROR', `server 目录不存在: ${serverDir}`);
    return;
  }

  const { cmd, args, useElectronAsNode } = findNodeRunner();
  log('INFO', `启动 Node 服务: ${cmd} ${args.join(' ')}`);
  log('INFO', `工作目录: ${serverDir}`);

  const distApp = path.join(serverDir, 'dist', 'app.js');
  log('INFO', `dist/app.js 存在: ${fs.existsSync(distApp)}`);

  const envFile = getEnvFilePath();
  log('INFO', `.env 存在: ${fs.existsSync(envFile)}`);

  const env: Record<string, string> = {
    ...process.env as Record<string, string>,
    PORT: String(NODE_PORT),
    HOST: '127.0.0.1',
    PYTHON_PUBLISH_SERVICE_URL: `http://127.0.0.1:${PYTHON_PORT}`,
    USE_REDIS_QUEUE: 'false',
  };

  if (useElectronAsNode) {
    env.ELECTRON_RUN_AS_NODE = '1';
    log('INFO', 'ELECTRON_RUN_AS_NODE=1');
  }

  const pwPath = getPlaywrightBrowsersPath();
  if (pwPath) {
    env.PLAYWRIGHT_BROWSERS_PATH = pwPath;
  }

  try {
    const child = spawn(cmd, args, {
      cwd: serverDir,
      env,
      stdio: ['ignore', 'pipe', 'pipe'],
      shell: false,
      windowsHide: true,
    });

    log('INFO', `Node 进程已 spawn, PID: ${child.pid}`);

    child.on('error', (err: Error) => {
      log('ERROR', `Node spawn error: ${err.message}`);
    });

    child.stdout?.on('data', (data: Buffer) => {
      const msg = data.toString().trim();
      if (msg) log('NODE', msg);
      if (msg.includes('listening') || msg.includes('started') || msg.includes('Server running')) {
        services.node.ready = true;
      }
    });

    child.stderr?.on('data', (data: Buffer) => {
      const msg = data.toString().trim();
      if (msg) log('NODE-ERR', msg);
    });

    child.on('exit', (code: number | null, signal: string | null) => {
      log('INFO', `Node 服务退出, code=${code}, signal=${signal}`);
      services.node.process = null;
      services.node.ready = false;
    });

    services.node.process = child;
  } catch (e: any) {
    log('ERROR', `Node spawn 异常: ${e.message}`);
  }
}

function startPythonService(): void {
  const pythonDir = getPythonDir();
  const appPy = path.join(pythonDir, 'app.py');
  if (!fs.existsSync(appPy)) {
    log('ERROR', `Python 入口不存在: ${appPy}`);
    return;
  }

  const pythonCmd = findPython();
  const args = [appPy, '--port', String(PYTHON_PORT), '--host', '127.0.0.1'];
  log('INFO', `启动 Python 服务: ${pythonCmd} ${args.join(' ')}`);
  log('INFO', `Python 可执行文件存在: ${fs.existsSync(pythonCmd)}`);
  log('INFO', `工作目录: ${pythonDir}`);

  const env: Record<string, string> = {
    ...process.env as Record<string, string>,
    PYTHONUNBUFFERED: '1',
    PYTHONIOENCODING: 'utf-8',
  };

  const pwPath = getPlaywrightBrowsersPath();
  if (pwPath) {
    env.PLAYWRIGHT_BROWSERS_PATH = pwPath;
    log('INFO', `Playwright 浏览器路径: ${pwPath}`);
  }

  try {
    const child = spawn(pythonCmd, args, {
      cwd: pythonDir,
      env,
      stdio: ['ignore', 'pipe', 'pipe'],
      shell: false,
      windowsHide: true,
    });

    log('INFO', `Python 进程已 spawn, PID: ${child.pid}`);

    child.on('error', (err: Error) => {
      log('ERROR', `Python spawn error: ${err.message}`);
    });

    child.stdout?.on('data', (data: Buffer) => {
      const msg = data.toString().trim();
      if (msg) log('PYTHON', msg);
      if (msg.includes('Running on') || msg.includes('启动服务')) {
        services.python.ready = true;
      }
    });

    child.stderr?.on('data', (data: Buffer) => {
      const msg = data.toString().trim();
      if (msg) log('PYTHON-ERR', msg);
    });

    child.on('exit', (code: number | null, signal: string | null) => {
      log('INFO', `Python 服务退出, code=${code}, signal=${signal}`);
      services.python.process = null;
      services.python.ready = false;
    });

    services.python.process = child;
  } catch (e: any) {
    log('ERROR', `Python spawn 异常: ${e.message}`);
  }
}

// ==================== 导出 API ====================

export async function startLocalServices(): Promise<{ nodeOk: boolean; pythonOk: boolean }> {
  // 清空旧日志
  try { fs.writeFileSync(getLogFilePath(), '', 'utf-8'); } catch { /* ignore */ }

  log('INFO', '========== 启动本地服务 ==========');
  log('INFO', `模式: ${isPackaged() ? '打包' : '开发'}`);
  log('INFO', `execPath: ${process.execPath}`);
  log('INFO', `resourcesPath: ${process.resourcesPath}`);
  log('INFO', `Server 目录: ${getServerDir()}`);
  log('INFO', `Python 目录: ${getPythonDir()}`);
  log('INFO', `日志文件: ${getLogFilePath()}`);

  dumpResourcesDir();

  const [nodeAlive, pythonAlive] = await Promise.all([
    checkPort(NODE_PORT),
    checkPort(PYTHON_PORT),
  ]);

  if (nodeAlive) {
    log('INFO', `Node 服务已在 ${NODE_PORT} 端口运行`);
    services.node.ready = true;
  } else {
    startNodeServer();
  }

  if (pythonAlive) {
    log('INFO', `Python 服务已在 ${PYTHON_PORT} 端口运行`);
    services.python.ready = true;
  } else {
    startPythonService();
  }

  const [nodeOk, pythonOk] = await Promise.all([
    services.node.ready ? Promise.resolve(true) : waitForService(NODE_PORT, 30000),
    services.python.ready ? Promise.resolve(true) : waitForService(PYTHON_PORT, 30000),
  ]);

  services.node.ready = nodeOk;
  services.python.ready = pythonOk;

  log('INFO', `结果 — Node: ${nodeOk ? '✓ 就绪' : '✗ 未就绪'}, Python: ${pythonOk ? '✓ 就绪' : '✗ 未就绪'}`);
  return { nodeOk, pythonOk };
}

export function stopLocalServices(): void {
  log('INFO', '正在停止本地服务...');

  for (const key of ['node', 'python'] as const) {
    const svc = services[key];
    if (svc.process) {
      log('INFO', `停止 ${svc.name} (PID: ${svc.process.pid})`);
      try {
        if (process.platform === 'win32') {
          spawn('taskkill', ['/pid', String(svc.process.pid), '/f', '/t'], { windowsHide: true });
        } else {
          svc.process.kill('SIGTERM');
        }
      } catch (e: any) {
        log('ERROR', `停止 ${svc.name} 失败: ${e.message}`);
      }
      svc.process = null;
      svc.ready = false;
    }
  }
}

export function getServiceStatus(): { node: { ready: boolean; port: number }; python: { ready: boolean; port: number } } {
  return {
    node: { ready: services.node.ready, port: NODE_PORT },
    python: { ready: services.python.ready, port: PYTHON_PORT },
  };
}

export function getLogPath(): string {
  return getLogFilePath();
}

export const LOCAL_NODE_URL = `http://127.0.0.1:${NODE_PORT}`;
export const LOCAL_PYTHON_URL = `http://127.0.0.1:${PYTHON_PORT}`;
