import { NextRequest } from 'next/server';
import { WebSocketServer } from 'ws';

// Store WebSocket connections for each project
const projectConnections = new Map<string, Set<any>>();
const userConnections = new Map<string, any>();

export async function GET(
  req: NextRequest,
  { params }: { params: Promise<{ projectId: string }> }
) {
  const { projectId } = await params;
  
  // In a real implementation, you would upgrade the HTTP connection to WebSocket
  // This is a placeholder for the WebSocket upgrade logic
  
  return new Response('WebSocket endpoint - upgrade required', {
    status: 426,
    headers: {
      'Upgrade': 'websocket',
      'Connection': 'Upgrade',
    },
  });
}

// WebSocket connection handler (this would typically be in a separate WebSocket server)
function handleWebSocketConnection(ws: any, projectId: string, userId: string) {
  // Add connection to project
  if (!projectConnections.has(projectId)) {
    projectConnections.set(projectId, new Set());
  }
  projectConnections.get(projectId)!.add(ws);
  userConnections.set(userId, ws);

  // Send welcome message
  ws.send(JSON.stringify({
    type: 'connected',
    message: 'Connected to project chat'
  }));

  // Notify other users that someone joined
  broadcastToProject(projectId, {
    type: 'user_joined',
    userId: userId,
    projectId: projectId
  }, userId);

  ws.on('message', async (data: string) => {
    try {
      const message = JSON.parse(data);
      await handleWebSocketMessage(ws, message, projectId, userId);
    } catch (error) {
      console.error('Error handling WebSocket message:', error);
      ws.send(JSON.stringify({
        type: 'error',
        message: 'Invalid message format'
      }));
    }
  });

  ws.on('close', () => {
    // Remove connection
    projectConnections.get(projectId)?.delete(ws);
    userConnections.delete(userId);
    
    // Notify other users that someone left
    broadcastToProject(projectId, {
      type: 'user_left',
      userId: userId,
      projectId: projectId
    }, userId);
    
    // Clean up empty project connection sets
    if (projectConnections.get(projectId)?.size === 0) {
      projectConnections.delete(projectId);
    }
  });

  ws.on('error', (error: any) => {
    console.error('WebSocket error:', error);
  });
}

async function handleWebSocketMessage(ws: any, message: any, projectId: string, userId: string) {
  switch (message.type) {
    case 'auth':
      // Store bidId with the connection for bid-specific threading
      if (message.bidId) {
        ws.bidId = message.bidId;
      }
      ws.send(JSON.stringify({
        type: 'auth_success',
        userId: userId,
        bidId: message.bidId
      }));
      break;

    case 'send_message':
      await handleSendMessage(message, projectId, userId);
      break;

    case 'typing_start':
      broadcastToProject(projectId, {
        type: 'typing_start',
        userId: userId,
        projectId: projectId
      }, userId);
      break;

    case 'typing_stop':
      broadcastToProject(projectId, {
        type: 'typing_stop',
        userId: userId,
        projectId: projectId
      }, userId);
      break;

    case 'mark_read':
      await handleMarkMessagesRead(message.messageIds, projectId, userId);
      break;

    default:
      ws.send(JSON.stringify({
        type: 'error',
        message: 'Unknown message type'
      }));
  }
}

