import ServiceList from "../Services/Service";
import {
    FindCompaniesParams,
    UpdateCompanyParams,
    ICompaniesAdapter,
    ICompany,
    PaginateCompaniesParams,
    PaginateCompaniesResults,
    SysAdminPaginateCompaniesParams,
} from "./Companies.Interface";
import mongoose, { Document, Model, Schema, model } from "mongoose";

export interface ICompanyDocument extends Omit<ICompany, "id">, Document {}

export class CompaniesAdapterMongoose implements ICompaniesAdapter {
    schema: Schema;
    companyList: Model<ICompanyDocument>;

    constructor() {
        this.schema = new Schema({
            name: { type: String, required: true, unique: true },
            slug: { type: String, required: true, unique: true },
            description: { type: String, required: true, default: "" },
            categoryId: { type: Number, required: false, default: 50000 },

            street: { type: String, required: false },
            streetNumber: { type: String, required: false },
            builingFloor: { type: String, required: false },
            buildingApartament: { type: String, required: false },
            block: { type: String, required: false },
            city: { type: String, required: false },
            state: { type: String, required: false },
            country: { type: String, required: false },
            zipCode: { type: String, required: false },
            latitude: { type: Number, required: false, default: 0 },
            longitude: { type: Number, required: false, default: 0 },

            phoneCountryCode: { type: String, required: false },
            phoneAreaCode: { type: String, required: false },
            phoneNumber: { type: String, required: false },
            heatMapFraction: { type: Number, required: false },

            headerFile: { type: String, required: false },
            headerColor: { type: String, required: false },
            headerFontColor: { type: String, required: false },
            headerFontShadowColor: { type: String, required: false },
            iconFile: { type: String, required: false },
            logoFile: { type: String, required: false },
            wapServerId: { type: Schema.Types.ObjectId, required: false, ref: "WapServer" },
            ownerId: { type: Schema.Types.ObjectId, required: true, ref: "User" },
            creationDate: { type: Date, required: true, default: Date.now },
            published: { type: String, required: false },
            automaticNotifications: { type: Boolean, required: false },

            appointmentTime: { type: Number, required: false },
            cancellationTime: { type: Number, required: false },
            appointmentAlert: { type: String, required: false },

            templateWapNotifId: { type: Schema.Types.ObjectId, required: false, ref: "Template" },
            templateWapAltaId: { type: Schema.Types.ObjectId, required: false, ref: "Template" },
            templateEmailNotifId: { type: Schema.Types.ObjectId, required: false, ref: "Template" },
            templateEmailAltaId: { type: Schema.Types.ObjectId, required: false, ref: "Template" },
            onboardingStep: { type: Number, required: false, default: 0 },
            onboardingCompleted: { type: Boolean, required: false, default: false },

            fixedPostIds: { type: Array, required: false },
            banned: { type: Boolean, required: false, default: false },
        });

        this.companyList = model<ICompanyDocument>("Company", this.schema);
    }

    public async create(company: ICompany): Promise<ICompany> {
        const newCompany = await this.companyList.create({
            ...company,
            onboardingStep: company.onboardingStep || 0
        });
        return newCompany;
    }

