#!/usr/bin/env -S deno run --allow-read=/tmp --allow-write=/tmp --allow-env --allow-run=free,nproc,swapon,sudo,chmod,cp,cat

/**
 * Swap configuration tool for development machines
 * Helps optimize swap settings to prevent system crashes during heavy testing
 */

import { parse } from "https://deno.land/std@0.220.0/flags/mod.ts";
import {
  blue,
  bold,
  green,
  red,
  yellow,
} from "https://deno.land/std@0.220.0/fmt/colors.ts";

interface MemoryInfo {
  totalRamGB: number;
  availableRamMB: number;
  swapTotalMB: number;
  swapUsedMB: number;
  swapPercent: number;
}

interface SwapSettings {
  swappiness: number;
  vfsCachePressure: number;
}

async function runCommand(cmd: string[]): Promise<string> {
  const process = new Deno.Command(cmd[0], {
    args: cmd.slice(1),
    stdout: "piped",
    stderr: "piped",
  });

  const { stdout, stderr, success } = await process.output();

  if (!success) {
    throw new Error(`Command failed: ${new TextDecoder().decode(stderr)}`);
  }

  return new TextDecoder().decode(stdout).trim();
}

async function getMemoryInfo(): Promise<MemoryInfo> {
  const memInfo = await runCommand(["free", "-m"]);
  const lines = memInfo.split("\n");

  const memLine = lines.find((l) => l.startsWith("Mem:"))!;
  const swapLine = lines.find((l) => l.startsWith("Swap:"))!;

  const memParts = memLine.split(/\s+/);
  const swapParts = swapLine.split(/\s+/);

  const totalRamMB = parseInt(memParts[1]);
  const availableRamMB = parseInt(memParts[6] || memParts[3]); // Handle different free versions
  const swapTotalMB = parseInt(swapParts[1]);
  const swapUsedMB = parseInt(swapParts[2]);

  return {
    totalRamGB: Math.round(totalRamMB / 1024),
    availableRamMB,
    swapTotalMB,
    swapUsedMB,
    swapPercent: swapTotalMB > 0
      ? Math.round((swapUsedMB / swapTotalMB) * 100)
      : 0,
  };
}

async function getCurrentSwapSettings(): Promise<SwapSettings> {
  const swappiness = parseInt(
    await runCommand(["cat", "/proc/sys/vm/swappiness"]),
  );
  const vfsCachePressure = parseInt(
    await runCommand(["cat", "/proc/sys/vm/vfs_cache_pressure"]),
  );

  return { swappiness, vfsCachePressure };
}

async function showSystemInfo() {
  console.log(blue(bold("\n=== System Information ===")));

  const memInfo = await getMemoryInfo();
  const cpuInfo = await runCommand(["nproc"]);

  console.log(`Total RAM: ${green(memInfo.totalRamGB + "GB")}`);
  console.log(`CPU Cores: ${green(cpuInfo)}`);
  console.log(`Current Swap: ${yellow(memInfo.swapTotalMB + "MB")}`);
  console.log(`Swap Used: ${memInfo.swapUsedMB}MB (${memInfo.swapPercent}%)`);

  if (memInfo.swapPercent > 80) {
    console.log(red("\n⚠️  Warning: Swap usage is very high!"));
  }
}

async function showSwapConfiguration() {
  console.log(blue(bold("\n=== Current Swap Configuration ===")));

  const settings = await getCurrentSwapSettings();
  console.log(
    `Swappiness: ${settings.swappiness} (lower = less aggressive swapping)`,
  );
  console.log(
    `VFS Cache Pressure: ${settings.vfsCachePressure} (lower = prefer caching)`,
  );

  console.log(blue(bold("\n=== Swap Devices ===")));
  const swapDevices = await runCommand(["swapon", "--show"]);
  console.log(swapDevices || "No swap devices found");
}

