import { NextResponse } from 'next/server';
import { exec } from 'child_process';
import { promisify } from 'util';
import { readFile } from 'fs/promises';
import os from 'os';

const execAsync = promisify(exec);

const IS_MAC = process.platform === 'darwin';

interface ProcessInfo {
  pid: number;
  name: string;
  rss: number; // bytes
  pctMem: number;
  user: string;
}

interface VmStatBreakdown {
  active: number;
  wired: number;
  compressor: number;
  inactive: number;
  purgeable: number;
  speculative: number;
  free: number;
  pageSize: number;
}

async function getTopProcesses(limit = 20): Promise<ProcessInfo[]> {
  try {
    // ps with RSS in KB, sorted descending by RSS — works on both macOS and Linux
    const { stdout } = await execAsync(
      `ps -axo pid=,rss=,%mem=,user=,comm= | sort -k2 -rn | head -${limit}`,
      { timeout: 3000 }
    );
    return stdout
      .trim()
      .split('\n')
      .filter(Boolean)
      .map(line => {
        const parts = line.trim().split(/\s+/);
        const pid = parseInt(parts[0]);
        const rssKb = parseInt(parts[1]);
        const pctMem = parseFloat(parts[2]);
        const user = parts[3];
        // comm can have spaces/slashes — take everything after user
        const rawName = parts.slice(4).join(' ');
        // Extract just the process name from the full path
        const name = rawName.split('/').pop() || rawName;
        return {
          pid,
          name,
          rss: rssKb * 1024,
          pctMem,
          user,
        };
      })
      .filter(p => p.rss > 0);
  } catch {
    return [];
  }
}

async function getVmStatBreakdown(): Promise<VmStatBreakdown> {
  if (IS_MAC) {
    try {
      const { stdout } = await execAsync('vm_stat', { timeout: 2000 });
      const pageSizeMatch = stdout.match(/page size of (\d+) bytes/);
      const pageSize = pageSizeMatch ? parseInt(pageSizeMatch[1]) : 16384;
      const parse = (label: string): number => {
        const match = stdout.match(new RegExp(`${label}:\\s+(\\d+)`));
        return match ? parseInt(match[1]) * pageSize : 0;
      };
      return {
        active: parse('Pages active'),
        wired: parse('Pages wired down'),
        compressor: parse('Pages occupied by compressor'),
        inactive: parse('Pages inactive'),
        purgeable: parse('Pages purgeable'),
        speculative: parse('Pages speculative'),
        free: parse('Pages free'),
        pageSize,
      };
    } catch {
      // fall through to zeros
    }
  } else {
    // Linux / WSL2 — parse /proc/meminfo into vm_stat-compatible structure
    try {
      const raw = await readFile('/proc/meminfo', 'utf-8');
      const parse = (key: string): number => {
        const match = raw.match(new RegExp(`${key}:\\s+(\\d+)`));
        return match ? parseInt(match[1]) * 1024 : 0;
      };
      const free = parse('MemFree');
      const buffers = parse('Buffers');
      const cached = parse('Cached');
      const sReclaimable = parse('SReclaimable');
      const active = parse('Active');

      return {
        active,
        wired: buffers, // closest analogy
        compressor: parse('SwapCached'),
        inactive: parse('Inactive'),
        purgeable: sReclaimable,
        speculative: 0,
        free,
        pageSize: 4096,
      };
    } catch {
      // fall through to zeros
    }
  }

  return {
    active: 0,
    wired: 0,
    compressor: 0,
    inactive: 0,
    purgeable: 0,
    speculative: 0,
    free: 0,
    pageSize: IS_MAC ? 16384 : 4096,
  };
}

export async function GET() {
  const [processes, vmstat] = await Promise.all([getTopProcesses(25), getVmStatBreakdown()]);

  // Group by process name and sum RSS (e.g. multiple Chrome helpers)
  const grouped: Record<string, { rss: number; pctMem: number; count: number; pids: number[] }> =
    {};
  for (const p of processes) {
    const key = p.name;
    if (!grouped[key]) {
      grouped[key] = { rss: 0, pctMem: 0, count: 0, pids: [] };
    }
    grouped[key].rss += p.rss;
    grouped[key].pctMem += p.pctMem;
    grouped[key].count += 1;
    grouped[key].pids.push(p.pid);
  }

  const groupedProcesses = Object.entries(grouped)
    .map(([name, info]) => ({
      name,
      rss: info.rss,
      pctMem: Math.round(info.pctMem * 10) / 10,
      count: info.count,
      pids: info.pids,
    }))
    .sort((a, b) => b.rss - a.rss);

  return NextResponse.json({
    totalRam: os.totalmem(),
    vmstat,
    categories: {
      active: vmstat.active,
      wired: vmstat.wired,
      compressor: vmstat.compressor,
      inactive: vmstat.inactive,
      purgeable: vmstat.purgeable,
      speculative: vmstat.speculative,
      free: vmstat.free,
    },
    processes: groupedProcesses,
  });
}
