/*
 * Copyright (c) 2022.
 * Author Peter Placzek (tada5hi)
 * For the full copyright and license information,
 * view the LICENSE file that was distributed with this source code.
 */

import {MemorySocketEventMessage} from "../../../../modules/memory-event-bus/socket";
import {SocketInterface} from "../../../../modules/socket";
import {ChatMessage} from "@chamesu/common/domains/chat/message";
import {RecordEvent, RecordEventOptions} from "../../../../modules/socket/event";

export type RoomSubscribeEvent = 'users' | 'messages';

function createMessageMemoryTemplate(roomId: string, message: Partial<ChatMessage>, event: string) : MemorySocketEventMessage {
    return  {
        event,
        payload: {
            id: message.id,
            type: 'chatMessage',
            meta: {
                roomId
            },
            data: message
        }
    };
}

export interface RoomEventOptions extends RecordEventOptions {
    socketRoomKey: string,
    socketUserKey: string,
    socketSubscriptionKey: string
}

export class RoomEvent extends RecordEvent {
    constructor(public readonly options : RoomEventOptions) {
        super(options);
    }

    //--------------------------------------------------------------------

    public emitMessageCreated(roomId: string, message: ChatMessage) {
        let redisMessage : MemorySocketEventMessage = createMessageMemoryTemplate(roomId, message, 'messageCreated');
        redisMessage.room = this.getFullSocketRoomSubscriptionKey(roomId, 'messages');

        this.publish(redisMessage);
    }

    public emitMessageUpdated(roomId: string, message: ChatMessage) {
        let redisMessage : MemorySocketEventMessage = createMessageMemoryTemplate(roomId, message, 'messageUpdated');
        redisMessage.room = this.getFullSocketRoomSubscriptionKey(roomId, 'messages');

        this.publish(redisMessage);
    }

    public emitMessageDeleted(roomId: string, message: ChatMessage) {
        let redisMessage : MemorySocketEventMessage = createMessageMemoryTemplate(roomId, message, 'messageDeleted');
        redisMessage.room = this.getFullSocketRoomSubscriptionKey(roomId, 'messages');

        this.publish(redisMessage);
    }

    //--------------------------------------------------------------------

    public emitConnectionClose(roomId: string, socketId: string) {
        console.log('connection close' + socketId);

        let redisMessage : MemorySocketEventMessage = {
            room: socketId,
            event: 'connectionClose',
            payload: {
                type: 'room',
                id: roomId
            }
        }

        this.publish(redisMessage);
    }

    public emitUserOnline(roomId: string, userId: number) {
        let redisMessage : MemorySocketEventMessage = {
            room: this.getFullSocketRoomSubscriptionKey(roomId, 'users'),
            event: 'userOnline',
            payload: {
                id: userId,
                type: 'user',
                meta: {
                    roomId,
                    firstSeen: Date.now()
                }
            }
        };

        this.publish(redisMessage);
    }

    public async emitUserOffline(roomId: string, userId: number) {
        let redisMessage : MemorySocketEventMessage = {
            room: this.getFullSocketRoomSubscriptionKey(roomId, 'users'),
            event: 'userOffline',
            payload: {
                id: userId,
                type: 'user',
                meta: {
                    roomId
                }
            }
        };

        this.publish(redisMessage);
    }

    public async emitUserMessageTyping(roomId: string, userId: number) {
        let redisMessage : MemorySocketEventMessage = {
            room: this.getFullSocketRoomSubscriptionKey(roomId, 'messages'),
            event: 'userTyping',
            payload: {
                id: userId,
                type: 'user',
                meta: {
                    roomId
                }
            }
        };

        this.publish(redisMessage);
    }

    //--------------------------------------------------------------------

    public subscribeAsSocket(socket: SocketInterface, roomId: string, event: RoomSubscribeEvent) {
        const subscriptionKey = this.getFullSocketRoomSubscriptionKey(roomId, event);

        socket.join(subscriptionKey);
    }

    public unsubscribeAsSocket(socket: SocketInterface, roomId: string, event: RoomSubscribeEvent) {
        const subscriptionKey = this.getFullSocketRoomSubscriptionKey(roomId, event);

        socket.leave(subscriptionKey);
    }

    //--------------------------------------------------------------------

    public getFullSocketUserKey(userId: number) {
        return this.options.socketUserKey + ':' + userId;
    }

    public getFullSocketRoomKey(roomId: string) {
        return this.options.socketRoomKey + ':' + roomId;
    }

    public getFullSocketRoomUserKey(roomId: string, userId: number) {
        return this.getFullSocketRoomKey(roomId) + ':' + this.getFullSocketUserKey(userId);
    }

    public getFullSocketSubscriptionKey(event: RoomSubscribeEvent) {
        return this.options.socketSubscriptionKey + ':' + event;
    }

    public getFullSocketRoomSubscriptionKey(roomId: string, event: RoomSubscribeEvent) {
        return this.getFullSocketRoomKey(roomId) + ':' + this.getFullSocketSubscriptionKey(event);
    }
}

let roomEvent : RoomEvent | undefined;

export function useChatRoomEvent(socketNamespaceName?: string) : RoomEvent {
    if(typeof roomEvent !== 'undefined') {
        return roomEvent;
    }

    if(typeof socketNamespaceName == 'undefined') {
        throw new Error('No namespace name injected in room event handlder...');
    }

    roomEvent = new RoomEvent({
        socketNamespace: socketNamespaceName,
        socketRoomKey: 'room',
        socketUserKey: 'user',
        socketSubscriptionKey: 'subscription'
    });

    return roomEvent;
}
