/**
 * Reimplementation of string-crypto v2 using SubtleCrypto (Web Crypto API)
 * Works in browsers, Bun, Deno, Cloudflare Workers, etc.
 */

const KEYLEN = 256; // bits

async function deriveKey(
  password: string,
  salt: string = "s41t",
  iterations: number = 1
): Promise<CryptoKey> {
  const enc = new TextEncoder();

  // Import password as raw key material
  const keyMaterial = await crypto.subtle.importKey(
    "raw",
    enc.encode(password),
    "PBKDF2",
    false,
    ["deriveKey"]
  );

  // Derive AES-GCM key using PBKDF2
  return crypto.subtle.deriveKey(
    {
      name: "PBKDF2",
      salt: enc.encode(salt),
      iterations,
      hash: "SHA-512",
    },
    keyMaterial,
    { name: "AES-GCM", length: KEYLEN },
    false,
    ["encrypt", "decrypt"]
  );
}

export async function encryptString(
  str: string,
  password: string
): Promise<string> {
  const key = await deriveKey(password);
  const enc = new TextEncoder();

  // Generate random IV
  const iv = crypto.getRandomValues(new Uint8Array(16));

  // Encrypt
  const encrypted = await crypto.subtle.encrypt(
    { name: "AES-GCM", iv },
    key,
    enc.encode(str)
  );

  // SubtleCrypto appends 16-byte auth tag, but v2 string-crypto ignores it
  // Strip the last 16 bytes to match v2 behavior
  const encryptedWithoutTag = new Uint8Array(encrypted).slice(0, -16);

  // Convert to base64, then to hex (v2 quirk)
  const encryptedBase64 = btoa(
    String.fromCharCode(...encryptedWithoutTag)
  );
  const encryptedHex = Buffer.from(encryptedBase64).toString("hex");

  // IV to hex
  const ivHex = Array.from(iv)
    .map((b) => b.toString(16).padStart(2, "0"))
    .join("");

  return `${ivHex}:${encryptedHex}`;
}

export async function decryptString(
  encryptedStr: string,
  password: string
): Promise<string> {
  const key = await deriveKey(password);
  const [ivHex, encryptedHex] = encryptedStr.split(":");

  // Hex to IV
  const iv = new Uint8Array(
    ivHex.match(/.{2}/g)!.map((byte) => parseInt(byte, 16))
  );

  // Hex to base64 to bytes
  const encryptedBase64 = Buffer.from(encryptedHex, "hex").toString();
  const encryptedBytes = Uint8Array.from(atob(encryptedBase64), (c) =>
    c.charCodeAt(0)
  );

  // Decrypt
  const decrypted = await crypto.subtle.decrypt(
    { name: "AES-GCM", iv },
    key,
    encryptedBytes
  );

  return new TextDecoder().decode(decrypted);
}

// Test
if (import.meta.main) {
  // Load .env
  const envFile = Bun.file(".env");
  if (await envFile.exists()) {
    const envContent = await envFile.text();
    for (const line of envContent.split("\n")) {
      const [key, ...valueParts] = line.split("=");
      if (key && valueParts.length) {
        process.env[key.trim()] = valueParts.join("=").trim();
      }
    }
  }

  const email = process.env.ANYCLIP_EMAIL || process.env.ANYCLIP_USER;
  const plaintext = process.env.ANYCLIP_PASSWORD;

  if (!email || !plaintext) {
    console.error("Set ANYCLIP_EMAIL and ANYCLIP_PASSWORD in .env");
    process.exit(1);
  }

  const salt = "$2b$04$wwky7rvtr6BFNaCqntwyie";
  const encrypted = await encryptString(plaintext, salt);
  console.log("Encrypted:", encrypted);

  // Test with API
  console.log("\nTesting with AnyClip API...");
  const response = await fetch(
    "https://videomanager-api.anyclip.com/public/auth/login",
    {
      method: "POST",
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        email,
        password: encrypted,
      }),
    }
  );

  console.log("Status:", response.status);
  if (response.ok) {
    console.log("✅ Works with SubtleCrypto!");
  } else {
    const text = await response.text();
    console.log("Response:", text);
  }
}
