export const AHA_SCREEN_EVENT_NAMES = [
  'screen.client.loaded',
  'screen.openfinance_import.loaded',
  'screen.client_categories.loaded',
  'screen.csv_import.loaded',
  'screen.investments.loaded',
  'screen.patrimony.loaded',
  'screen.objectives.loaded',
  'screen.debts.loaded',
] as const;

export const AHA_RETURN_EVENT_NAME = 'user.returned_d1';

const metadataWhitelist: Record<string, readonly string[]> = {
  'auth.login': ['route'],
  'session.started': ['route', 'started_at'],
  'client.created': ['client_uuid'],
  'client.selected': ['client_uuid'],
  'import.openfinance.succeeded': [
    'client_uuid',
    'trigger',
    'account_id',
    'imported_count',
    'skipped_count',
    'duration_ms',
  ],
  'import.openfinance.failed': [
    'client_uuid',
    'trigger',
    'account_id',
    'status_code',
    'error_name',
    'error_message',
    'duration_ms',
  ],
  'import.csv.succeeded': [
    'client_uuid',
    'trigger',
    'file_type',
    'imported_count',
    'skipped_count',
    'invalid_count',
    'duration_ms',
  ],
  'import.csv.failed': [
    'client_uuid',
    'trigger',
    'file_type',
    'invalid_count',
    'error_name',
    'error_message',
    'duration_ms',
  ],
  'screen.client.loaded': ['client_uuid', 'route', 'record_count'],
  'screen.openfinance_import.loaded': ['client_uuid', 'route', 'record_count'],
  'screen.client_categories.loaded': ['client_uuid', 'route', 'record_count'],
  'screen.csv_import.loaded': ['client_uuid', 'route', 'record_count'],
  'screen.investments.loaded': ['client_uuid', 'route', 'record_count'],
  'screen.patrimony.loaded': ['client_uuid', 'route', 'record_count'],
  'screen.objectives.loaded': ['client_uuid', 'route', 'record_count'],
  'screen.debts.loaded': ['client_uuid', 'route', 'record_count'],
  'screen.analysis_builder.loaded': ['client_uuid', 'route', 'record_count'],
  'analysis.report.viewed': ['client_uuid', 'block_count'],
  'analysis.save.succeeded': ['client_uuid', 'block_count', 'duration_ms'],
  'analysis.save.failed': [
    'client_uuid',
    'error_name',
    'error_message',
    'duration_ms',
  ],
  'analysis.export.succeeded': ['client_uuid', 'block_count', 'duration_ms'],
  'analysis.export.failed': [
    'client_uuid',
    'error_name',
    'error_message',
    'duration_ms',
  ],
  'budget.rule.applied': ['client_uuid', 'match_type', 'matched_count'],
  'user.returned_d1': ['first_seen_at', 'returned_at', 'days_since_first_seen'],
  'api.request.failed': [
    'status_code',
    'method',
    'endpoint',
    'route',
    'duration_ms',
    'client_uuid',
    'url',
    'axios_code',
    'request_id',
    'trace_id',
    'correlation_id',
    'response_code',
    'response_message',
    'response_errors',
    'response_body_preview',
    'exception_class',
    'exception_message',
    'exception_file',
    'exception_line',
    'exception_trace',
    'exception_previous',
    'trace_truncated',
  ],
  'ui.crash': ['route', 'error_name', 'error_message', 'stack'],
  // Alocação de Ativos — analytics (web) e audit trail (api). Escopo: portfolio_uuid + currency.
  'screen.asset_allocation.loaded': [
    'client_uuid',
    'route',
    'role',
    'portfolio_uuid',
    'currency',
  ],
  'asset_allocation.target_saved': [
    'client_uuid',
    'class_count',
    'portfolio_uuid',
    'currency',
  ],
  'asset_allocation.pdf_exported': [
    'client_uuid',
    'category_count',
    'portfolio_uuid',
    'currency',
  ],
  'asset_allocation.simulator_used': [
    'client_uuid',
    'mode',
    'portfolio_uuid',
    'currency',
  ],
  'asset_allocation.target_created': [
    'client_uuid',
    'entity_uuid',
    'after',
    'portfolio_uuid',
    'currency',
  ],
  'asset_allocation.target_updated': [
    'client_uuid',
    'entity_uuid',
    'before',
    'after',
    'portfolio_uuid',
    'currency',
  ],
  'asset_allocation.target_deleted': [
    'client_uuid',
    'entity_uuid',
    'portfolio_uuid',
    'currency',
  ],
  'asset_allocation.classes_set': [
    'client_uuid',
    'entity_uuid',
    'before',
    'after',
    'portfolio_uuid',
    'currency',
  ],
};

