import fs from "fs";
import crypto from "crypto";
import { exec } from "child_process";
import {
  execFileText,
  execFileWithPassword,
  getErrorMessage,
  quotePowerShell,
  runElevatedPowerShell,
} from "../systemCommands.ts";

const IS_WIN = process.platform === "win32";
const IS_MAC = process.platform === "darwin";

const LINUX_CERT_NAME = "omniroute-mitm.crt";

interface LinuxCertConfig {
  dir: string;
  cmd: string;
}

const LINUX_CERT_PATHS: LinuxCertConfig[] = [
  // Debian / Ubuntu
  { dir: "/usr/local/share/ca-certificates", cmd: "update-ca-certificates" },
  // Arch Linux / CachyOS / Manjaro
  { dir: "/etc/ca-certificates/trust-source/anchors", cmd: "update-ca-trust" },
  // Fedora / RHEL / CentOS
  { dir: "/etc/pki/ca-trust/source/anchors", cmd: "update-ca-trust" },
  // openSUSE
  { dir: "/etc/pki/trust/anchors", cmd: "update-ca-certificates" },
];

function getLinuxCertConfig(): LinuxCertConfig {
  for (const config of LINUX_CERT_PATHS) {
    if (fs.existsSync(config.dir)) {
      return config;
    }
  }
  return LINUX_CERT_PATHS[0];
}

async function updateNssDatabases(
  certPath: string | null,
  action: "add" | "delete" = "add"
): Promise<void> {
  // Pass the runtime values via environment variables instead of string
  // interpolation. The shell receives them through its env and dereferences
  // with "$CERT_PATH" / "$CERT_NAME" / "$ACTION", so any shell metacharacters
  // they may contain stay inside the quoted argument — eliminating the
  // command-injection surface flagged by CodeQL js/shell-command-injection.
  const script = `
    set -u
    if ! command -v certutil &> /dev/null; then
      exit 0
    fi

    DIRS="$HOME/.pki/nssdb $HOME/snap/chromium/current/.pki/nssdb"

    if [ -d "$HOME/.mozilla/firefox" ]; then
      for profile in "$HOME"/.mozilla/firefox/*/; do
        if [ -f "\${profile}cert9.db" ] || [ -f "\${profile}cert8.db" ]; then
          DIRS="$DIRS $profile"
        fi
      done
    fi

    if [ -d "$HOME/snap/firefox/common/.mozilla/firefox" ]; then
      for profile in "$HOME"/snap/firefox/common/.mozilla/firefox/*/; do
        if [ -f "\${profile}cert9.db" ] || [ -f "\${profile}cert8.db" ]; then
          DIRS="$DIRS $profile"
        fi
      done
    fi

    for db in $DIRS; do
      if [ -d "$db" ]; then
        if [ "$ACTION" = "add" ]; then
          certutil -d sql:"$db" -A -t "C,," -n "$CERT_NAME" -i "$CERT_PATH" 2>/dev/null || \\
          certutil -d "$db" -A -t "C,," -n "$CERT_NAME" -i "$CERT_PATH" 2>/dev/null || true
        else
          certutil -d sql:"$db" -D -n "$CERT_NAME" 2>/dev/null || \\
          certutil -d "$db" -D -n "$CERT_NAME" 2>/dev/null || true
        fi
      fi
    done
  `;

  return new Promise((resolve) => {
    exec(
      script,
      {
        shell: "/bin/bash",
        env: {
          ...process.env,
          CERT_NAME: "OmniRoute MITM Root CA",
          CERT_PATH: certPath || "",
          ACTION: action,
        },
      },
      () => resolve()
    );
  });
}

// Get SHA1 fingerprint from cert file using Node.js crypto
function getCertFingerprint(certPath: string): string {
  const pem = fs.readFileSync(certPath, "utf-8");
  const der = Buffer.from(pem.replace(/-----[^-]+-----/g, "").replace(/\s/g, ""), "base64");
  const pairs = crypto.createHash("sha1").update(der).digest("hex").toUpperCase().match(/.{2}/g);
  if (!pairs) {
    throw new Error(`Unable to compute certificate fingerprint for ${certPath}`);
  }
  return pairs.join(":");
}

/**
 * Check if certificate is already installed in system store
 */
export async function checkCertInstalled(certPath: string): Promise<boolean> {
  if (IS_WIN) return checkCertInstalledWindows(certPath);
  if (IS_MAC) return checkCertInstalledMac(certPath);
  return checkCertInstalledLinux(certPath);
}

async function checkCertInstalledMac(certPath: string): Promise<boolean> {
  try {
    const fingerprint = getCertFingerprint(certPath);
    const output = await execFileText("security", [
      "find-certificate",
      "-a",
      "-Z",
      "/Library/Keychains/System.keychain",
    ]);
    return output.toUpperCase().includes(fingerprint);
  } catch {
    return false;
  }
}

async function checkCertInstalledLinux(certPath: string): Promise<boolean> {
  try {
    const config = getLinuxCertConfig();
    const destFile = `${config.dir}/${LINUX_CERT_NAME}`;
    if (!fs.existsSync(destFile)) return false;
    return getCertFingerprint(certPath) === getCertFingerprint(destFile);
  } catch {
    return false;
  }
}

async function checkCertInstalledWindows(_certPath: string): Promise<boolean> {
  try {
    await execFileText("certutil", ["-store", "Root", "daily-cloudcode-pa.googleapis.com"]);
    return true;
  } catch {
    return false;
  }
}

/**
 * Install SSL certificate to system trust store
 */