async function applyOptimalSettings() {
  console.log(yellow(bold("\n=== Applying Optimal Swap Settings ===")));

  const settings = await getCurrentSwapSettings();
  const optimalSwappiness = 10;
  const optimalCachePressure = 50;

  console.log(`\nCurrent settings:`);
  console.log(`  Swappiness: ${settings.swappiness}`);
  console.log(`  VFS Cache Pressure: ${settings.vfsCachePressure}`);

  console.log(`\nOptimal settings for development:`);
  console.log(`  Swappiness: ${optimalSwappiness}`);
  console.log(`  VFS Cache Pressure: ${optimalCachePressure}`);

  // Apply settings
  try {
    await runCommand(["sudo", "sysctl", `vm.swappiness=${optimalSwappiness}`]);
    await runCommand([
      "sudo",
      "sysctl",
      `vm.vfs_cache_pressure=${optimalCachePressure}`,
    ]);

    // Create persistent configuration
    const sysctlConfig = `# Swap optimization for development machines
# Generated by paiml-mcp-agent-toolkit

# Reduce swappiness (default is 60)
vm.swappiness=${optimalSwappiness}

# Reduce cache pressure (default is 100)
vm.vfs_cache_pressure=${optimalCachePressure}

# Additional optimizations
vm.max_map_count=262144
vm.dirty_ratio=15
vm.dirty_background_ratio=5
`;

    // Create intermediate file for safe writing
    const tempFile = await Deno.makeTempFile();
    await Deno.writeTextFile(tempFile, sysctlConfig);

    // Move intermediate file to system location
    await runCommand([
      "sudo",
      "cp",
      tempFile,
      "/etc/sysctl.d/99-swap-optimization.conf",
    ]);
    await Deno.remove(tempFile);

    // Reload settings
    await runCommand([
      "sudo",
      "sysctl",
      "-p",
      "/etc/sysctl.d/99-swap-optimization.conf",
    ]);

    console.log(green("\n✅ Optimal swap settings applied successfully!"));
  } catch (error) {
    console.error(
      red(
        `\n❌ Failed to apply settings: ${
          error instanceof Error ? error.message : String(error)
        }`,
      ),
    );
    console.log(
      "You may need to run this script with appropriate permissions.",
    );
  }
}

async function clearSwap() {
  console.log(yellow(bold("\n=== Clearing Swap ===")));

  const memInfo = await getMemoryInfo();

  console.log(`Swap used: ${memInfo.swapUsedMB}MB`);
  console.log(`RAM available: ${memInfo.availableRamMB}MB`);

  if (memInfo.availableRamMB < memInfo.swapUsedMB) {
    console.log(red("\n⚠️  Warning: Not enough RAM to safely clear swap!"));
    console.log("This operation may cause system instability.");

    const response = prompt("Continue anyway? (y/N): ");
    if (response?.toLowerCase() !== "y") {
      return;
    }
  }

  try {
    console.log("\nDisabling and re-enabling swap...");
    await runCommand(["sudo", "swapoff", "-a"]);
    await runCommand(["sudo", "swapon", "-a"]);
    console.log(green("✅ Swap cleared successfully!"));
  } catch (error) {
    console.error(
      red(
        `❌ Failed to clear swap: ${
          error instanceof Error ? error.message : String(error)
        }`,
      ),
    );
  }
}