export type AuditMetadata = Record<string, unknown>;

const REDACTED = '[REDACTED]';
const OMIT_METADATA_FIELD = Symbol('omit_metadata_field');
const SENSITIVE_KEY_PATTERN = /(token|password|secret|senha)/i;
const OMITTED_KEY_PATTERN = /^response_body$/i;
const MAX_EXCEPTION_TRACE_CHARS = 120_000;

export const sanitizeAuditMetadata = (
  eventName: string,
  metadata: AuditMetadata | undefined,
): AuditMetadata => {
  if (!metadata || typeof metadata !== 'object' || Array.isArray(metadata)) {
    return {};
  }

  const allowedFields = metadataWhitelist[eventName];
  if (!allowedFields) {
    return {};
  }

  const sanitized = allowedFields.reduce<AuditMetadata>((sanitized, key) => {
    if (Object.prototype.hasOwnProperty.call(metadata, key)) {
      const sanitizedValue = sanitizeMetadataValue(key, metadata[key]);
      if (sanitizedValue !== OMIT_METADATA_FIELD) {
        sanitized[key] = sanitizedValue;
      }
    }
    return sanitized;
  }, {});

  if (
    eventName === 'api.request.failed' &&
    typeof sanitized.exception_trace === 'string' &&
    sanitized.exception_trace.length > MAX_EXCEPTION_TRACE_CHARS
  ) {
    sanitized.exception_trace = `${sanitized.exception_trace.slice(
      0,
      MAX_EXCEPTION_TRACE_CHARS,
    )}...[truncated]`;
    sanitized.trace_truncated = true;
  }

  return sanitized;
};

const sanitizeMetadataValue = (key: string, value: unknown): unknown => {
  if (OMITTED_KEY_PATTERN.test(key)) {
    return OMIT_METADATA_FIELD;
  }

  if (SENSITIVE_KEY_PATTERN.test(key)) {
    return REDACTED;
  }

  if (typeof value === 'string') {
    return redactSensitiveString(value);
  }

  if (Array.isArray(value)) {
    return value
      .map((item) => sanitizeMetadataValue('', item))
      .filter((item) => item !== OMIT_METADATA_FIELD);
  }

  if (value && typeof value === 'object') {
    return Object.entries(value as Record<string, unknown>).reduce<
      Record<string, unknown>
    >((sanitized, [nestedKey, nestedValue]) => {
      const sanitizedValue = sanitizeMetadataValue(nestedKey, nestedValue);
      if (sanitizedValue !== OMIT_METADATA_FIELD) {
        sanitized[nestedKey] = sanitizedValue;
      }
      return sanitized;
    }, {});
  }

  return value;
};

const redactSensitiveString = (value: string): string =>
  value
    .replace(/\bbearer\s+[^\s&]+/gi, REDACTED)
    .replace(/\b\d{2}\.?\d{3}\.?\d{3}\/?\d{4}-?\d{2}\b/g, REDACTED)
    .replace(/\b\d{3}\.?\d{3}\.?\d{3}-?\d{2}\b/g, REDACTED)
    .replace(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi, REDACTED)
    .replace(
      /(\b(?:password|token|secret|senha)\b\s*[=:]?\s*)[^\s&]+/gi,
      `$1${REDACTED}`,
    );
