import { createDecipheriv, createHmac, pbkdf2Sync } from 'node:crypto';

/**
 * WCDB (Tencent SQLCipher fork) cipher_compatibility=4 解密。
 * 已通过 packages/backend/poc/weixin-4x/poc-7f-reserve80.mjs 实测验证。
 *
 * 关键参数:
 *   PageSize     = 4096
 *   ReservedSize = 80 (IV 16 + HMAC-SHA512 64)
 *   encKey       = raw 32B (不再派生,直接作为 AES key)
 *   hmacKey      = PBKDF2-HMAC-SHA512(rawKey, salt XOR 0x3a, 2 rounds, 32B)
 */
export const PAGE_SIZE = 4096;
export const RESERVED_SIZE = 80;
export const IV_SIZE = 16;
export const HMAC_SIZE = 64;
export const KEY_SIZE = 32;
export const HMAC_SALT_MASK = 0x3a;

/** 派生 HMAC 密钥:salt XOR 0x3a 后 PBKDF2-SHA512 2 轮,输出 32B。 */
export function deriveHmacKey(rawKey: Buffer, salt: Buffer): Buffer {
  if (rawKey.length !== KEY_SIZE) throw new Error('rawKey must be 32B');
  if (salt.length !== IV_SIZE) throw new Error('salt must be 16B');
  const hmacSalt = Buffer.from(salt);
  for (let i = 0; i < hmacSalt.length; i++) hmacSalt[i] ^= HMAC_SALT_MASK;
  return pbkdf2Sync(rawKey, hmacSalt, 2, KEY_SIZE, 'sha512');
}

/**
 * 解密单 page。pageBuf.length 必须 === PAGE_SIZE。
 * HMAC 验证失败时返回 null(全零 page 也会 fail,调用方按需跳过)。
 */
export function decryptPage(
  pageBuf: Buffer,
  pageNum: number,
  rawKey: Buffer,
  hmacKey: Buffer,
): Buffer | null {
  if (pageBuf.length !== PAGE_SIZE) throw new Error('page must be 4096B');
  const encStart = pageNum === 1 ? 16 : 0;
  const encEnd = PAGE_SIZE - RESERVED_SIZE;
  const ciphertext = pageBuf.subarray(encStart, encEnd);
  const iv = pageBuf.subarray(encEnd, encEnd + IV_SIZE);
  const storedHmac = pageBuf.subarray(encEnd + IV_SIZE, encEnd + IV_SIZE + HMAC_SIZE);

  const pgLE = Buffer.alloc(4);
  pgLE.writeUInt32LE(pageNum, 0);
  const h = createHmac('sha512', hmacKey);
  h.update(ciphertext);
  h.update(iv);
  h.update(pgLE);
  const computed = h.digest();
  if (!computed.equals(storedHmac)) return null;

  const dec = createDecipheriv('aes-256-cbc', rawKey, iv);
  dec.setAutoPadding(false);
  const plaintext = Buffer.concat([dec.update(ciphertext), dec.final()]);

  const result = Buffer.alloc(PAGE_SIZE);
  if (pageNum === 1) {
    Buffer.from('SQLite format 3\0', 'utf8').copy(result, 0);
  }
  plaintext.copy(result, encStart);
  pageBuf.subarray(encEnd).copy(result, encEnd);
  return result;
}

/**
 * 解密整个 DB 文件 buffer。输出明文 SQLite DB。
 * 全零 / HMAC 失败的 page 保留 0(未使用 page 正常情况)。
 */
export function decryptDatabase(dbBuf: Buffer, rawKey: Buffer): Buffer {
  if (dbBuf.length < PAGE_SIZE || dbBuf.length % PAGE_SIZE !== 0) {
    throw new Error(`db size invalid: ${dbBuf.length}`);
  }
  const salt = dbBuf.subarray(0, 16);
  const hmacKey = deriveHmacKey(rawKey, salt);
  const total = dbBuf.length / PAGE_SIZE;
  const out = Buffer.alloc(dbBuf.length);
  let okPages = 0;
  for (let n = 1; n <= total; n++) {
    const start = (n - 1) * PAGE_SIZE;
    const page = dbBuf.subarray(start, start + PAGE_SIZE);
    const decrypted = decryptPage(page, n, rawKey, hmacKey);
    if (decrypted) {
      decrypted.copy(out, start);
      okPages++;
    }
  }
  if (okPages === 0) throw new Error('No page decrypted — wrong key?');
  return out;
}