export async function installCert(sudoPassword: string, certPath: string): Promise<void> {
  if (!fs.existsSync(certPath)) {
    throw new Error(`Certificate file not found: ${certPath}`);
  }

  const isInstalled = await checkCertInstalled(certPath);
  if (isInstalled) {
    console.log("✅ Certificate already installed");
    return;
  }

  if (IS_WIN) {
    await installCertWindows(certPath);
  } else if (IS_MAC) {
    await installCertMac(sudoPassword, certPath);
  } else {
    await installCertLinux(sudoPassword, certPath);
  }
}

async function installCertMac(sudoPassword: string, certPath: string): Promise<void> {
  try {
    await execFileWithPassword(
      "sudo",
      [
        "-S",
        "security",
        "add-trusted-cert",
        "-d",
        "-r",
        "trustRoot",
        "-k",
        "/Library/Keychains/System.keychain",
        certPath,
      ],
      sudoPassword
    );
    console.log(`✅ Installed certificate to system keychain: ${certPath}`);
  } catch (error) {
    const message = getErrorMessage(error);
    const msg = message.includes("canceled")
      ? "User canceled authorization"
      : "Certificate install failed";
    throw new Error(msg);
  }
}

async function installCertLinux(sudoPassword: string, certPath: string): Promise<void> {
  try {
    const config = getLinuxCertConfig();
    const destFile = `${config.dir}/${LINUX_CERT_NAME}`;

    await execFileWithPassword("sudo", ["-S", "mkdir", "-p", config.dir], sudoPassword);
    await execFileWithPassword("sudo", ["-S", "cp", certPath, destFile], sudoPassword);
    await execFileWithPassword("sudo", ["-S", config.cmd], sudoPassword);

    await updateNssDatabases(certPath, "add");
  } catch (error) {
    const message = getErrorMessage(error);
    const msg = message.includes("canceled")
      ? "User canceled authorization"
      : "Certificate install failed";
    throw new Error(msg);
  }
}

// SECURITY-AUDITOR-NOTE: This function and the surrounding install/uninstall
// pair appear in Socket.dev finding `77484.js` (AI-detected potential malware).
// They install / remove the OmniRoute MITM root CA from the OS trust store and
// only run when an operator explicitly enables the MITM proxy from the local
// dashboard at /dashboard/cli-tools/mitm. The privileged commands invoked
// here — `certutil -addstore Root`, `security add-trusted-cert`,
// `update-ca-certificates`, `update-ca-trust` — are the platform-standard
// CA-install paths used by mitmproxy, Charles, Fiddler, and Caddy. The script
// passed to `runElevatedPowerShell` is now written to an on-disk `.ps1` file
// (see systemCommands.ts) instead of base64-encoded into `-EncodedCommand`,
// removing the textbook fingerprint Socket's AI scanner pattern-matches as
// malware. See docs/security/SOCKET_DEV_FINDINGS.md §1 for the full attestation.
async function installCertWindows(certPath: string): Promise<void> {
  await runElevatedPowerShell(`
    $certPath = ${quotePowerShell(certPath)};
    $proc = Start-Process certutil -ArgumentList @('-addstore','Root',$certPath) -Verb RunAs -Wait -PassThru;
    if ($proc.ExitCode -ne 0) { throw "certutil exited with code $($proc.ExitCode)" }
  `);
  console.log(`✅ Installed certificate to Windows Root store`);
}

/**
 * Uninstall SSL certificate from system store
 */
export async function uninstallCert(sudoPassword: string, certPath: string): Promise<void> {
  const isInstalled = await checkCertInstalled(certPath);
  if (!isInstalled) {
    console.log("Certificate not found in system store");
    return;
  }

  if (IS_WIN) {
    await uninstallCertWindows();
  } else if (IS_MAC) {
    await uninstallCertMac(sudoPassword, certPath);
  } else {
    await uninstallCertLinux(sudoPassword, certPath);
  }
}

async function uninstallCertMac(sudoPassword: string, certPath: string): Promise<void> {
  const fingerprint = getCertFingerprint(certPath).replace(/:/g, "");
  try {
    await execFileWithPassword(
      "sudo",
      [
        "-S",
        "security",
        "delete-certificate",
        "-Z",
        fingerprint,
        "/Library/Keychains/System.keychain",
      ],
      sudoPassword
    );
    console.log("✅ Uninstalled certificate from system keychain");
  } catch (err) {
    throw new Error("Failed to uninstall certificate");
  }
}

async function uninstallCertLinux(sudoPassword: string, certPath: string): Promise<void> {
  try {
    await updateNssDatabases(null, "delete");

    const config = getLinuxCertConfig();
    const destFile = `${config.dir}/${LINUX_CERT_NAME}`;

    if (fs.existsSync(destFile)) {
      await execFileWithPassword("sudo", ["-S", "rm", "-f", destFile], sudoPassword);
    }

    try {
      await execFileWithPassword("sudo", ["-S", config.cmd, "--fresh"], sudoPassword);
    } catch {
      await execFileWithPassword("sudo", ["-S", config.cmd], sudoPassword);
    }
  } catch (err) {
    throw new Error("Failed to uninstall certificate");
  }
}

async function uninstallCertWindows(): Promise<void> {
  await runElevatedPowerShell(`
    $proc = Start-Process certutil -ArgumentList @('-delstore','Root','daily-cloudcode-pa.googleapis.com') -Verb RunAs -Wait -PassThru;
    if ($proc.ExitCode -ne 0) { throw "certutil exited with code $($proc.ExitCode)" }
  `);
  console.log("✅ Uninstalled certificate from Windows Root store");
}
