import mongoose, { Document, FilterQuery, Model, Schema, model } from "mongoose";
import {
    CreateSystemNotificationParams,
    DeleteSystemNotificationsByConversation,
    FindSystemNotificationsParams,
    ISystemNotification,
    ISystemNotificationsAdapter,
    PaginateSystemNotificationsParams,
    PaginateSystemNotificationsResults,
    UpdateSystemNotificationParams,
} from "./SystemNotification.Interface";

export interface ISystemNotificationDocument extends Omit<ISystemNotification, "id">, Document {}

export class SystemNotificationsAdapterMongoose implements ISystemNotificationsAdapter {
    schema: Schema;
    notificationsList: Model<ISystemNotificationDocument>;

    constructor() {
        this.schema = new Schema({
            userId: { type: Schema.Types.ObjectId, required: false, ref: "User" },
            companyId: { type: Schema.Types.ObjectId, required: false, ref: "Companie" },
            serviceId: { type: Schema.Types.ObjectId, required: false, ref: "Service" },
            conversationId: {
                type: Schema.Types.ObjectId,
                required: false,
                ref: "MessagesConversation",
            },
            subject: { type: String, required: true },
            message: { type: String, required: true },
            notificationDate: { type: Date, required: true },
            readed: { type: Boolean, default: false },
        });

        this.notificationsList = model<ISystemNotificationDocument>("SystemNotification", this.schema);
    }

    public async create(data: CreateSystemNotificationParams): Promise<ISystemNotification> {
        return await this.notificationsList.create(data);
    }

    public async update(data: UpdateSystemNotificationParams): Promise<void> {
        const updateNotification = await this.notificationsList.findOne({ _id: data.id });

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

        updateNotification.readed = data.readed;

        await updateNotification.save();
    }

    public async delete(id: string): Promise<void> {
        await this.notificationsList.findByIdAndDelete(id).exec();
    }

    public async deleteMany(
        data: Omit<DeleteSystemNotificationsByConversation, "sessionUser">
    ): Promise<void> {
        await this.notificationsList.deleteMany(data).exec();
    }

    public async deleteByQuery(data: FilterQuery<ISystemNotification>): Promise<void> {
        await this.notificationsList.deleteMany(data).exec();
    }

    public async find(
        filters: Omit<FindSystemNotificationsParams, "sessionUser">
    ): Promise<ISystemNotification[]> {
        return this.notificationsList.find(filters).exec();
    }

    public async findOne(
        filters: FindSystemNotificationsParams
    ): Promise<ISystemNotificationDocument | null> {
        return this.notificationsList.findOne(filters).exec();
    }

    // Método para búsqueda con expresiones regulares y paginación
    public async paginate(
        data: PaginateSystemNotificationsParams
    ): Promise<PaginateSystemNotificationsResults> {
        const skip = (data.page - 1) * data.limit;

        // Construye los criterios de búsqueda
        const searchCriteria: mongoose.FilterQuery<ISystemNotification> = {};

        // Agrega filtro por query usando expresiones regulares para 'name' y 'description'
        if (data.query) {
            const regex = new RegExp(data.query, "i");
            searchCriteria.$or = [{ subject: regex }, { message: regex }];
        }

        // Filtra por categoryId si está presente
        if (data.userId !== undefined) {
            searchCriteria.userId = data.userId;
        }

        if (data.companyId !== undefined) {
            searchCriteria.companyId = data.companyId;
        }

        if (data.conversationId !== undefined) {
            searchCriteria.conversationId = data.conversationId;
        }

        if (data.serviceId !== undefined) {
            searchCriteria._id = data.serviceId;
        }

        if (data.readed) {
            searchCriteria.readed = data.readed;
        }

        // Realiza la consulta con paginación
        const results = await this.notificationsList
            .find(searchCriteria)
            .sort({ notificationDate: -1 })
            .skip(skip)
            .limit(data.limit)
            .exec();

        // Cuenta el total de documentos que coinciden con los criterios de búsqueda
        const count = await this.notificationsList.countDocuments(searchCriteria).exec();

        return {
            data: results,
            page: data.page,
            pages: Math.ceil(count / data.limit),
        };
    }
}
