import { Document, Model, Schema, model, FilterQuery } from "mongoose";
import {
    CreateCashFlowParams,
    FindCashFlowParams,
    ICashFlow,
    ICashFlowAdapter,
    PaginateCashFlowParams,
    PaginateCashFlowResults,
} from "./CashFlow.Interface";
import { PAYMENT_PROCCESS_DATE } from "../Payments/Payments.Interface";
import AppointmentList from "../Appointments/Appointments";

export interface ICashFlowDocument extends Omit<ICashFlow, "id">, Document {}

export class CashFlowAdapterMongoose implements ICashFlowAdapter {
    schema: Schema;
    movementList: Model<ICashFlowDocument>;

    constructor() {
        this.schema = new Schema({
            companyId: {
                type: Schema.Types.ObjectId,
                required: true,
                ref: "Company",
            },
            clientId: {
                type: Schema.Types.ObjectId,
                required: false,
                ref: "Client",
            },
            clientAccountMovementId: {
                type: Schema.Types.ObjectId,
                required: false,
                ref: "ClientAccountMovement",
            },
            paymentId: {
                type: Schema.Types.ObjectId,
                required: false,
                ref: "Payment",
            },
            appointmentId: {
                type: Schema.Types.ObjectId,
                required: false,
                ref: "Appointment",
            },
            employeeId: {
                type: Schema.Types.ObjectId,
                required: false,
                ref: "Employee",
            },
            amount: { type: Number, required: true, default: 0 },
            description: { type: String, required: false, default: "" },
            year: { type: Number, required: true, default: 0 },
            month: { type: Number, required: true, default: 0 },
            type: { type: String, required: true, default: 0 },
            reference: { type: String, required: false, default: "" },
            paymentMethod: { type: String, required: false },
            createdAt: { type: Date, required: true, default: Date.now },
        });

        this.movementList = model<ICashFlowDocument>("CashFlow", this.schema);
    }

    public async create(data: CreateCashFlowParams): Promise<ICashFlow> {
        let creationDate = new Date();

        if (data.proccessDate == PAYMENT_PROCCESS_DATE.NOW) {
            creationDate = new Date();
        } else if (data.proccessDate == PAYMENT_PROCCESS_DATE.APPOINTMENT) {
            const appointment = await AppointmentList.Appointments.AppointmentList.findOne({
                _id: data.appointmentId,
            });
            if (!appointment) {
                throw new Error("No se encontró la reserva");
            }
            creationDate = new Date(appointment.start);
        }

        return await this.movementList.create({ ...data, createdAt: creationDate });
    }

    public async delete(id: string): Promise<void> {
        await this.movementList.deleteOne({ _id: id });
    }

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

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

        if (filters.year) {
            searchCriteria.year = filters.year;
        }

        if (filters.month) {
            searchCriteria.month = filters.month;
        }

        if (filters.type) {
            searchCriteria.type = filters.type;
        }

        if (filters.reference) {
            searchCriteria.reference = filters.reference;
        }

        if (filters.clientId) {
            searchCriteria.clientId = filters.clientId;
        }

        if (filters.clientAccountMovementId) {
            searchCriteria.clientAccountMovementId = filters.clientAccountMovementId;
        }

        if (filters.companyId) {
            searchCriteria.companyId = filters.companyId;
        }

        if (filters.paymentId) {
            searchCriteria.paymentId = filters.paymentId;
        }

        if (filters.appointmentId) {
            searchCriteria.appointmentId = filters.appointmentId;
        }

        if (filters.employeeId) {
            searchCriteria.employeeId = filters.employeeId;
        }

        if (filters.paymentMethod) {
            searchCriteria.paymentMethod = filters.paymentMethod;
        }

        return searchCriteria;
    }

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

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

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

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

    public async find(filters: Omit<FindCashFlowParams, "sessionUser">): Promise<ICashFlow[]> {
        const searchCriteria = this.buildSearchCriteria(filters);
        return this.movementList.find(searchCriteria).exec();
    }

    public async findOne(
        filters: Omit<FindCashFlowParams, "sessionUser">
    ): Promise<ICashFlowDocument | null> {
        return this.movementList.findOne(filters).exec();
    }
}
