/**
 * Unified Input Normalization Utility
 *
 * All input adapters (Telegram, Slack, GitHub, Jira, etc.) should produce a
 * `NormalizedMessage` that conforms to the same shape expected by downstream
 * agents. This utility provides a single place to enforce that shape and to
 * perform any required sanitisation, defaulting, or enrichment.
 *
 * The shape mirrors the `InboundMessage` type used throughout the system.
 */

import { InboundMessage } from './types';
import { z } from 'zod';
import { AttachmentSchema } from './types';

/**
 * NormalizedMessage is the canonical representation of an inbound message.
 * It contains the minimal fields required by the orchestration layer.
 */
export interface NormalizedMessage {
  source: string; // e.g. 'telegram', 'slack', 'github'
  userId: string; // unique identifier for the user (or channel)
  sessionId: string; // conversation/session identifier (FNV-1a hashed for stable addressing)
  text: string; // user supplied text (or empty string for non‑text events)
  attachments: z.infer<typeof AttachmentSchema>[];
  metadata: Record<string, unknown>;
  timestamp: string; // ISO string
}

/**
 * Helper to coerce any adapter‑specific raw payload into a NormalizedMessage.
 *
 * Each adapter should call this function after its own parsing logic.
 *
 * @param raw - The raw InboundMessage returned by the adapter's `parse` method.
 * @returns NormalizedMessage
 */
export function normalizeMessage(raw: InboundMessage): NormalizedMessage {
  // Ensure required fields exist; fall back to safe defaults.
  let { userId = 'unknown', sessionId = userId } = raw;
  const { source = 'unknown', text = '', attachments = [], metadata = {}, timestamp } = raw;

  // Security: Prevent external identity spoofing via internal prefixes (P0 finding)
  if (userId.startsWith('CONV#') || userId.includes('#')) {
    userId = userId.replace(/#/g, '_');
  }
  if (sessionId.startsWith('CONV#') || (sessionId !== userId && sessionId.includes('#'))) {
    sessionId = sessionId.replace(/#/g, '_');
  }

  // Preserve the original timestamp if provided; otherwise generate now.
  const isoTimestamp = timestamp ?? new Date().toISOString();

  return {
    source,
    userId,
    sessionId,
    text,
    attachments,
    metadata,
    timestamp: isoTimestamp,
  };
}
