import { Server, Socket } from 'socket.io';
import jwt from 'jsonwebtoken';
import config from '../config/environment';
import { messengerService } from '../services/messengerService';
import { JwtPayload } from '../types/auth';

interface AuthenticatedSocket extends Socket {
  data: {
    userId: string;
    userName: string;
    companyCode: string;
  };
}

// In-memory presence store: userId → { companyCode, status }
const presenceStore = new Map<string, { companyCode: string; status: 'online' | 'away' }>();

export function initMessengerSocket(io: Server) {
  // JWT authentication middleware
  io.use((socket, next) => {
    const token = socket.handshake.auth?.token || socket.handshake.query?.token;
    if (!token) {
      return next(new Error('Authentication required'));
    }

    try {
      const decoded = jwt.verify(token as string, config.jwt.secret) as JwtPayload;
      socket.data.userId = decoded.userId;
      socket.data.userName = decoded.userName;
      socket.data.companyCode = decoded.companyCode || '';
      next();
    } catch (error) {
      next(new Error('Invalid token'));
    }
  });

  io.on('connection', async (socket: AuthenticatedSocket) => {
    const { userId, companyCode } = socket.data;
    console.log(`[Messenger] User connected: ${userId}`);

    // Join company presence room and broadcast online status
    const presenceRoom = `${companyCode}:presence`;
    socket.join(presenceRoom);
    presenceStore.set(userId, { companyCode, status: 'online' });
    socket.to(presenceRoom).emit('user_status', { userId, status: 'online' });

    // Send current online users list to newly connected socket
    const currentPresence: Record<string, string> = {};
    for (const [uid, info] of presenceStore.entries()) {
      if (info.companyCode === companyCode) {
        currentPresence[uid] = info.status;
      }
    }
    socket.emit('presence_list', currentPresence);

    // set_status: client emits when tab focus changes
    socket.on('set_status', (data: { status: 'online' | 'away' }) => {
      const entry = presenceStore.get(userId);
      if (entry) {
        entry.status = data.status;
        io.to(presenceRoom).emit('user_status', { userId, status: data.status });
      }
    });

    // join_rooms: subscribe to all user's rooms
    socket.on('join_rooms', async () => {
      try {
        const roomIds = await messengerService.getUserRoomIds(userId, companyCode);
        for (const roomId of roomIds) {
          socket.join(`${companyCode}:${roomId}`);
        }
        socket.emit('rooms_joined', { roomIds });
      } catch (error) {
        console.error('[Messenger] join_rooms error:', error);
        socket.emit('error', { message: 'Failed to join rooms' });
      }
    });

    // send_message: save and broadcast
    socket.on('send_message', async (data: {
      room_id: number;
      content: string;
      message_type?: string;
      parent_message_id?: number;
    }) => {
      try {
        const message = await messengerService.sendMessage(
          data.room_id,
          userId,
          companyCode,
          data.content,
          data.message_type || 'text',
          data.parent_message_id
        );
        io.to(`${companyCode}:${data.room_id}`).emit('new_message', message);
      } catch (error) {
        console.error('[Messenger] send_message error:', error);
        socket.emit('error', { message: 'Failed to send message' });
      }
    });

    // message_read: update last_read_at
    socket.on('message_read', async (data: { room_id: number }) => {
      try {
        await messengerService.markAsRead(data.room_id, userId);
        io.to(`${companyCode}:${data.room_id}`).emit('user_read', {
          room_id: data.room_id,
          user_id: userId,
          read_at: new Date().toISOString(),
        });
      } catch (error) {
        console.error('[Messenger] message_read error:', error);
      }
    });

    // typing indicators
    socket.on('typing_start', (data: { room_id: number }) => {
      socket.to(`${companyCode}:${data.room_id}`).emit('user_typing', {
        room_id: data.room_id,
        user_id: userId,
        user_name: socket.data.userName,
      });
    });

    socket.on('typing_stop', (data: { room_id: number }) => {
      socket.to(`${companyCode}:${data.room_id}`).emit('user_stop_typing', {
        room_id: data.room_id,
        user_id: userId,
        user_name: socket.data.userName,
      });
    });

    // reactions
    socket.on('add_reaction', async (data: { message_id: number; emoji: string; room_id: number }) => {
      try {
        await messengerService.addReaction(data.message_id, userId, data.emoji);
        io.to(`${companyCode}:${data.room_id}`).emit('reaction_added', {
          message_id: data.message_id,
          user_id: userId,
          emoji: data.emoji,
        });
      } catch (error) {
        console.error('[Messenger] add_reaction error:', error);
      }
    });

    socket.on('remove_reaction', async (data: { message_id: number; emoji: string; room_id: number }) => {
      try {
        await messengerService.removeReaction(data.message_id, userId, data.emoji);
        io.to(`${companyCode}:${data.room_id}`).emit('reaction_removed', {
          message_id: data.message_id,
          user_id: userId,
          emoji: data.emoji,
        });
      } catch (error) {
        console.error('[Messenger] remove_reaction error:', error);
      }
    });

    // join a specific room (e.g., after creating a new room)
    socket.on('join_room', (data: { room_id: number }) => {
      socket.join(`${companyCode}:${data.room_id}`);
    });

    socket.on('disconnect', () => {
      console.log(`[Messenger] User disconnected: ${userId}`);
      presenceStore.delete(userId);
      io.to(presenceRoom).emit('user_status', { userId, status: 'offline' });
    });
  });
}
