/**
 * PII masking utility for structured logs.
 * Redacts sensitive fields in log data before they reach the transport.
 */

const SENSITIVE_KEYS = new Set([
  'password',
  'passwordHash',
  'token',
  'accessToken',
  'refreshToken',
  'secret',
  'authorization',
  'cookie',
  'creditCard',
  'cardNumber',
  'cvv',
  'ssn',
  'cmnd',
  'cccd',
  'email',
  'phone',
  'kycData',
  'idNumber',
  'identityNumber',
  'dateOfBirth',
  'dob',
  'address',
  'bankAccount',
  'accountNumber',
  'apiKey',
  'privateKey',
  'encryptionKey',
]);

const EMAIL_REGEX = /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g;
const PHONE_REGEX = /(?:\+84|0)\d{9,10}/g;

export function maskPii(obj: unknown): unknown {
  if (obj === null || obj === undefined) return obj;
  if (typeof obj === 'string') return maskString(obj);
  if (Array.isArray(obj)) return obj.map(maskPii);
  if (typeof obj === 'object') {
    const masked: Record<string, unknown> = {};
    for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {
      if (SENSITIVE_KEYS.has(key)) {
        masked[key] = '[REDACTED]';
      } else {
        masked[key] = maskPii(value);
      }
    }
    return masked;
  }
  return obj;
}

function maskString(value: string): string {
  let result = value;
  result = result.replace(EMAIL_REGEX, (match) => {
    const [local, domain] = match.split('@');
    return `${local![0]}***@${domain}`;
  });
  result = result.replace(PHONE_REGEX, (match) => {
    return match.slice(0, 3) + '****' + match.slice(-3);
  });
  return result;
}
