import { Server as HttpServer } from 'http';
import { Server, Socket } from 'socket.io';
import { verifyAccessToken } from './lib/jwt';
import prisma from './lib/prisma';
import { getConversationRecipientIds } from './services/notification.service';
import { SOCKET_EVENTS, JOB_FEED_ROOM, JobReactionType } from '@ehandyhire/shared';
import { setJobReaction } from './services/jobReactions.service';
import { assertCustomerVerified } from './lib/customerVerification';
import { touchLastActive, chatUserSelect } from './lib/lastActive';

let io: Server;

const isDev = process.env.NODE_ENV !== 'production';
const corsOrigin = process.env.CORS_ORIGIN || 'http://localhost:3000';

export function initSocket(httpServer: HttpServer): Server {
  io = new Server(httpServer, {
    cors: {
      origin: isDev
        ? (origin, callback) => {
            if (!origin || origin.startsWith('http://localhost:')) {
              callback(null, true);
            } else {
              callback(new Error('Not allowed by CORS'));
            }
          }
        : corsOrigin,
      credentials: true,
    },
    transports: ['websocket', 'polling'],
  });

  io.use(async (socket, next) => {
    try {
      const token = socket.handshake.auth?.token || socket.handshake.query?.token;
      if (token) {
        const payload = verifyAccessToken(String(token));
        socket.data.user = payload;
      }
      next();
    } catch {
      // Allow unauthenticated connections for public events
      next();
    }
  });

  io.on('connection', (socket: Socket) => {
    const userId = socket.data.user?.userId;
    if (userId) {
      socket.join(`user:${userId}`);
      void touchLastActive(userId);
      console.log(`User ${userId} connected`);
    }

    socket.on(SOCKET_EVENTS.JOIN_ROOM, (room: string) => {
      socket.join(room);
    });

    socket.on(SOCKET_EVENTS.LEAVE_ROOM, (room: string) => {
      socket.leave(room);
    });

    // Join conversation room
    socket.on('join-conversation', (conversationId: string) => {
      socket.join(`conversation:${conversationId}`);
    });

    // Join category room for job notifications
    socket.on('join-category', (categoryId: string) => {
      socket.join(`category:${categoryId}`);
    });

    socket.on(SOCKET_EVENTS.JOIN_JOB_FEED, () => {
      socket.join(JOB_FEED_ROOM);
    });

    socket.on(SOCKET_EVENTS.LEAVE_JOB_FEED, () => {
      socket.leave(JOB_FEED_ROOM);
    });

    socket.on(
      SOCKET_EVENTS.SET_JOB_REACTION,
      async (data: { jobId: string; type: JobReactionType }) => {
        if (!userId || socket.data.user?.role !== 'WORKER') return;
        if (!data?.jobId || !data?.type) return;

        try {
          const payload = await setJobReaction(data.jobId, userId, data.type);
          io.to(JOB_FEED_ROOM).emit(SOCKET_EVENTS.JOB_REACTION, payload);
        } catch (e) {
          console.error('Job reaction error:', e);
        }
      }
    );

    // Real-time chat
    socket.on(SOCKET_EVENTS.SEND_MESSAGE, async (data: {
      conversationId: string;
      content: string;
    }) => {
      if (!userId) return;

      try {
        if (socket.data.user?.role === 'CUSTOMER') {
          await assertCustomerVerified(userId, 'message');
        }

        const message = await prisma.message.create({
          data: {
            conversationId: data.conversationId,
            senderId: userId,
            content: data.content,
          },
          include: {
            sender: { select: chatUserSelect },
          },
        });

        await prisma.conversation.update({
          where: { id: data.conversationId },
          data: { updatedAt: new Date() },
        });

        io.to(`conversation:${data.conversationId}`).emit(SOCKET_EVENTS.RECEIVE_MESSAGE, message);

        const { recipientIds } = await getConversationRecipientIds(data.conversationId, userId);
        for (const recipientId of recipientIds) {
          io.to(`user:${recipientId}`).emit(SOCKET_EVENTS.RECEIVE_MESSAGE, {
            ...message,
            conversationId: data.conversationId,
          });
        }
      } catch (e) {
        console.error('Send message error:', e);
      }
    });

    // Typing indicator
    socket.on(SOCKET_EVENTS.TYPING, (data: { conversationId: string; isTyping: boolean }) => {
      socket.to(`conversation:${data.conversationId}`).emit(SOCKET_EVENTS.TYPING, {
        userId,
        isTyping: data.isTyping,
      });
    });

    // Read receipts
    socket.on(SOCKET_EVENTS.READ_RECEIPT, async (data: { conversationId: string }) => {
      if (!userId) return;

      await prisma.message.updateMany({
        where: {
          conversationId: data.conversationId,
          senderId: { not: userId },
          isRead: false,
        },
        data: { isRead: true, readAt: new Date() },
      });

      socket.to(`conversation:${data.conversationId}`).emit(SOCKET_EVENTS.READ_RECEIPT, {
        conversationId: data.conversationId,
        userId,
      });
    });

    socket.on('disconnect', () => {
      if (userId) console.log(`User ${userId} disconnected`);
    });
  });

  return io;
}

export function getIO(): Server {
  if (!io) throw new Error('Socket.IO not initialized');
  return io;
}
