/**
 * Shared PII scrubbing for logs and Sentry payloads.
 * Used by sentry middleware, capture.server, and client capture.
 */

const PII_KEYS = new Set([
  'phone',
  'email',
  'address',
  'display_name',
  'displayName',
  'ip',
  'ipAddress',
  'ip_address',
  'full_address',
  'fullAddress',
  'guest_email',
  'guest_phone',
  'name',
  'full_name',
  'fullName',
]);

// postgres://user:pass@  ->  postgres://[REDACTED]@   (password may contain spaces; user/pass stop at / or @)
const POSTGRES_USERINFO_PATTERN = /(\bpostgres(?:ql)?:\/\/)[^/@\s]+(?::[^/@]*)?@/gi;
const POSTGRES_DSN_PATTERN = /\bpostgres(?:ql)?:\/\/[^\s'"\\)]+/gi;
const PASSWORD_KV_PATTERN = /\b(?:ssl)?password=(?:'[^']*'|"[^"]*"|[^\s&'"]+)/gi;

const PII_VALUE_PATTERNS: RegExp[] = [
  /(?:\+972|0)[\s-]?(?:5[0-9]|[23489])[\s-]?\d{3}[\s-]?\d{4}/g,
  /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
];

export function scrubStringValue(value: string): string {
  let result = value
    .replace(POSTGRES_USERINFO_PATTERN, '$1[REDACTED]@')
    .replace(POSTGRES_DSN_PATTERN, 'postgres://[REDACTED]')
    .replace(PASSWORD_KV_PATTERN, 'password=[REDACTED]');
  for (const pattern of PII_VALUE_PATTERNS) {
    result = result.replace(pattern, '[Scrubbed]');
  }
  return result;
}

export function scrubPii(obj: unknown, depth = 0): unknown {
  if (depth > 10) return obj;
  if (obj === null || typeof obj !== 'object') {
    if (typeof obj === 'string') return scrubStringValue(obj);
    return obj;
  }
  if (Array.isArray(obj)) return obj.map((item) => scrubPii(item, depth + 1));

  const result: Record<string, unknown> = {};
  for (const [key, value] of Object.entries(obj as Record<string, unknown>)) {
    if (PII_KEYS.has(key)) {
      result[key] = '[Scrubbed]';
    } else {
      result[key] = scrubPii(value, depth + 1);
    }
  }
  return result;
}

const SAFE_PG_ERROR_FIELD_KEYS = ['code', 'severity', 'routine'] as const;

function collectSafePgErrorFields(obj: unknown, keyPrefix = ''): string[] {
  if (!obj || typeof obj !== 'object') return [];
  const record = obj as Record<string, unknown>;
  const parts: string[] = [];
  for (const key of SAFE_PG_ERROR_FIELD_KEYS) {
    const val = record[key];
    if (typeof val === 'string' && val.length > 0) {
      parts.push(`${keyPrefix}${key}=${val}`);
    }
  }
  return parts;
}

export function scrubErrorForLog(err: unknown): string {
  if (!(err instanceof Error)) return '[non-Error]';
  const msg = err.message ? scrubStringValue(err.message) : '';
  let result = msg ? `${err.name}: ${msg}` : err.name;

  const errFields = collectSafePgErrorFields(err);
  const cause = err.cause;

  if (cause instanceof Error) {
    const causeMsg = cause.message ? scrubStringValue(cause.message) : '';
    const causeLabel = causeMsg ? `${cause.name}: ${causeMsg}` : cause.name;
    const causeFields = [`cause=${causeLabel}`, ...collectSafePgErrorFields(cause, 'cause_')];
    const bracket =
      errFields.length > 0
        ? `${errFields.join(' ')}; ${causeFields.join(' ')}`
        : causeFields.join(' ');
    result += ` [${bracket}]`;
  } else if (errFields.length > 0) {
    result += ` [${errFields.join(' ')}]`;
  }

  return result;
}
