import {
    IDiscount,
    IDiscountDocument,
    IDiscountsAdapter,
    DiscountType,
    CreateDiscountParams,
    DeleteDiscountParams,
    FindDiscountsParams,
    PaginateDiscountsParams,
    PaginateDiscountsResults,
} from "./Discounts.Interface";
import { Schema, model, Model, FilterQuery } from "mongoose";

export class DiscountsAdapterMongoose implements IDiscountsAdapter {
    schema: Schema;
    discountsList: Model<IDiscountDocument>;

    constructor() {
        this.schema = new Schema({
            companyId: {
                type: Schema.Types.ObjectId,
                required: true,
                ref: "Company",
            },
            name: { type: String, required: true },
            code: { type: String, required: true },
            type: { type: String, enum: Object.values(DiscountType), required: true },
            value: { type: Number, required: true },
            createdAt: { type: Date, default: Date.now, required: true },
        });
        this.discountsList = model<IDiscountDocument>("Discount", this.schema);
    }

    public async create(data: Omit<CreateDiscountParams, "sessionUser">): Promise<IDiscount> {
        return await this.discountsList.create(data);
    }

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

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

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

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

        if (filters.id) searchCriteria._id = filters.id;
        if (filters.companyId) searchCriteria.companyId = filters.companyId;
        if (filters.code) searchCriteria.code = { $regex: filters.code, $options: "i" };
        if (filters.type) searchCriteria.type = filters.type;

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

        if (filters.startValue !== undefined && filters.endValue !== undefined) {
            searchCriteria.value = {
                $gte: filters.startValue,
                $lte: filters.endValue,
            };
        }

        return searchCriteria;
    }

    public async paginate(filters: PaginateDiscountsParams): Promise<PaginateDiscountsResults> {
        const searchCriteria = this.buildSearchCriteria(filters);

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

        const results = await this.discountsList
            .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 };
    }
}