async function createSwapMonitor() {
  console.log(yellow(bold("\n=== Creating Swap Monitor Service ===")));

  const monitorScript =
    `#!/usr/bin/env -S deno run --allow-read --allow-run --allow-sys

// Automatic swap monitor for paiml-mcp-agent-toolkit
// Monitors swap usage and clears when safe

const THRESHOLD_PERCENT = 80;
const CHECK_INTERVAL_MS = 300000; // 5 minutes

async function getSwapInfo() {
  const cmd = new Deno.Command("free", {
    args: ["-m"],
    stdout: "piped",
  });
  
  const output = await cmd.output();
  const text = new TextDecoder().decode(output.stdout);
  const swapLine = text.split("\\n").find(l => l.startsWith("Swap:"));
  
  if (!swapLine) return null;
  
  const parts = swapLine.split(/\\s+/);
  return {
    total: parseInt(parts[1]),
    used: parseInt(parts[2]),
    available: parseInt(parts[3]),
  };
}

async function checkAndClearSwap() {
  const swap = await getSwapInfo();
  if (!swap || swap.total === 0) return;
  
  const percent = Math.round((swap.used / swap.total) * 100);
  
  if (percent > THRESHOLD_PERCENT) {
    console.log(\`[\${new Date().toISOString()}] High swap usage: \${percent}% (\${swap.used}MB)\`);
    
    // Check if we have enough RAM
    const memCmd = new Deno.Command("free", {
      args: ["-m"],
      stdout: "piped",
    });
    
    const memOutput = await memCmd.output();
    const memText = new TextDecoder().decode(memOutput.stdout);
    const memLine = memText.split("\\n").find(l => l.startsWith("Mem:"));
    const memParts = memLine.split(/\\s+/);
    const memAvailable = parseInt(memParts[6] || memParts[3]);
    
    if (memAvailable > swap.used * 2) {
      console.log("Sufficient RAM available. Clearing swap...");
      
      try {
        const clearCmd = new Deno.Command("sudo", {
          args: ["sh", "-c", "swapoff -a && swapon -a"],
        });
        await clearCmd.output();
        console.log("Swap cleared successfully");
      } catch (error) {
        console.error("Failed to clear swap:", error);
      }
    }
  }
}

// Main loop
console.log("Starting swap monitor...");
while (true) {
  await checkAndClearSwap();
  await new Promise(resolve => setTimeout(resolve, CHECK_INTERVAL_MS));
}
`;

  const monitorPath = "/tmp/pmat-swap-monitor.ts";
  await Deno.writeTextFile(monitorPath, monitorScript);
  await runCommand(["chmod", "+x", monitorPath]);

  console.log(green(`✅ Swap monitor created at: ${monitorPath}`));
  console.log("\nTo run the monitor in background:");
  console.log(
    blue(`  nohup ${monitorPath} > /var/log/pmat-swap-monitor.log 2>&1 &`),
  );
}

async function interactiveMenu() {
  while (true) {
    console.log(blue(bold("\n=== Swap Configuration Menu ===")));
    console.log("1) Show system information");
    console.log("2) Show current swap configuration");
    console.log("3) Apply optimal swap settings");
    console.log("4) Clear swap now");
    console.log("5) Create swap monitor");
    console.log("6) Exit");

    const choice = prompt("\nSelect option (1-6): ");

    try {
      switch (choice) {
        case "1":
          await showSystemInfo();
          break;
        case "2":
          await showSwapConfiguration();
          break;
        case "3":
          await applyOptimalSettings();
          break;
        case "4":
          await clearSwap();
          break;
        case "5":
          await createSwapMonitor();
          break;
        case "6":
          console.log(green("\nGoodbye!"));
          Deno.exit(0);
          break;
        default:
          console.log(red("Invalid choice"));
      }
    } catch (error) {
      console.error(
        red(`Error: ${error instanceof Error ? error.message : String(error)}`),
      );
    }

    await new Promise((resolve) => setTimeout(resolve, 1000));
  }
}

// Main
const args = parse(Deno.args, {
  boolean: ["auto", "help"],
  alias: { h: "help" },
});

if (args.help) {
  console.log(`
${bold("Swap Configuration Tool")}

Helps optimize swap settings for development machines to prevent crashes during testing.

${bold("Usage:")}
  configure-swap.ts [options]

${bold("Options:")}
  --auto    Apply optimal settings automatically
  --help    Show this help message

${bold("Interactive mode:")}
  Run without options to use the interactive menu.
`);
  Deno.exit(0);
}

if (args.auto) {
  console.log(blue(bold("Running in automatic mode...")));
  await showSystemInfo();
  await applyOptimalSettings();
  await clearSwap();
} else {
  await interactiveMenu();
}
