import { Socket } from 'socket.io';
import Sentry from '../external-services/sentry.js';

type HandlerType = (socket: Socket, message: any) => Promise<void>;
const socketServices: { [key: string]: HandlerType } = {};
const publicSocketServices: { [key: string]: HandlerType } = {};

export enum SocketEvent {
  Progress = 'progress',
  Final = 'final',
  Stream = 'stream',
  Response = 'response',
}

export const socketMessageHandler = async (socket: Socket, message: any) => {
  try {
    if (!message.service) {
      throw new Error('No socket service name provided');
    }

    let handler;
    if (!!socketServices[message.service]) {
      if (!(socket.request as any).user) {
        socket.emit(SocketEvent.Final, { error: true, message: 'Not authenticated' });
        return;
      }
      handler = socketServices[message.service];
    } else {
      handler = publicSocketServices[message.service];
    }

    if (!handler) {
      throw new Error(`No handler found for socket service ${message.service}`);
    }
    await handler(socket, message);
  } catch (e) {
    console.error(e);
    Sentry.captureException(e);
    socket.emit(SocketEvent.Final, {
      error: true,
      message: process.env.ENVIRONMENT === 'production' ? 'Unhandled exception' : e.message,
    });
  }
};

export const bindPublicSocketService = (serviceName: string, handler: HandlerType) => {
  if (publicSocketServices[serviceName]) {
    throw new Error(`Socket service ${serviceName} already bound`);
  }
  publicSocketServices[serviceName] = handler;
};

export const bindSocketService = (serviceName: string, handler: HandlerType) => {
  if (socketServices[serviceName]) {
    throw new Error(`Socket service ${serviceName} already bound`);
  }
  socketServices[serviceName] = handler;
};
