/**
 * Chat Message Sanitization
 *
 * Sanitizes chat/conversation messages to prevent XSS attacks.
 * Used when storing messages from N8N webhooks, voice agents, etc.
 *
 * Security Considerations:
 * 1. DOMPurify removes dangerous HTML/scripts
 * 2. Length limits prevent DoS attacks
 * 3. Null byte removal prevents bypass attempts
 * 4. Link validation prevents javascript: protocol attacks
 */

import DOMPurify from 'isomorphic-dompurify';

// DOMPurify configuration for chat messages
const CHAT_DOMPURIFY_CONFIG = {
  // Remove all HTML - chat messages should be plain text
  ALLOWED_TAGS: [],
  ALLOWED_ATTR: [],
  // Remove dangerous URL protocols
  FORBID_ATTR: ['style', 'onerror', 'onload', 'onclick', 'onmouseover'],
  // Extra safety
  FORBID_CONTENTS: ['script', 'style', 'iframe', 'object', 'embed'],
};

// Maximum message length (prevent DoS with huge messages)
const MAX_MESSAGE_LENGTH = 10000;

// Maximum customer name length
const MAX_NAME_LENGTH = 200;

// Maximum phone length
const MAX_PHONE_LENGTH = 30;

/**
 * Sanitize a chat message content
 *
 * Removes HTML tags, dangerous protocols, and normalizes the text.
 * Safe to use for rendering in React (but still use JSX escaping).
 */
export function sanitizeChatMessage(content: string | null | undefined): string {
  if (!content) return '';

  // 1. Trim and limit length
  let sanitized = String(content).trim().slice(0, MAX_MESSAGE_LENGTH);

  // 2. Remove null bytes (can bypass sanitization)
  sanitized = sanitized.replace(/\0/g, '');

  // 3. Remove dangerous protocols
  sanitized = sanitized.replace(/javascript:/gi, '');
  sanitized = sanitized.replace(/vbscript:/gi, '');
  sanitized = sanitized.replace(/data:/gi, 'data-blocked:');

  // 4. Use DOMPurify for HTML sanitization
  sanitized = String(DOMPurify.sanitize(sanitized, CHAT_DOMPURIFY_CONFIG));

  // 5. Normalize whitespace (preserve newlines for formatting)
  sanitized = sanitized
    .split('\n')
    .map(line => line.trim())
    .join('\n')
    .replace(/\n{3,}/g, '\n\n'); // Max 2 consecutive newlines

  return sanitized;
}

/**
 * Sanitize customer name from external sources
 */
export function sanitizeCustomerName(name: string | null | undefined): string {
  if (!name) return '';

  let sanitized = String(name).trim().slice(0, MAX_NAME_LENGTH);

  // Remove null bytes
  sanitized = sanitized.replace(/\0/g, '');

  // Use DOMPurify
  sanitized = String(DOMPurify.sanitize(sanitized, CHAT_DOMPURIFY_CONFIG));

  // Remove control characters except spaces
  sanitized = sanitized.replace(/[\x00-\x1F\x7F]/g, '');

  return sanitized.trim();
}

/**
 * Sanitize phone number from external sources
 */
export function sanitizePhoneNumber(phone: string | null | undefined): string {
  if (!phone) return '';

  // Allow only digits, +, -, (), spaces
  let sanitized = String(phone).trim().slice(0, MAX_PHONE_LENGTH);
  sanitized = sanitized.replace(/[^\d+\-() ]/g, '');

  return sanitized.trim();
}

/**
 * Sanitize metadata object from external sources
 *
 * Removes any potentially dangerous keys or values.
 * Only allows string, number, boolean, and nested objects.
 */
export function sanitizeMetadata(
  metadata: Record<string, unknown> | null | undefined
): Record<string, unknown> {
  if (!metadata || typeof metadata !== 'object') return {};

  const sanitized: Record<string, unknown> = {};

  for (const [key, value] of Object.entries(metadata)) {
    // Skip dangerous keys
    if (key.startsWith('__') || key.includes('<') || key.includes('>')) {
      continue;
    }

    // Sanitize key
    const cleanKey = key.replace(/[<>'"&]/g, '').slice(0, 100);

    if (typeof value === 'string') {
      // Sanitize string values
      sanitized[cleanKey] = sanitizeChatMessage(value);
    } else if (typeof value === 'number' && isFinite(value)) {
      sanitized[cleanKey] = value;
    } else if (typeof value === 'boolean') {
      sanitized[cleanKey] = value;
    } else if (value === null) {
      sanitized[cleanKey] = null;
    } else if (typeof value === 'object' && !Array.isArray(value)) {
      // Recursively sanitize nested objects (max 1 level)
      sanitized[cleanKey] = sanitizeMetadata(value as Record<string, unknown>);
    }
    // Skip arrays and other types for safety
  }

  return sanitized;
}

/**
 * Sanitize an entire conversation message object
 */
export interface RawConversationMessage {
  role?: string;
  content?: string;
  text?: string;
  timestamp?: string;
  speaker?: string;
  metadata?: Record<string, unknown>;
}

export interface SanitizedConversationMessage {
  role: 'customer' | 'ai_agent' | 'admin';
  content: string;
  timestamp: string;
  metadata: Record<string, unknown>;
}

export function sanitizeConversationMessage(
  raw: RawConversationMessage
): SanitizedConversationMessage {
  // Sanitize content
  const content = sanitizeChatMessage(raw.content || raw.text);

  // Map role to MessageSender enum
  let role: 'customer' | 'ai_agent' | 'admin';
  const rawRole = String(raw.role || raw.speaker || 'user').toLowerCase();

  if (rawRole === 'user' || rawRole === 'customer') {
    role = 'customer';
  } else if (rawRole === 'assistant' || rawRole === 'agent' || rawRole === 'ai_agent') {
    role = 'ai_agent';
  } else if (rawRole === 'admin' || rawRole === 'system') {
    role = 'admin';
  } else {
    role = 'customer'; // Default to customer for unknown roles
  }

  // Validate and sanitize timestamp
  let timestamp: string;
  if (raw.timestamp) {
    const parsed = new Date(raw.timestamp);
    timestamp = isNaN(parsed.getTime()) ? new Date().toISOString() : parsed.toISOString();
  } else {
    timestamp = new Date().toISOString();
  }

  return {
    role,
    content,
    timestamp,
    metadata: sanitizeMetadata(raw.metadata),
  };
}

/**
 * Check if content contains potential XSS patterns
 * Used for logging/alerting on suspicious input
 */
export function detectXSSPatterns(content: string): {
  isSuspicious: boolean;
  patterns: string[];
} {
  const patterns: string[] = [];

  // Check for script tags
  if (/<script/i.test(content)) {
    patterns.push('script_tag');
  }

  // Check for event handlers
  if (/on\w+=/i.test(content)) {
    patterns.push('event_handler');
  }

  // Check for javascript protocol
  if (/javascript:/i.test(content)) {
    patterns.push('javascript_protocol');
  }

  // Check for data URIs
  if (/data:\s*text\/html/i.test(content)) {
    patterns.push('data_uri_html');
  }

  // Check for expression() (IE CSS)
  if (/expression\s*\(/i.test(content)) {
    patterns.push('css_expression');
  }

  // Check for SVG with onload
  if (/<svg[^>]*onload/i.test(content)) {
    patterns.push('svg_onload');
  }

  return {
    isSuspicious: patterns.length > 0,
    patterns,
  };
}
