/**
 * phpass-compatible password validation for WordPress migrated users.
 * Supports:
 *   $P$B...       — Standard WordPress phpass (MD5-based iterated hash)
 *   $wp$2y$10$... — bcrypt-based WordPress hash (newer accounts)
 */

import crypto from 'crypto';

const ITOA64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';

function encode64(input: Buffer, count: number): string {
  let output = '';
  let i = 0;
  do {
    let value = input[i++];
    output += ITOA64[value & 0x3f];
    if (i < count) value |= input[i] << 8;
    output += ITOA64[(value >> 6) & 0x3f];
    if (i++ >= count) break;
    if (i < count) value |= input[i] << 16;
    output += ITOA64[(value >> 12) & 0x3f];
    if (i++ >= count) break;
    output += ITOA64[(value >> 18) & 0x3f];
  } while (i < count);
  return output;
}

function cryptPrivate(password: string, setting: string): string {
  let output = '*0';
  if (setting.substring(0, 2) === output) return '*1';

  const id = setting.substring(0, 3);
  if (id !== '$P$' && id !== '$H$') return output;

  const countLog2 = ITOA64.indexOf(setting[3]);
  if (countLog2 < 7 || countLog2 > 30) return output;

  let count = 1 << countLog2;
  const salt = setting.substring(4, 12);
  if (salt.length !== 8) return output;

  let hash = crypto.createHash('md5').update(salt + password).digest();

  do {
    hash = crypto.createHash('md5').update(Buffer.concat([hash, Buffer.from(password)])).digest();
  } while (--count);

  return setting.substring(0, 12) + encode64(hash, 16);
}

/**
 * Validate a password against a WordPress phpass hash.
 * Returns true if the password matches.
 */
export async function validatePhpassPassword(
  plainPassword: string,
  storedHash: string
): Promise<boolean> {
  if (!storedHash || !plainPassword) return false;

  // Handle bcrypt-based WordPress hashes: $wp$2y$10$...
  // WordPress stores these as $wp$2y$... — strip the $wp prefix to get standard bcrypt
  if (storedHash.startsWith('$wp$2y$') || storedHash.startsWith('$wp$2b$')) {
    const bcryptHash = storedHash.replace(/^\$wp/, '');
    try {
      const bcrypt = await import('bcryptjs');
      return bcrypt.compare(plainPassword, bcryptHash);
    } catch {
      // bcryptjs not available — fall through to manual check
      return false;
    }
  }

  // Handle standard bcrypt hashes: $2y$... or $2b$...
  if (storedHash.startsWith('$2y$') || storedHash.startsWith('$2b$') || storedHash.startsWith('$2a$')) {
    try {
      const bcrypt = await import('bcryptjs');
      const normalizedHash = storedHash.replace(/^\$2y\$/, '$2b$');
      return bcrypt.compare(plainPassword, normalizedHash);
    } catch {
      return false;
    }
  }

  // Handle phpass portable hash: $P$B...
  if (storedHash.startsWith('$P$') || storedHash.startsWith('$H$')) {
    const computed = cryptPrivate(plainPassword, storedHash);
    return computed === storedHash;
  }

  // Fallback: MD5 (very old WordPress)
  if (storedHash.length === 32 && /^[a-f0-9]+$/i.test(storedHash)) {
    const md5 = crypto.createHash('md5').update(plainPassword).digest('hex');
    return md5 === storedHash;
  }

  return false;
}

/**
 * Detect the hash type for logging/debugging purposes.
 */
export function detectHashType(hash: string): 'phpass' | 'bcrypt_wp' | 'bcrypt' | 'md5' | 'unknown' {
  if (hash.startsWith('$P$') || hash.startsWith('$H$')) return 'phpass';
  if (hash.startsWith('$wp$2y$') || hash.startsWith('$wp$2b$')) return 'bcrypt_wp';
  if (hash.startsWith('$2y$') || hash.startsWith('$2b$') || hash.startsWith('$2a$')) return 'bcrypt';
  if (hash.length === 32 && /^[a-f0-9]+$/i.test(hash)) return 'md5';
  return 'unknown';
}