async function handleSendMessage(messageData: any, projectId: string, userId: string) {
  try {
    // Import here to avoid circular dependencies
    const { prisma } = require('@/lib/prisma');
    
    if (!prisma) {
      throw new Error('Database not available');
    }

    // Get project details to determine receiver and bid context
    const project = await prisma.project.findUnique({
      where: { id: projectId },
      include: {
        bids: {
          where: {
            OR: [
              { freelancerId: userId },
              { id: messageData.bidId }
            ]
          },
          include: {
            freelancer: {
              select: {
                id: true,
                email: true,
                image: true,
                profile: {
                  select: {
                    firstName: true,
                    lastName: true
                  }
                }
              }
            }
          }
        }
      }
    });

    if (!project) {
      throw new Error('Project not found');
    }

    // Determine receiver based on sender
    let receiverId: string;
    let targetBidId: string | null = null;

    if (userId === project.clientId) {
      // Client is sending - send to freelancer of the specific bid
      const targetBid = project.bids.find((b: any) => b.id === messageData.bidId);
      if (!targetBid) {
        throw new Error('Target bid not found');
      }
      receiverId = targetBid.freelancerId;
      targetBidId = targetBid.id;
    } else {
      // Freelancer is sending - send to client
      const freelancerBid = project.bids.find((b: any) => b.freelancerId === userId);
      if (!freelancerBid) {
        throw new Error('Freelancer bid not found');
      }
      receiverId = project.clientId;
      targetBidId = freelancerBid.id;
    }

    // Save message to database
    const savedMessage = await prisma.message.create({
      data: {
        content: messageData.content,
        senderId: userId,
        receiverId: receiverId,
        projectId: projectId,
        bidId: targetBidId,
        messageType: messageData.messageType || 'text'
      },
      include: {
        sender: {
          select: {
            id: true,
            email: true,
            image: true,
            profile: {
              select: {
                firstName: true,
                lastName: true
              }
            }
          }
        }
      }
    });

    // Format message for WebSocket broadcast
    const senderName = savedMessage.sender.profile 
      ? `${savedMessage.sender.profile.firstName || ''} ${savedMessage.sender.profile.lastName || ''}`.trim() || 'User'
      : 'User';
    
    const senderRole = savedMessage.senderId === project.clientId ? 'client' : 'freelancer';

    const formattedMessage = {
      id: savedMessage.id,
      projectId: savedMessage.projectId,
      senderId: savedMessage.senderId,
      senderName: senderName,
      senderAvatar: savedMessage.sender.image || '/assets/image/default-avatar.png',
      senderRole: senderRole,
      content: savedMessage.content,
      messageType: savedMessage.messageType,
      timestamp: new Date(savedMessage.createdAt).toLocaleTimeString('en-US', { 
        hour: '2-digit', 
        minute: '2-digit' 
      }),
      isRead: savedMessage.isRead,
      bidId: savedMessage.bidId
    };

    // Broadcast message only to participants of this specific bid thread
    broadcastToBidThread(projectId, targetBidId, {
      type: 'new_message',
      message: formattedMessage
    });

  } catch (error) {
    console.error('Error sending message:', error);
    throw error;
  }
}

async function handleMarkMessagesRead(messageIds: string[], projectId: string, userId: string) {
  try {
    // TODO: Update database to mark messages as read
    
    // Broadcast read status to other participants
    broadcastToProject(projectId, {
      type: 'message_read',
      messageIds: messageIds,
      userId: userId
    }, userId);

  } catch (error) {
    console.error('Error marking messages as read:', error);
  }
}

function broadcastToProject(projectId: string, message: any, excludeUserId?: string) {
  const connections = projectConnections.get(projectId);
  if (!connections) return;

  connections.forEach((ws: any) => {
    if (ws.readyState === 1) { // WebSocket.OPEN
      try {
        ws.send(JSON.stringify(message));
      } catch (error) {
        console.error('Error broadcasting message:', error);
      }
    }
  });
}

// Broadcast to specific bid thread participants only
function broadcastToBidThread(projectId: string, bidId: string | null, message: any) {
  if (!bidId) {
    // If no bidId, fall back to project broadcast
    broadcastToProject(projectId, message);
    return;
  }

  const connections = projectConnections.get(projectId);
  if (!connections) return;

  connections.forEach((ws: any) => {
    // Only send to connections that are listening to this specific bid thread
    if (ws.readyState === 1 && (ws.bidId === bidId || ws.bidId === null)) {
      try {
        ws.send(JSON.stringify(message));
      } catch (error) {
        console.error('Error broadcasting message to bid thread:', error);
      }
    }
  });
}

// Helper function to send message to specific user
function sendToUser(userId: string, message: any) {
  const ws = userConnections.get(userId);
  if (ws && ws.readyState === 1) {
    try {
      ws.send(JSON.stringify(message));
    } catch (error) {
      console.error('Error sending message to user:', error);
    }
  }
}

// Cleanup function to remove stale connections
setInterval(() => {
  projectConnections.forEach((connections, projectId) => {
    connections.forEach((ws: any) => {
      if (ws.readyState !== 1) { // Not WebSocket.OPEN
        connections.delete(ws);
      }
    });
    
    if (connections.size === 0) {
      projectConnections.delete(projectId);
    }
  });
}, 30000); // Clean up every 30 seconds