'use strict';

const SAFE_CREDENTIAL_METADATA_KEYS = new Set(['password_change_required']);
const SENSITIVE_KEY = /(?:^|_)(?:password|passwd|pwd|contrasena|pin|secret|credential|biometric|fingerprint|huella|template)(?:_|$)/i;
const IMAGE_KEY = /(?:^|_)(?:fotografia|photo|pic|image|imagen)(?:_|$)/i;
const KEY_MATERIAL_KEY = /(?:^|_)(?:(?:license|licencia|private|api|fiscal)_?key|llave_?privada)(?:_|$)/i;
const ALLOWED_CANONICAL_FIELDS_BY_EVENT = Object.freeze({
  OPERATOR_USER_UPSERTED: new Set([
    'operator_user_id', 'external_operator_id', 'username', 'employee_id', 'display_name', 'email', 'phone',
    'source_kind', 'role_external_id', 'active', 'status', 'administrator', 'password_change_required',
    'source_workspace_id', 'actor_id', 'reason', 'occurred_at', 'approval_request_id',
  ]),
  OPERATOR_PASSWORD_CHANGE_REQUESTED: new Set([
    'operator_user_id', 'requested_by', 'reason', 'requested_at', 'branch_id', 'scope',
    'approval_request_id', 'expires_at',
  ]),
  OPERATOR_PIN_RESET_REQUESTED: new Set([
    'operator_user_id', 'requested_by', 'reason', 'requested_at', 'branch_id', 'scope',
    'approval_request_id', 'expires_at',
  ]),
});

function normalizedKey(key) {
  return String(key).normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
}

function isSensitiveKey(key) {
  const normalized = normalizedKey(key);
  return !SAFE_CREDENTIAL_METADATA_KEYS.has(normalized) && (SENSITIVE_KEY.test(normalized) || IMAGE_KEY.test(normalized) || KEY_MATERIAL_KEY.test(normalized));
}

function sanitize(value) {
  if (Array.isArray(value)) return value.map(sanitize);
  if (!value || typeof value !== 'object') return value;
  return Object.fromEntries(Object.entries(value)
    .filter(([key]) => !isSensitiveKey(key))
    .map(([key, child]) => [key, sanitize(child)]));
}

function assertSafe(value, path = 'payload_json') {
  if (!value || typeof value !== 'object') return;
  for (const [key, child] of Object.entries(value)) {
    if (isSensitiveKey(key)) throw new Error(`Campo sensible prohibido en ${path}.${key}`);
    assertSafe(child, `${path}.${key}`);
  }
}

function assertAllowedCanonicalFields(eventType, canonical) {
  const allowed = ALLOWED_CANONICAL_FIELDS_BY_EVENT[eventType];
  if (!allowed) return;
  const unexpected = Object.keys(canonical || {}).filter((key) => !allowed.has(key));
  if (unexpected.length) throw new Error(`${eventType} contiene campos no permitidos: ${unexpected.join(', ')}`);
}

function assertSafeEvent(event) {
  assertSafe(event?.payload_json);
  assertAllowedCanonicalFields(event?.event_type, event?.payload_json?.canonical);
}

module.exports = {
  SAFE_CREDENTIAL_METADATA_KEYS,
  SENSITIVE_KEY,
  IMAGE_KEY,
  KEY_MATERIAL_KEY,
  ALLOWED_CANONICAL_FIELDS_BY_EVENT,
  normalizedKey,
  isSensitiveKey,
  sanitize,
  assertSafe,
  assertAllowedCanonicalFields,
  assertSafeEvent,
};
