#!/usr/bin/env node
/**
 * Extract cookies from Chrome via CDP and write to Netscape cookies.txt format.
 *
 * This script launches Chrome with a given user data directory, connects via CDP,
 * extracts all cookies, and writes them to a cookies.txt file in Netscape format.
 *
 * Usage:
 *   CHROME_USER_DATA_DIR=/path/to/profile COOKIES_OUTPUT_FILE=/path/to/cookies.txt node extract_cookies.js
 *
 * Environment variables:
 *   CHROME_USER_DATA_DIR: Path to Chrome user data directory (required)
 *   COOKIES_OUTPUT_FILE: Path to output cookies.txt file (required)
 *   CHROME_HEADLESS: Run in headless mode (default: true)
 *   NODE_MODULES_DIR: Path to node_modules for module resolution
 */

const fs = require("fs");
const path = require("path");
const { ensureNodeModuleResolution, getEnv } = require("../base/utils.js");
ensureNodeModuleResolution(module);
const {
  findAnyChromiumBinary,
  launchChromium,
  killChrome,
  getCookiesViaCdp,
} = require("./chrome_utils.js");

/**
 * Convert a cookie object to Netscape cookies.txt format line.
 *
 * Format: domain  includeSubdomains  path  secure  expiry  name  value
 *
 * @param {Object} cookie - CDP cookie object
 * @returns {string} - Netscape format cookie line
 */
function cookieToNetscape(cookie) {
  // Domain: prefix with . for domain cookies (not host-only)
  let domain = cookie.domain;
  if (!domain.startsWith(".") && !cookie.hostOnly) {
    domain = "." + domain;
  }

  // Include subdomains: TRUE if domain cookie (starts with .)
  const includeSubdomains = domain.startsWith(".") ? "TRUE" : "FALSE";

  // Path
  const cookiePath = cookie.path || "/";

  // Secure flag
  const secure = cookie.secure ? "TRUE" : "FALSE";

  // Expiry timestamp (0 for session cookies)
  let expiry = "0";
  if (cookie.expires && cookie.expires > 0) {
    // CDP returns expiry in seconds since epoch
    expiry = Math.floor(cookie.expires).toString();
  }

  // Name and value
  const name = cookie.name;
  const value = cookie.value;

  return `${domain}\t${includeSubdomains}\t${cookiePath}\t${secure}\t${expiry}\t${name}\t${value}`;
}

/**
 * Write cookies to Netscape cookies.txt format file.
 *
 * @param {Array} cookies - Array of CDP cookie objects
 * @param {string} outputPath - Path to output file
 */
function writeCookiesFile(cookies, outputPath) {
  const lines = [
    "# Netscape HTTP Cookie File",
    "# https://curl.se/docs/http-cookies.html",
    "# This file was generated by ArchiveBox persona cookie extraction",
    "#",
    "# Format: domain\\tincludeSubdomains\\tpath\\tsecure\\texpiry\\tname\\tvalue",
    "",
  ];

  for (const cookie of cookies) {
    lines.push(cookieToNetscape(cookie));
  }

  fs.writeFileSync(outputPath, lines.join("\n") + "\n");
}

async function main() {
  const userDataDir = getEnv("CHROME_USER_DATA_DIR");
  const outputFile = getEnv("COOKIES_OUTPUT_FILE");

  if (!userDataDir) {
    console.error(
      "ERROR: CHROME_USER_DATA_DIR environment variable is required"
    );
    process.exit(1);
  }

  if (!outputFile) {
    console.error(
      "ERROR: COOKIES_OUTPUT_FILE environment variable is required"
    );
    process.exit(1);
  }

  if (!fs.existsSync(userDataDir)) {
    console.error(`ERROR: User data directory does not exist: ${userDataDir}`);
    process.exit(1);
  }

  const binary = findAnyChromiumBinary();
  if (!binary) {
    console.error("ERROR: Chromium-based browser binary not found");
    process.exit(1);
  }

  console.error(`[*] Extracting cookies from: ${userDataDir}`);
  console.error(`[*] Output file: ${outputFile}`);
  console.error(`[*] Using browser: ${binary}`);

  // Create a temporary output directory for Chrome files
  const outputDir = fs.mkdtempSync(
    path.join(require("os").tmpdir(), "chrome-cookies-")
  );

  let chromePid = null;

  try {
    // Launch Chrome with the user data directory
    const result = await launchChromium({
      binary,
      outputDir,
      CHROME_USER_DATA_DIR: userDataDir,
      CHROME_HEADLESS: true,
      killZombies: false, // Don't kill other Chrome instances
    });

    if (!result.success) {
      console.error(`ERROR: Failed to launch Chrome: ${result.error}`);
      process.exit(1);
    }

    chromePid = result.pid;
    const cdpUrl = result.cdpUrl;
    const port = result.port;

    console.error(`[*] Chrome launched (PID: ${chromePid})`);
    console.error(`[*] CDP URL: ${cdpUrl}`);

    // Wait a moment for the browser to fully initialize
    await new Promise((r) => setTimeout(r, 2000));

    console.error("[*] Fetching cookies via CDP...");
    const cookies = await getCookiesViaCdp(port, { timeoutMs: 20000 });

    console.error(`[+] Retrieved ${cookies.length} cookies`);

    // Write cookies to file
    writeCookiesFile(cookies, outputFile);
    console.error(`[+] Wrote cookies to: ${outputFile}`);

    // Clean up
    await killChrome(chromePid, outputDir);
    chromePid = null;

    // Remove temp directory
    fs.rmSync(outputDir, { recursive: true, force: true });

    console.error("[+] Cookie extraction complete");
    process.exit(0);
  } catch (error) {
    console.error(`ERROR: ${error.message}`);

    // Clean up on error
    if (chromePid) {
      await killChrome(chromePid, outputDir);
    }

    try {
      fs.rmSync(outputDir, { recursive: true, force: true });
    } catch (e) {}

    process.exit(1);
  }
}

main().catch((e) => {
  console.error(`Fatal error: ${e.message}`);
  process.exit(1);
});
