import {
  WebSocketGateway,
  WebSocketServer,
  SubscribeMessage,
  OnGatewayConnection,
  OnGatewayDisconnect,
  ConnectedSocket,
  MessageBody,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';
import { ConversationService } from './conversation.service';

interface FileAttachment {
  id: string;
  originalName: string;
  mimeType: string;
  type: 'image' | 'document' | 'audio' | 'video' | 'other';
  size: number;
  downloadUrl?: string;
  thumbnailUrl?: string;
}

interface SendMessagePayload {
  conversationId: string;
  content: string;
  attachments?: FileAttachment[];
}

@WebSocketGateway({
  cors: {
    origin: process.env.CORS_ORIGINS?.split(',') || ['http://localhost:5173'],
    credentials: true,
  },
  namespace: '/ws/conversation',
  path: '/ws/conversation/socket.io',
})
export class ConversationGateway
  implements OnGatewayConnection, OnGatewayDisconnect
{
  @WebSocketServer()
  server: Server;

  // Map socket ID to user ID
  private connections = new Map<string, string>();

  constructor(private conversationService: ConversationService) {}

  async handleConnection(client: Socket) {
    // Extract user ID from query or headers
    const userId =
      (client.handshake.query.userId as string) ||
      (client.handshake.headers['x-user-id'] as string);

    if (!userId) {
      console.log(`Client ${client.id} connected without user ID`);
      client.emit('error', { message: 'User ID is required' });
      client.disconnect();
      return;
    }

    this.connections.set(client.id, userId);
    console.log(`Client ${client.id} connected as user ${userId}`);

    client.emit('connected', { userId, socketId: client.id });
  }

  handleDisconnect(client: Socket) {
    const userId = this.connections.get(client.id);
    this.connections.delete(client.id);
    console.log(`Client ${client.id} (user ${userId}) disconnected`);
  }

  @SubscribeMessage('message')
  async handleMessage(
    @ConnectedSocket() client: Socket,
    @MessageBody() payload: SendMessagePayload,
  ) {
    const userId = this.connections.get(client.id);

    if (!userId) {
      client.emit('error', { message: 'Not authenticated' });
      return;
    }

    const { conversationId, content, attachments } = payload;

    if (!conversationId || !content) {
      client.emit('error', { message: 'conversationId and content are required' });
      return;
    }

    try {
      // Generate unique message ID for this response
      const messageId = `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;

      // Emit stream start
      client.emit('stream_start', { messageId, conversationId });

      let chunkIndex = 0;

      // Stream the response
      for await (const chunk of this.conversationService.sendMessage({
        conversationId,
        userId,
        content,
        attachments,
      })) {
        if (chunk.type === 'text' && chunk.content) {
          client.emit('stream_chunk', {
            messageId,
            conversationId,
            content: chunk.content,
            index: chunkIndex++,
          });
        } else if (chunk.type === 'tool_use') {
          client.emit('tool_call', {
            messageId,
            conversationId,
            tool: chunk.toolName,
            input: chunk.toolInput,
          });
        } else if (chunk.type === 'tool_result') {
          client.emit('tool_result', {
            messageId,
            conversationId,
            tool: chunk.toolName,
            result: chunk.toolResult,
          });
        } else if (chunk.type === 'end') {
          client.emit('stream_end', {
            messageId,
            conversationId,
            isComplete: true,
          });
        }
      }
    } catch (error) {
      console.error('Error processing message:', error);
      client.emit('error', {
        message: error instanceof Error ? error.message : 'Failed to process message',
        conversationId,
      });
    }
  }

  @SubscribeMessage('typing_start')
  handleTypingStart(
    @ConnectedSocket() client: Socket,
    @MessageBody() payload: { conversationId: string },
  ) {
    // Could be used to show typing indicator to admin monitoring
    console.log(`User typing in conversation ${payload.conversationId}`);
  }

  @SubscribeMessage('typing_end')
  handleTypingEnd(
    @ConnectedSocket() client: Socket,
    @MessageBody() payload: { conversationId: string },
  ) {
    console.log(`User stopped typing in conversation ${payload.conversationId}`);
  }
}