    public async update(data: UpdateCompanyParams): Promise<void> {
        const updateCompany = await this.companyList.findOne({ _id: data.id });

        if (!updateCompany) {
            throw new Error("Company not found");
        }

        if (data.name) {
            updateCompany.name = data.name;
        }

        if (data.description) {
            updateCompany.description = data.description;
        }

        if (data.categoryId) {
            updateCompany.categoryId = data.categoryId;
        }

        if (data.headerFile) {
            updateCompany.headerFile = data.headerFile;
        }

        if (data.iconFile) {
            updateCompany.iconFile = data.iconFile;
        }

        if (data.logoFile) {
            updateCompany.logoFile = data.logoFile;
        }

        if (data.headerColor) {
            updateCompany.headerColor = data.headerColor;
        }

        if (data.headerFontColor) {
            updateCompany.headerFontColor = data.headerFontColor;
        }

        if (data.headerFontShadowColor) {
            updateCompany.headerFontShadowColor = data.headerFontShadowColor;
        }

        if (data.street) {
            updateCompany.street = data.street;
        }

        if (data.streetNumber) {
            updateCompany.streetNumber = data.streetNumber;
        }

        if (data.builingFloor) {
            updateCompany.builingFloor = data.builingFloor;
        }

        if (data.buildingApartament) {
            updateCompany.buildingApartament = data.buildingApartament;
        }

        if (data.block) {
            updateCompany.block = data.block;
        }

        if (data.city) {
            updateCompany.city = data.city;
        }

        if (data.state) {
            updateCompany.state = data.state;
        }

        if (data.country) {
            updateCompany.country = data.country;
        }

        if (data.zipCode) {
            updateCompany.zipCode = data.zipCode;
        }

        if (data.latitude) {
            updateCompany.latitude = data.latitude;
        }

        if (data.longitude) {
            updateCompany.longitude = data.longitude;
        }

        if (data.phoneCountryCode) {
            updateCompany.phoneCountryCode = data.phoneCountryCode;
        }

        if (data.phoneAreaCode) {
            updateCompany.phoneAreaCode = data.phoneAreaCode;
        }

        if (data.phoneNumber) {
            updateCompany.phoneNumber = data.phoneNumber;
        }

        if (data.heatMapFraction) {
            updateCompany.heatMapFraction = data.heatMapFraction;
        }

        if (data.appointmentTime) {
            updateCompany.appointmentTime = data.appointmentTime;
        }

        if (data.cancellationTime) {
            updateCompany.cancellationTime = data.cancellationTime;
        }

        if (data.wapServerId) {
            updateCompany.wapServerId = data.wapServerId;
        }

        if (data.appointmentAlert) {
            updateCompany.appointmentAlert = data.appointmentAlert;
        }

        if (data.templateWapNotifId) {
            updateCompany.templateWapNotifId = data.templateWapNotifId;
        }

        if (data.templateWapAltaId) {
            updateCompany.templateWapAltaId = data.templateWapAltaId;
        }

        if (data.templateEmailNotifId) {
            updateCompany.templateEmailNotifId = data.templateEmailNotifId;
        }

        if (data.templateEmailAltaId) {
            updateCompany.templateEmailAltaId = data.templateEmailAltaId;
        }

        if (data.onboardingStep !== undefined) {
            updateCompany.onboardingStep = data.onboardingStep;
        }

        if (data.onboardingCompleted !== undefined) {
            updateCompany.onboardingCompleted = data.onboardingCompleted;
        }

        if (data.banned !== undefined) {
            updateCompany.banned = data.banned;
        }

        updateCompany.save();

        //actualizo los campos correspondientes a la localizacion de los servicios de la organizacion.
        try {
            const servicesUpdate = await ServiceList.services.find({ companyId: updateCompany.id });

            for (const service of servicesUpdate) {
                if (!service.id) {
                    continue;
                }

                await ServiceList.updateService({
                    id: service.id,
                    companyId: updateCompany.id,
                    latitude: updateCompany.latitude,
                    longitude: updateCompany.longitude,
                    block: updateCompany.block,
                    city: updateCompany.city,
                    state: updateCompany.state,
                    country: updateCompany.country,
                    zipCode: updateCompany.zipCode,
                    sessionUser: data.sessionUser,
                });
            }
        } catch (error) {
            console.log(
                "Ha ocurrido un error al actualizar la localizacion de los servicios de la organizacion."
            );
        }
    }

    public async delete(id: string): Promise<void> {
        await this.companyList.deleteOne({ _id: id }).exec();
    }
    public async find(filters: FindCompaniesParams): Promise<ICompany[]> {
        return await this.companyList.find(filters).exec();
    }

    public async findOne(filters: FindCompaniesParams): Promise<ICompanyDocument | null> {
        return await this.companyList.findOne(filters).exec();
    }

    public async paginate(filters: PaginateCompaniesParams): Promise<PaginateCompaniesResults> {
        const { page, limit, ...findFilters } = filters; //Extract page and limit from filters
        const count = await this.companyList.countDocuments(findFilters).exec();
        const skip = (page - 1) * limit;

        const results = await this.companyList
            .find(findFilters)
            .skip(skip)
            .limit(filters.limit)
            .exec();

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

    public async sysAdminPaginate(filters: SysAdminPaginateCompaniesParams): Promise<PaginateCompaniesResults> {
        const { page, limit, sortBy, sortOrder, name, banned } = filters as any;
        const query: any = {};
        if (name) {
            query.name = { $regex: name, $options: "i" };
        }
        if (banned !== undefined) {
            query.banned = banned;
        }

        const count = await this.companyList.countDocuments(query).exec();
        const skip = (page - 1) * limit;

        const sortObj: any = {};
        if (sortBy) {
            sortObj[sortBy] = sortOrder === "desc" ? -1 : 1;
        }

        const results = await this.companyList
            .find(query)
            .sort(sortObj)
            .skip(skip)
            .limit(limit)
            .exec();

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

    public async paginateFilterAll(
        data: PaginateCompaniesParams
    ): Promise<PaginateCompaniesResults> {
        const skip = (data.page - 1) * data.limit;
        const searchCriteria: mongoose.FilterQuery<ICompany> = {};

        if (data.name) {
            const words = data.name.split(" ").map((word) => new RegExp(word, "i"));
            searchCriteria.$and = words.map((word) => ({
                $or: [{ name: word }, { description: word }],
            }));
        }

        // Realiza la consulta con paginación
        const results = await this.companyList
            .find(searchCriteria)
            .sort({ price: 1 }) // Ordena por precio ascendente
            .skip(skip)
            .limit(data.limit)
            .exec();

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

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