import dayjs from "dayjs";
import axios from "axios";
import { logDone, logError, logIntent } from "../../config/logger.js";
import { Appointments, IAppointment, IAppointmentDocument } from "../Appointments/Appointments.js";
import { Companies } from "../Companies/Companies.js";
import { PlanSuscriptions } from "../PlanSubscriptions/PlanSubscriptions.js";

export type ApiVoidResult = {
    success: boolean;
    message: string;
};

export type SendWapSystemNotificationParams = {
    appointmentId: string;
    systemToken: string;
};

export class Notifications {
    private users: string[] = [];
    private companies: string[] = [];
    private appointments: IAppointmentDocument[] = [];
    private appointmentsCollection: Appointments;
    constructor() {
        this.users = [];
        this.companies = [];
        this.appointments = [];
        this.appointmentsCollection = new Appointments();
    }

    private async loadUsers() {
        logIntent("Cargando lista de usuarios.");
        const loadedPlanIds = `${process.env.PLAN_LIST}`;

        if (loadedPlanIds === "") {
            logError("No se ha encontrado una lista de planes valida.", "");
            process.exit(1);
        }

        const PLAN_IDS = loadedPlanIds.split(",");
        const subs = new PlanSuscriptions();
        const result = await subs.planSuscriptionList.find({ planId: { $in: PLAN_IDS } }).exec();
        this.users = result.map((x) => String(x.userId));
        logDone("Lista de usuarios cargada correctamente.");
    }

    private async loadCompanies() {
        logIntent("Cargando lista de organizaciones.");
        const orgs = new Companies();

        for (const user of this.users) {
            const organizations = await orgs.companyList
                .find({ ownerId: user, automaticNotifications: true })
                .exec();

            for (const orgItem of organizations) {
                this.companies.push(orgItem.id);
            }
        }
        logDone("Lista de organizaciones cargada correctamente.");
    }

    private async loadAppointments() {
        try {
            logIntent("Cargando nuevo lote de notificaciones");
            this.appointments = [];
            const dateFrom = dayjs().clone().startOf("day");
            const dateTo = dayjs().clone().endOf("day");

            for (const company of this.companies) {
                const result = await this.appointmentsCollection.appointmentsList
                    .findOne({
                        start: { $gte: dateFrom.toDate(), $lte: dateTo.toDate() },
                        companyId: company,
                        $and: [
                            { $or: [{ notificated: false }, { notificated: { $exists: false } }] },
                            {
                                $or: [
                                    { notificationIntents: { $lt: 3 } },
                                    { notificationIntents: { $exists: false } },
                                ],
                            },
                        ],
                    })
                    .exec();
                if (!result) {
                    continue;
                }
                this.appointments.push(result);
            }
            logDone("Nuevo lote de notificaciones cargado correctamente.");
        } catch (error) {
            logError("Error cargando nuevo lote de notificaciones:", error);
        }
    }

    public async start() {
        await this.loadUsers();
        await this.loadCompanies();
        await this.loadAppointments();

        this.next();
    }
    public async next() {
        const getDelay = () => {
            const timeBase = Number(process.env.TIME_BASE ? process.env.TIME_BASE : 8);
            const timeExtra = Number(process.env.TIME_EXTRA ? process.env.TIME_EXTRA : 8);
            return Math.floor(Math.random() * timeBase) + timeExtra;
        };

        const app = this.appointments.shift();

        if (!app) {
            logDone("Se ha finalizado el envío del lote de notificaciones.");
            setTimeout(() => {
                this.reload();
            }, getDelay() * 1000);
            return;
        }

        try {
            await this.send(String(app.id));
            app.notificated = true;
        } catch (error) {
            app.notificationIntents = app.notificationIntents ? app.notificationIntents + 1 : 1;
            logError("Error enviando notificación:", error);
        }

        await app.save();

        setTimeout(() => {
            this.next();
        }, getDelay() * 1000);
    }

    public async reload() {
        await this.loadAppointments();
        if (this.appointments.length < 1) {
            this.end();
        }
        this.next();
    }

    public async end() {
        process.exit(0);
    }

    private async send(appointmentId: string): Promise<void> {
        logIntent(`Enviando notificación para la cita ${appointmentId}.`);

        const dataApi: SendWapSystemNotificationParams = {
            appointmentId: appointmentId,
            systemToken: `${process.env.API_KEY}`,
        };
        const response = await axios.post<ApiVoidResult>(`${process.env.API_URL}`, dataApi, {
            headers: {
                "Content-Type": "application/json",
            },
        });

        const result: ApiVoidResult = response.data;

        if (!result.success) {
            throw new Error(result.message);
        }

        logDone("Notificación enviada correctamente.");
    }
}
