// src/lib/message-queue.ts
// Copyright (C) 2026 Robert Colbert <rob.colbert@openplatform.us>
// All Rights Reserved

import redis from './redis.js';
import { GadgetLog } from '@gadget/api';

const log = new GadgetLog({ name: 'MessageQueue', slug: 'message-queue' });

export interface QueuedMessage {
  type: 'thinking' | 'response' | 'toolCall' | 'workOrderComplete' | 
        'log' | 'status' | 'agent:thinking' | 'agent:response' | 
        'agent:tool-call' | 'agent:tool-result' | 'agent:complete';
  args: unknown[];
  timestamp: number;
}

const QUEUE_KEY = (chatSessionId: string) => `gadget:messages:${chatSessionId}`;
const TTL_SECONDS = 1800; // 30 minutes
const MAX_QUEUE_SIZE = 1000;

class MessageQueueService {
  /**
   * Enqueues a message to Redis for later delivery.
   * Uses RPUSH for FIFO ordering (messages delivered in order).
   * Trims queue to MAX_QUEUE_SIZE messages (drop oldest if exceeded).
   */
  async enqueue(chatSessionId: string, message: QueuedMessage): Promise<void> {
    const key = QUEUE_KEY(chatSessionId);
    const serialized = JSON.stringify(message);
    
    try {
      // RPUSH for FIFO ordering
      await redis.rpush(key, serialized);
      
      // Trim to MAX_QUEUE_SIZE messages (drop oldest from left)
      await redis.ltrim(key, -MAX_QUEUE_SIZE, -1);
      
      // Set/refresh TTL
      await redis.expire(key, TTL_SECONDS);
    } catch (error) {
      log.error('failed to enqueue message', { chatSessionId, error });
      throw error; // Drone will stop on error
    }
  }
  
  /**
   * Drains all queued messages in FIFO order and deletes the queue.
   * Returns messages for delivery to reconnected client.
   */
  async drain(chatSessionId: string): Promise<QueuedMessage[]> {
    const key = QUEUE_KEY(chatSessionId);
    
    try {
      // Get all messages
      const messages = await redis.lrange(key, 0, -1);
      
      if (messages.length > 0) {
        // Delete the queue after draining
        await redis.del(key);
        
        return messages.map(msg => JSON.parse(msg) as QueuedMessage);
      }
      
      return [];
    } catch (error) {
      log.error('failed to drain message queue', { chatSessionId, error });
      throw error;
    }
  }
  
  /**
   * Cleans up a message queue (e.g., on session close).
   */
  async cleanup(chatSessionId: string): Promise<void> {
    const key = QUEUE_KEY(chatSessionId);
    try {
      await redis.del(key);
    } catch (error) {
      log.error('failed to cleanup message queue', { chatSessionId, error });
    }
  }
}

export default new MessageQueueService();
