import { Schema, model, Model, FilterQuery } from "mongoose";
import {
    CreateTemplateParams,
    DeleteTemplateParams,
    FindTemplatesParams,
    ITemplate,
    ITemplateDocument,
    ITemplatesAdapter,
    PaginateTemplatesParams,
    PaginateTemplatesResults,
    TemplateTypes,
    UpdateTemplateParams,
} from "./Templates.Interface";

export class TemplatesAdapterMongoose implements ITemplatesAdapter {
    schema: Schema;
    templatesList: Model<ITemplateDocument>;

    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(TemplateTypes), required: true },
            template: { type: String, required: true },
            createdAt: { type: Date, default: Date.now, required: true },
        });
        this.templatesList = model<ITemplateDocument>("Template", this.schema);
    }

    public async create(data: Omit<CreateTemplateParams, "sessionUser">): Promise<ITemplate> {
        return await this.templatesList.create(data);
    }

    public async update(data: Omit<UpdateTemplateParams, "sessionUser">): Promise<ITemplate> {
        await this.templatesList
            .updateOne(
                { _id: data.templateId },
                {
                    name: data.name,
                    code: data.code,
                    type: data.type,
                    template: data.template,
                }
            )
            .exec();

        const updatedTemplate = await this.templatesList.findById(data.templateId).exec();
        if (!updatedTemplate) {
            throw new Error("Error al actualizar la plantilla");
        }

        return updatedTemplate;
    }

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

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

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

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

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

        return searchCriteria;
    }

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

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

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