import CompaniesManager from "../Companies/Companies";
import {
    CreateSystemNotificationParams,
    DeleteNotificationsByDateParams,
    DeleteNotificationsByUserParams,
    DeleteSystemNotificationParams,
    DeleteSystemNotificationsByConversation,
    FindSystemNotificationsParams,
    ISystemNotification,
    ISystemNotificationsManager,
    PaginateSystemNotificationsParams,
    PaginateSystemNotificationsResults,
    ToggleStateSystemNotificationsParams,
    UnreadNotificationResults,
    UpdateSystemNotificationParams,
} from "./SystemNotification.Interface";
import { SystemNotificationsAdapterMongoose } from "./SystemNotification.Adapter.Mongoose";
import { io } from "../../index";
import MessageConversationList from "../MessageConversations/MessagesConversations";
import { FilterQuery } from "mongoose";
import { validateSessionUser } from "../../helpers/check";

class SystemNotificationsManager implements ISystemNotificationsManager {
    notifications: SystemNotificationsAdapterMongoose;

    constructor() {
        this.notifications = new SystemNotificationsAdapterMongoose();
    }

    public async setStateByUser(data: ToggleStateSystemNotificationsParams): Promise<void> {
        await validateSessionUser({
            sessionUser: data.sessionUser,
        });

        await this.notifications.notificationsList.updateMany(
            { userId: data.sessionUser },
            { $set: { readed: data.state } }
        );

        const countUnreadNotifications = await this.finUnreadNotifications({
            userId: data.sessionUser,
            sessionUser: data.sessionUser,
        });

        io.to(`user:${data.sessionUser}`).emit("new_system_notification", countUnreadNotifications);
    }

    public async deleteNotificationsByUser(data: DeleteNotificationsByUserParams): Promise<void> {
        await this.notifications.notificationsList.deleteMany({ userId: data.userId });
    }

    public async deleteNotificationsByDate(data: DeleteNotificationsByDateParams): Promise<void> {
        await validateSessionUser({
            sessionUser: data.sessionUser,
        });

        if (data.sessionUser !== data.userId) {
            throw new Error("No tienes permiso para eliminar estas notificaciones");
        }

        const deleteFilter: FilterQuery<ISystemNotification> = { userId: data.userId };

        if (data.dateFrom || data.dateTo) {
            deleteFilter.notificationDate = {};
        }

        if (data.dateFrom) {
            deleteFilter.notificationDate.$gte = data.dateFrom;
        }

        if (data.dateTo) {
            deleteFilter.notificationDate.$lte = data.dateTo;
        }

        await this.notifications.deleteByQuery(deleteFilter);

        const countUnreadNotifications = await this.finUnreadNotifications({
            userId: data.sessionUser,
            sessionUser: data.sessionUser,
        });

        io.to(`user:${data.sessionUser}`).emit("new_system_notification", countUnreadNotifications);
    }

    public async createNotification(data: CreateSystemNotificationParams): Promise<ISystemNotification> {
        if (data.userId) {
            await validateSessionUser({
                sessionUser: data.userId,
            });
        }

        if (data.companyId) {
            const companyCheck = await CompaniesManager.companies.findOne({ _id: data.companyId });

            if (!companyCheck) {
                throw new Error("La compañia no existe");
            }
        }

        const newNotification = await this.notifications.create({
            ...data,
            ...{
                notificationDate: new Date(),
                readed: false,
            },
        });

        return newNotification;
    }

    public async updateNotification(data: UpdateSystemNotificationParams): Promise<void> {
        await validateSessionUser({
            sessionUser: data.sessionUser,
        });

        const checkNotification = await this.notifications.notificationsList.findOne({
            _id: data.id,
        });

        if (!checkNotification) {
            throw new Error("Notificación invalida");
        }

        if (String(checkNotification.userId) !== data.sessionUser) {
            throw new Error("No tienes permiso para modificar esta notificación");
        }

        await this.notifications.update(data);

        const countUnreadNotifications = await this.finUnreadNotifications({
            userId: data.sessionUser,
            sessionUser: data.sessionUser,
        });

        io.to(`user:${data.sessionUser}`).emit("new_system_notification", countUnreadNotifications);
    }

    public async deleteNotification(data: DeleteSystemNotificationParams): Promise<void> {
        await validateSessionUser({
            sessionUser: data.sessionUser,
        });

        const checkNotification = await this.notifications.notificationsList.findOne({
            _id: data.id,
        });

        if (!checkNotification) {
            throw new Error("Notificación invalida");
        }

        if (String(checkNotification.userId) !== data.sessionUser) {
            throw new Error("No tienes permiso para eliminar esta notificación");
        }

        await this.notifications.delete(data.id);

        const countUnreadNotifications = await this.finUnreadNotifications({
            userId: data.sessionUser,
            sessionUser: data.sessionUser,
        });

        io.to(`user:${data.sessionUser}`).emit("new_system_notification", countUnreadNotifications);
    }

    public async deleteConversationNotifications(
        data: DeleteSystemNotificationsByConversation
    ): Promise<void> {
        await validateSessionUser({
            sessionUser: data.sessionUser,
        });

        if (!data.conversationId) {
            throw new Error("No se ha seleccionado una conversación");
        }

        const conversation = await MessageConversationList.conversations.findOne({
            _id: data.conversationId,
        });

        if (!conversation) {
            throw new Error("Conversación invalida");
        }

        await this.notifications.deleteMany({
            conversationId: data.conversationId,
            userId: data.sessionUser,
        });

        const countUnreadNotifications = await this.finUnreadNotifications({
            userId: data.sessionUser,
            sessionUser: data.sessionUser,
        });

        io.to(`user:${data.sessionUser}`).emit("new_system_notification", countUnreadNotifications);
    }

    public async paginateNotifications(
        filters: PaginateSystemNotificationsParams
    ): Promise<PaginateSystemNotificationsResults> {
        return await this.notifications.paginate(filters);
    }

    public async finUnreadNotifications(
        data: FindSystemNotificationsParams
    ): Promise<UnreadNotificationResults> {
        await validateSessionUser({
            sessionUser: data.sessionUser,
        });

        const unread = await this.notifications.find({
            userId: data.userId,
            readed: false,
        });

        if (!unread) {
            return { count: 0 };
        }

        return {
            count: unread.length,
        };
    }
}

const SystemNoficationsList = new SystemNotificationsManager();

export default SystemNoficationsList;
