import { Schema, model, Model, FilterQuery } from "mongoose";
import {
    CreateDiscountEmailParams,
    DeleteDiscountEmailParams,
    FindDiscountsEmailParams,
    IDiscountEmail,
    IDiscountEmailDocument,
    IDiscountsEmailAdapter,
    PaginateDiscountsEmailParams,
    PaginateDiscountsEmailResults,
} from "./DiscountsEmail.Interface";

export class DiscountsEmailAdapterMongoose implements IDiscountsEmailAdapter {
    private schema: Schema;
    private discountsEmailList: Model<IDiscountEmailDocument>;

    constructor() {
        this.schema = new Schema({
            companyId: {
                type: Schema.Types.ObjectId,
                required: true,
                ref: "Company",
            },
            discountId: {
                type: Schema.Types.ObjectId,
                required: true,
                ref: "Discount",
            },
            email: { type: String, required: true },
            startDate: { type: Date, required: true },
            endDate: { type: Date, required: true },
            createdAt: { type: Date, default: Date.now, required: true },
        });
        this.discountsEmailList = model<IDiscountEmailDocument>("DiscountsEmail", this.schema);
    }

    public async create(data: Omit<CreateDiscountEmailParams, "sessionUser">): Promise<IDiscountEmail> {
        await this.cleanUp();
        return await this.discountsEmailList.create(data);
    }

    public async delete(data: Omit<DeleteDiscountEmailParams, "sessionUser">): Promise<void> {
        await this.cleanUp();
        await this.discountsEmailList.deleteOne({ _id: data.id });
    }

    public async find(filters: Omit<FindDiscountsEmailParams, "sessionUser">): Promise<IDiscountEmail[]> {
        await this.cleanUp();
        const searchCriteria = this.buildSearchCriteria(filters);
        return await this.discountsEmailList.find(searchCriteria).exec();
    }

    public async deleteMany(filters: Omit<FindDiscountsEmailParams, "sessionUser">): Promise<void> {
        await this.cleanUp();
        const searchCriteria = this.buildSearchCriteria(filters);
        await this.discountsEmailList.deleteMany(searchCriteria).exec();
    }

    public async findOne(
        filters: Omit<FindDiscountsEmailParams, "sessionUser">
    ): Promise<IDiscountEmailDocument | null> {
        await this.cleanUp();
        const searchCriteria = this.buildSearchCriteria(filters);
        return await this.discountsEmailList.findOne(searchCriteria).exec();
    }

    private async cleanUp() {
        await this.discountsEmailList.deleteMany({ endDate: { $lt: new Date() } });
    }

    private buildSearchCriteria(
        filters: Omit<FindDiscountsEmailParams, "sessionUser">
    ): FilterQuery<IDiscountEmail> {
        const searchCriteria: FilterQuery<IDiscountEmail> = {};

        if (filters.id) searchCriteria._id = filters.id;
        if (filters.companyId) searchCriteria.companyId = filters.companyId;
        if (filters.discountId) searchCriteria.discountId = filters.discountId;
        if (filters.email) searchCriteria.email = filters.email;

        if (filters.searchDate) {
            searchCriteria.startDate = {
                $lte: filters.searchDate,
            };
            searchCriteria.endDate = {
                $gte: filters.searchDate,
            };
        }

        return searchCriteria;
    }

    public async paginate(filters: PaginateDiscountsEmailParams): Promise<PaginateDiscountsEmailResults> {
        await this.cleanUp();
        const searchCriteria = this.buildSearchCriteria(filters);

        const count = await this.discountsEmailList.countDocuments(searchCriteria).exec();
        const skip = (filters.page - 1) * filters.limit;

        const results = await this.discountsEmailList
            .find(searchCriteria)
            .skip(skip)
            .limit(filters.limit)
            .sort({ createdAt: -1 })
            .exec();

        let pages = Math.ceil(count / filters.limit);
        if (pages == 0) {
            pages = 1;
        }

        return { data: results, page: filters.page, pages: pages };
    }
}
