import UsersManager from "../Users/Users";
import {
    DeleteMessageParams,
    FindMessageParams,
    IMessageManager,
    ISendMessageResult,
    PaginateMessageParams,
    PaginateMessageResults,
    SendMessageParams,
} from "./Messages.Interface";
import { MessagesAdapterMongoose } from "./Messages.Adapter.Mongoose";
import {
    IMessageConversation,
    MESSAGE_CONVERSATION_TYPES,
} from "../MessageConversations/MessagesConversations.Interface";
import MessageConversationList from "../MessageConversations/MessagesConversations";
import { NotificationsManager } from "../Notifications/Notifications";
import EmployeesList from "../Employees/Employee";
import CompaniesList from "../Companies/Companies";
import { NotificationType } from "../../Models/SystemNotifications/SystemNotification.Interface";

class MessageManager implements IMessageManager {
    messages: MessagesAdapterMongoose;

    constructor() {
        this.messages = new MessagesAdapterMongoose();
    }

    public async sendMessage(data: SendMessageParams): Promise<ISendMessageResult> {
        /*
         * Cuando envio un mensaje tengo dos opciones:
         * 1. Enviar un mensaje a una conversacion existente
         *   - En este caso debo proporcionar un conversationId.
         * 2. Crear una nueva conversacion
         *    - En este caso debo proporcionar el tipo de conversacion que se va ha crear
         *      y los datos necesarios para crearla.
         */

        if (data.sessionUser) {
            const userCheck = await UsersManager.users.findOne({ _id: data.sessionUser });
            if (!userCheck) {
                throw new Error("Usuario invalido");
            }
        }

        let conversation: IMessageConversation | null = null;

        if (data.conversationId) {
            conversation = await MessageConversationList.conversations.findOne({
                _id: data.conversationId,
            });
            if (!conversation) {
                throw new Error("Conversacion invalida");
            }
        }

        if (!data.conversationId) {
            if (!data.type) {
                throw new Error("El tipo de la conversación es requerido");
            }

            if (data.type === MESSAGE_CONVERSATION_TYPES.USER_TO_USER) {
                conversation = await MessageConversationList.conversations.findOne({
                    userIdFrom: data.sessionUser,
                    userIdTo: data.userIdTo,
                });

                if (!conversation) {
                    conversation = await MessageConversationList.createConversation({
                        userIdFrom: data.sessionUser,
                        userIdTo: data.userIdTo,
                        type: MESSAGE_CONVERSATION_TYPES.USER_TO_USER,
                        sessionUser: data.sessionUser,
                    });
                }
            } else if (data.type === MESSAGE_CONVERSATION_TYPES.USER_TO_ORG) {
                conversation = await MessageConversationList.conversations.findOne({
                    userIdFrom: data.sessionUser,
                    companyId: data.companyId,
                });

                if (!conversation) {
                    conversation = await MessageConversationList.createConversation({
                        userIdFrom: data.sessionUser,
                        companyId: data.companyId,
                        type: MESSAGE_CONVERSATION_TYPES.USER_TO_ORG,
                        sessionUser: data.sessionUser,
                    });
                }
            }
        }

        if (!conversation) {
            throw new Error("No se pudo crear la conversacion");
        }

        if (!conversation.id) {
            throw new Error("No se pudo crear la conversacion");
        }

        //Cuando alguien contesta la conversacion se pasa a user to user.
        //De esta manera el userTo pasa a ser el que contesto la conversacion (data.sessionUser).
        if (
            conversation.companyId &&
            !conversation.userIdTo &&
            conversation.userIdFrom != data.sessionUser
        ) {
            //Convertir la conversation en user to user
            await MessageConversationList.convertUserToUser({
                conversationId: conversation.id,
                sessionUser: data.sessionUser,
            });
        }

        const newMessage = await this.messages.create({
            conversationId: conversation.id,
            message: data.message,
            sessionUser: data.sessionUser,
        });

        const shortMessage =
            data.message.length > 100 ? data.message.substring(0, 100) + "..." : data.message;

        if (conversation.userIdTo) {
            const toUser =
                String(data.sessionUser) === String(conversation.userIdTo)
                    ? conversation.userIdFrom
                    : conversation.userIdTo;
            await NotificationsManager.sendSystemNotification({
                userId: String(toUser),
                conversationId: conversation.id,
                subject: "Nuevo mensaje recibido",
                message: shortMessage,
                type: NotificationType.MESSAGE,
                code: String((newMessage as any)._id)
            });
        }

        if (!conversation.userIdTo && conversation.companyId) {
            const org = await CompaniesList.companies.findOne({
                _id: conversation.companyId,
            });

            if (org) {
                const employees = await EmployeesList.employees.find({
                    companyId: conversation.companyId,
                });

                for (const employee of employees) {
                    await NotificationsManager.sendSystemNotification({
                userId: String(employee.userId),
                        conversationId: conversation.id,
                subject: "Nueva consulta a la organización " + org.name,
                message: shortMessage,
                type: NotificationType.MESSAGE,
                code: String((newMessage as any)._id)
            });
                }
            }
        }

        return {
            message: newMessage,
            conversation: {
                id: conversation.id,
                userIdFrom: conversation.userIdFrom,
                userIdTo: conversation.userIdTo,
                companyId: conversation.companyId,
                type: conversation.type,
                newMessages: conversation.newMessages,
            },
        };
    }

    public async deleteMessage(data: DeleteMessageParams): Promise<void> {
        const checkUser = await UsersManager.users.findOne({ _id: data.sessionUser });
        if (!checkUser) {
            throw new Error("Usuario invalido");
        }

        const messacheCheck = await this.messages.messageList.findOne({
            _id: data.id,
        });

        if (!messacheCheck) {
            throw new Error("Mensaje invalido");
        }

        if (String(messacheCheck.userId) !== data.sessionUser) {
            throw new Error("No tienes permiso para eliminar este mensaje");
        }

        await this.messages.delete(data.id);
    }

    public async paginateMessages(filters: PaginateMessageParams): Promise<PaginateMessageResults> {
        return await this.messages.paginate(filters);
    }

    public async markAsRead(data: FindMessageParams): Promise<void> {
        const checkUser = await UsersManager.users.findOne({ _id: data.sessionUser });
        if (!checkUser) {
            throw new Error("Usuario invalido");
        }
        this.messages.markAsRead(data);
    }
}

const MessagesList = new MessageManager();

export default MessagesList;
