import { NextResponse } from 'next/server';
import { run as safeRun, runAllowFail, tmux } from '@/lib/safe-exec';

// Run `ps aux` once via argv (no shell) and return its output lines. Swallows
// errors and returns [] on failure, preserving the old error-tolerant behavior.
function psAuxLines(): string[] {
  try {
    const out = runAllowFail('ps', ['aux'], { timeout: 5000 }).stdout.trim();
    return out ? out.split('\n') : [];
  } catch {
    return [];
  }
}

// `ps -o lstart= -p <pid>` via argv. Returns '' on any failure (mirrors old
// `2>/dev/null` + catch).
function psStartedAt(pid: string): string {
  if (!pid) return '';
  try {
    return runAllowFail('ps', ['-o', 'lstart=', '-p', pid], { timeout: 5000 }).stdout.trim();
  } catch {
    return '';
  }
}

// Scope every ps grep to THIS instance — standalones spawned by init.ts
// (or forge-server.mjs) carry `--forge-port=<webPort>` so dev-test on
// port 4000 doesn't surface the production Forge processes from 8403
// and vice versa.
const INSTANCE_TAG = `--forge-port=${process.env.PORT || 8403}`;

function countProcess(pattern: string): { count: number; pid: string; startedAt: string } {
  // Filter the `ps aux` lines in JS: match the binary pattern AND the per-instance
  // tag, and exclude the grep processes themselves (the old `grep -v grep`).
  // The original shell built `grep '<pattern>'` (regex) and `grep -F -- '<tag>'`
  // (fixed-string), so the pattern is a RegExp and the tag is a literal substring.
  const lines = psAuxLines().filter(
    (l) => new RegExp(pattern).test(l) && l.includes(INSTANCE_TAG) && !/grep/.test(l)
  );
  const first = lines[0] || '';
  const pid = first ? first.split(/\s+/)[1] || '' : '';
  const count = lines.length;
  const startedAt = pid ? psStartedAt(pid) : '';
  return { count, pid, startedAt };
}

// Processes that don't carry --forge-port in their command line:
//   - next-server: bare "next-server (vX.Y.Z)" — we ARE next-server when
//     this handler runs, so reporting it as down would always be a lie.
//   - cloudflared: third-party binary, no tag.
// For these, fall back to a tag-less ps grep so the panel reflects reality.
function countProcessUntagged(pattern: string): { count: number; pid: string; startedAt: string } {
  const lines = psAuxLines().filter((l) => new RegExp(pattern).test(l) && !/grep/.test(l));
  const first = lines[0] || '';
  const pid = first ? first.split(/\s+/)[1] || '' : '';
  const count = lines.length;
  const startedAt = pid ? psStartedAt(pid) : '';
  return { count, pid, startedAt };
}

export async function GET() {
  // next-server: we ARE next-server when this runs — report self.
  const nextjs = { count: 1, pid: String(process.pid), startedAt: '' };
  const terminal = countProcess('terminal-standalone');
  const telegram = countProcess('telegram-standalone');
  const workspace = countProcess('workspace-standalone');
  const chat = countProcess('chat-standalone');
  const browserBridge = countProcess('browser-bridge-standalone');
  const memoryWorker = countProcess('memory-standalone');
  // cloudflared has no --forge-port tag — fall back to global grep.
  const tunnel = countProcessUntagged('cloudflared tunnel');

  // Chat backend health (port 8408 — process can be alive but crashed)
  let chatStatus: { running: boolean; sessions: number; port: number } = {
    running: false,
    sessions: 0,
    port: Number(process.env.CHAT_PORT) || 8408,
  };
  try {
    const chatRes = runAllowFail('curl', ['-s', `http://127.0.0.1:${chatStatus.port}/api/status`], { timeout: 5000 }).stdout.trim();
    if (chatRes) {
      const data = JSON.parse(chatRes);
      chatStatus.running = true;
      chatStatus.sessions = data.active_sse_sessions ?? 0;
    }
  } catch {}

  // MCP Server (runs inside workspace process, check /health endpoint)
  let mcpStatus = { running: false, sessions: 0 };
  try {
    const mcpPort = Number(process.env.MCP_PORT) || 8406;
    const mcpRes = runAllowFail('curl', ['-s', `http://localhost:${mcpPort}/health`], { timeout: 5000 }).stdout.trim();
    if (mcpRes.includes('"ok":true')) {
      const data = JSON.parse(mcpRes);
      mcpStatus = { running: true, sessions: data.sessions || 0 };
    }
  } catch {}

  // Tunnel URL
  let tunnelUrl = '';
  try {
    const { readFileSync } = require('fs');
    const { join } = require('path');
    const { getDataDir: _gdd } = require('@/lib/dirs');
    const state = JSON.parse(readFileSync(join(_gdd(), 'tunnel-state.json'), 'utf-8'));
    tunnelUrl = state.url || '';
  } catch {}

  // tmux sessions
  let sessions: { name: string; created: string; attached: boolean; windows: number }[] = [];
  try {
    const out = tmux(['list-sessions', '-F', '#{session_name}||#{session_created}||#{session_attached}||#{session_windows}']).trim();
    sessions = out.split('\n').filter(l => l.startsWith('mw-')).map(line => {
      const [name, created, attached, windows] = line.split('||');
      return { name, created: new Date(Number(created) * 1000).toISOString(), attached: attached !== '0', windows: Number(windows) || 1 };
    });
  } catch {}

  // System info
  let uptime = '';
  try {
    uptime = safeRun('uptime', [], { timeout: 5000 }).trim();
  } catch {}
  // Current process RSS (the old `ps -o rss= -p $$` reported the subshell's RSS;
  // with no shell we report this Next.js process's RSS via its real pid).
  try {
    runAllowFail('ps', ['-o', 'rss=', '-p', String(process.pid)], { timeout: 5000 });
  } catch {}

  return NextResponse.json({
    processes: {
      nextjs: { running: nextjs.count > 0, pid: nextjs.pid, startedAt: nextjs.startedAt },
      terminal: { running: terminal.count > 0, pid: terminal.pid, startedAt: terminal.startedAt },
      telegram: { running: telegram.count > 0, pid: telegram.pid, startedAt: telegram.startedAt },
      workspace: { running: workspace.count > 0, pid: workspace.pid, startedAt: workspace.startedAt },
      browserBridge: { running: browserBridge.count > 0, pid: browserBridge.pid, startedAt: browserBridge.startedAt },
      memory: { running: memoryWorker.count > 0, pid: memoryWorker.pid, startedAt: memoryWorker.startedAt },
      chat: {
        running: chatStatus.running,
        pid: chat.pid,
        startedAt: chat.startedAt,
        port: chatStatus.port,
        sessions: chatStatus.sessions,
        processAlive: chat.count > 0,
      },
      tunnel: { running: tunnel.count > 0, pid: tunnel.pid, url: tunnelUrl, startedAt: tunnel.startedAt },
      mcp: { running: mcpStatus.running, port: 8406, sessions: mcpStatus.sessions },
    },
    sessions,
    uptime: uptime.replace(/.*up\s+/, '').replace(/,\s+\d+ user.*/, '').trim(),
  });
}
