const { spawn, exec } = require("child_process");
const path = require("path");
const fs = require("fs");
const os = require("os");
const { addDNSEntry, removeDNSEntry, checkDNSEntry } = require("./dns/dnsConfig");

const IS_WIN = process.platform === "win32";
const { generateCert } = require("./cert/generate");
const { installCert } = require("./cert/install");

// Store server process
let serverProcess = null;
let serverPid = null;
// Persist across Next.js hot reloads
function getCachedPassword() { return globalThis.__mitmSudoPassword || null; }
function setCachedPassword(pwd) { globalThis.__mitmSudoPassword = pwd; }

// server.js is in same directory as this file
const PID_FILE = path.join(os.homedir(), ".9router", "mitm", ".mitm.pid");

// Check if a PID is alive
function isProcessAlive(pid) {
  try {
    process.kill(pid, 0);
    return true;
  } catch {
    return false;
  }
}

// Cross-platform process kill
function killProcess(pid, force = false) {
  if (IS_WIN) {
    const flag = force ? "/F " : "";
    exec(`taskkill ${flag}/PID ${pid}`, () => {});
  } else {
    process.kill(pid, force ? "SIGKILL" : "SIGTERM");
  }
}

/**
 * Get MITM status
 */
async function getMitmStatus() {
  // Check in-memory process first, then fallback to PID file
  let running = serverProcess !== null && !serverProcess.killed;
  let pid = serverPid;

  if (!running) {
    try {
      if (fs.existsSync(PID_FILE)) {
        const savedPid = parseInt(fs.readFileSync(PID_FILE, "utf-8").trim(), 10);
        if (savedPid && isProcessAlive(savedPid)) {
          running = true;
          pid = savedPid;
        } else {
          // Stale PID file, clean up
          fs.unlinkSync(PID_FILE);
        }
      }
    } catch {
      // Ignore
    }
  }

  // Check DNS configuration (cross-platform via dnsConfig)
  const dnsConfigured = checkDNSEntry();

  // Check cert
  const certDir = path.join(os.homedir(), ".9router", "mitm");
  const certExists = fs.existsSync(path.join(certDir, "server.crt"));

  return { running, pid, dnsConfigured, certExists };
}

/**
 * Start MITM proxy
 * @param {string} apiKey - 9Router API key
 * @param {string} sudoPassword - Sudo password for DNS/cert operations
 */
async function startMitm(apiKey, sudoPassword) {
  // Check if already running
  if (serverProcess && !serverProcess.killed) {
    throw new Error("MITM proxy is already running");
  }
  
  // 1. Generate SSL certificate if not exists
  const certPath = path.join(os.homedir(), ".9router", "mitm", "server.crt");
  if (!fs.existsSync(certPath)) {
    console.log("Generating SSL certificate...");
    await generateCert();
  }
  
  // 2. Install certificate to system keychain
  await installCert(sudoPassword, certPath);
  
  // 3. Add DNS entry
  console.log("Adding DNS entry...");
  await addDNSEntry(sudoPassword);
  
  // 4. Start MITM server (port 443 requires elevated privileges)
  console.log("Starting MITM server...");
  const serverPath = path.join(process.cwd(), "src/mitm/server.js");

  if (IS_WIN) {
    // Windows: spawn via powershell elevated to bind port 443
    const nodePath = process.execPath;
    const envArgs = `$env:ROUTER_API_KEY='${apiKey}'; $env:NODE_ENV='production'; & '${nodePath}' '${serverPath}'`;
    serverProcess = spawn("powershell", [
      "-Command",
      `Start-Process powershell -ArgumentList '-NoProfile','-Command','${envArgs.replace(/'/g, "''")}' -Verb RunAs -PassThru`
    ], {
      detached: false,
      stdio: ["ignore", "pipe", "pipe"]
    });
  } else {
    serverProcess = spawn("node", [serverPath], {
      env: {
        ...process.env,
        ROUTER_API_KEY: apiKey,
        NODE_ENV: "production"
      },
      detached: false,
      stdio: ["ignore", "pipe", "pipe"]
    });
  }
  
  serverPid = serverProcess.pid;
  
  // Save PID to file
  fs.writeFileSync(PID_FILE, String(serverPid));
  
  // Log server output
  serverProcess.stdout.on("data", (data) => {
    console.log(`[MITM Server] ${data.toString().trim()}`);
  });
  
  serverProcess.stderr.on("data", (data) => {
    console.error(`[MITM Server Error] ${data.toString().trim()}`);
  });
  
  serverProcess.on("exit", (code) => {
    console.log(`MITM server exited with code ${code}`);
    serverProcess = null;
    serverPid = null;
    
    // Remove PID file
    try {
      fs.unlinkSync(PID_FILE);
    } catch (error) {
      // Ignore
    }
  });
  
  // Wait and verify server actually started
  const started = await new Promise((resolve) => {
    let resolved = false;
    const timeout = setTimeout(() => {
      if (!resolved) { resolved = true; resolve(true); }
    }, 2000);

    serverProcess.on("exit", (code) => {
      clearTimeout(timeout);
      if (!resolved) { resolved = true; resolve(false); }
    });

    // Check stderr for error messages
    serverProcess.stderr.on("data", (data) => {
      const msg = data.toString().trim();
      if (msg.includes("Port") && msg.includes("already in use")) {
        clearTimeout(timeout);
        if (!resolved) { resolved = true; resolve(false); }
      }
    });
  });

  if (!started) {
    throw new Error("MITM server failed to start (port 443 may be in use)");
  }

  return {
    running: true,
    pid: serverPid
  };
}

/**
 * Stop MITM proxy
 * @param {string} sudoPassword - Sudo password for DNS cleanup
 */
async function stopMitm(sudoPassword) {
  // 1. Kill server process (in-memory or from PID file)
  const proc = serverProcess;
  if (proc && !proc.killed) {
    console.log("Stopping MITM server...");
    killProcess(proc.pid, false);
    await new Promise(resolve => setTimeout(resolve, 1000));
    if (isProcessAlive(proc.pid)) {
      killProcess(proc.pid, true);
    }
    serverProcess = null;
    serverPid = null;
  } else {
    // Fallback: kill by PID file
    try {
      if (fs.existsSync(PID_FILE)) {
        const savedPid = parseInt(fs.readFileSync(PID_FILE, "utf-8").trim(), 10);
        if (savedPid && isProcessAlive(savedPid)) {
          console.log(`Killing MITM server (PID: ${savedPid})...`);
          killProcess(savedPid, false);
          await new Promise(resolve => setTimeout(resolve, 1000));
          if (isProcessAlive(savedPid)) {
            killProcess(savedPid, true);
          }
        }
      }
    } catch {
      // Ignore
    }
    serverProcess = null;
    serverPid = null;
  }
  
  // 2. Remove DNS entry
  console.log("Removing DNS entry...");
  await removeDNSEntry(sudoPassword);
  
  // 3. Remove PID file
  try {
    fs.unlinkSync(PID_FILE);
  } catch (error) {
    // Ignore
  }
  
  return {
    running: false,
    pid: null
  };
}

module.exports = {
  getMitmStatus,
  startMitm,
  stopMitm,
  getCachedPassword,
  setCachedPassword
};
