import UsersManager from "../Users/Users";
import EmployeesList from "../Employees/Employee";
import { isNull } from "../../helpers/IsNull";
import { joinStrings } from "../../helpers/String";
import getAvatar from "../../helpers/getAvatar";
import { NotificationsManager } from "../Notifications/Notifications";
import dayjs from "dayjs";
import {
    CheckSchedulesDisabledParams,
    CreateSchedulesDisabledParams,
    DeleteSchedulesDisabledByCompanyParams,
    DeleteSchedulesDisabledByEmployeeParams,
    DeleteSchedulesDisabledParams,
    DisableScheduleParams,
    FindSchedulesDisabledParams,
    ISchedulesDisabled,
    ISchedulesDisabledManager,
    SchedulesDisabledByCollaboratorView,
} from "./SchedulesDisabled.Interface";
import { SchedulesDisabledAdapterMongoose } from "./SchedulesDisabled.Adapter.Mongoose";
import { validatePermissionsByCompany, validateSessionUser } from "../../helpers/check";
import { NotificationType } from "../../Models/SystemNotifications/SystemNotification.Interface";

dayjs.locale("es");

class SchedulesDisabledManager implements ISchedulesDisabledManager {
    schedulesDisabled: SchedulesDisabledAdapterMongoose;

    constructor() {
        this.schedulesDisabled = new SchedulesDisabledAdapterMongoose();
    }

    public async deleteSchedulesDisabledByEmployee(
        data: DeleteSchedulesDisabledByEmployeeParams
    ): Promise<void> {
        await this.schedulesDisabled.schedulesDisabledList.deleteMany({
            employeeId: data.employeeId,
        });
    }

    public async deleteSchedulesDisabledByCompany(
        data: DeleteSchedulesDisabledByCompanyParams
    ): Promise<void> {
        await this.schedulesDisabled.schedulesDisabledList.deleteMany({ companyId: data.companyId });
    }

    public async createSchedulesDisabled(data: CreateSchedulesDisabledParams): Promise<ISchedulesDisabled> {
        await validateSessionUser({ sessionUser: data.sessionUser });

        const companyCheck = await validatePermissionsByCompany({
            sessionUser: data.sessionUser,
            companyId: data.companyId,
        });

        const employeeCheck = await EmployeesList.employees.findOne({
            companyId: data.companyId,
            _id: data.employeeId,
        });

        if (!employeeCheck) {
            throw new Error("El empleado no existe o no pertenece a la compañia.");
        }

        //La fecha data.startDate no puede ser mayor que data.endData
        if (new Date(data.startDate) > new Date(data.endDate)) {
            throw new Error("La fecha de inicio no puede ser mayor que la fecha de fin.");
        }

        const schedulesDisabledCheck = await this.schedulesDisabled.findOne({
            employeeId: data.employeeId,
            companyId: data.companyId,
            startDate: new Date(data.startDate),
            endDate: new Date(data.endDate),
        });

        if (schedulesDisabledCheck) {
            return schedulesDisabledCheck;
        }

        const newEmplyeesDisabled = await this.schedulesDisabled.create(data);

        const strFechaInicio = dayjs(data.startDate).format("DD/MM/YYYY");
        const strFechaFin = dayjs(data.endDate).format("DD/MM/YYYY");

        await NotificationsManager.sendSystemNotification({
                userId: String(employeeCheck.userId),
                subject: "Se deshabilito un nuevo periodo de trabajo",
                message: `La organización ${companyCheck.company.name} ha deshabilitado las reservas para su usuario desde ${strFechaInicio} hasta ${strFechaFin}.`,
                type: NotificationType.SCHEDULE_RESTRICTION,
                code: String(employeeCheck._id)
            });

        return newEmplyeesDisabled;
    }

    public async disableSchedule(data: DisableScheduleParams): Promise<void> {
        await validateSessionUser({ sessionUser: data.sessionUser });

        const companyCheck = await validatePermissionsByCompany({
            sessionUser: data.sessionUser,
            companyId: data.companyId,
        });

        for (let i = 0; i < data.employees.length; i++) {
            const employeeId = data.employees[i].employeeId;
            const employeeCheck = await EmployeesList.employees.findOne({
                companyId: data.companyId,
                _id: employeeId,
            });

            if (!employeeCheck) {
                continue;
            }

            const schedulesDisabledCheck = await this.schedulesDisabled.findOne({
                employeeId: employeeId,
                companyId: data.companyId,
                startDate: new Date(data.startDate),
                endDate: new Date(data.endDate),
            });

            if (schedulesDisabledCheck) {
                continue;
            }

            await this.schedulesDisabled.create({
                employeeId: employeeId,
                companyId: data.companyId,
                startDate: new Date(data.startDate),
                endDate: new Date(data.endDate),
                sessionUser: data.sessionUser,
            });

            const strFechaInicio = dayjs(data.startDate).format("DD/MM/YYYY");
            const strFechaFin = dayjs(data.endDate).format("DD/MM/YYYY");

            await NotificationsManager.sendSystemNotification({
                userId: String(employeeCheck.userId),
                subject: "Se deshabilito un nuevo periodo de trabajo",
                message: `La organización ${companyCheck.company.name} ha deshabilitado las reservas para su usuario desde ${strFechaInicio} hasta ${strFechaFin}.`,
                type: NotificationType.SCHEDULE_RESTRICTION,
                code: String(employeeCheck._id)
            });
        }
    }

    public async enableSchedule(data: DisableScheduleParams): Promise<void> {
        await validateSessionUser({ sessionUser: data.sessionUser });

        const companyCheck = await validatePermissionsByCompany({
            sessionUser: data.sessionUser,
            companyId: data.companyId,
        });

        for (let i = 0; i < data.employees.length; i++) {
            const employeeId = data.employees[i].employeeId;
            const employeeCheck = await EmployeesList.employees.findOne({
                companyId: data.companyId,
                _id: employeeId,
            });

            if (!employeeCheck) {
                continue;
            }

            const schedulesDisabledCheck = await this.schedulesDisabled.findOne({
                employeeId: employeeId,
                companyId: data.companyId,
                startDate: new Date(data.startDate),
                endDate: new Date(data.endDate),
            });

            if (schedulesDisabledCheck) {
                await this.schedulesDisabled.delete(String(schedulesDisabledCheck.id));
                const strFechaInicio = dayjs(data.startDate).format("DD/MM/YYYY");
                const strFechaFin = dayjs(data.endDate).format("DD/MM/YYYY");

                await NotificationsManager.sendSystemNotification({
                userId: String(employeeCheck.userId),
                subject: "Se ha habilitado un periodo de trabajo",
                message: `La organización ${companyCheck.company.name} ha habilitado las reservas para su usuario desde ${strFechaInicio} hasta ${strFechaFin}.`,
                type: NotificationType.SCHEDULE_RESTRICTION,
                code: String(employeeCheck._id)
            });
            }
        }
    }

    public async exists(data: CheckSchedulesDisabledParams): Promise<boolean> {
        const scheduleCheck = await this.schedulesDisabled.schedulesDisabledList.findOne({
            employeeId: data.employeeId,
            companyId: data.companyId,
            startDate: { $lte: data.dateDay },
            endDate: { $gte: data.dateDay },
        });

        if (scheduleCheck) {
            return true;
        }
        return false;
    }

    public async findSchedulesDisabledByCollaborator(
        data: FindSchedulesDisabledParams
    ): Promise<SchedulesDisabledByCollaboratorView> {
        await validateSessionUser({ sessionUser: data.sessionUser });
        await validatePermissionsByCompany({
            sessionUser: data.sessionUser,
            companyId: data.companyId,
        });

        //Borramos los registros que esten pasados
        await this.schedulesDisabled.schedulesDisabledList.deleteMany({
            companyId: data.companyId,
            employeeId: String(data.employeeId),
            endDate: { $lt: new Date() },
        });

        let returnData: SchedulesDisabledByCollaboratorView = {
            id: "",
            employeeId: "",
            employeeFullName: "",
            employeeEmail: "",
            employeeAvatar: "",
            employeeUserId: "",
            disabledItems: [],
        };

        const employee = await EmployeesList.employees.findOne({
            companyId: String(data.companyId),
            _id: String(data.employeeId),
        });

        if (!employee) {
            throw new Error("No se encontro el colaborador");
        }

        const employUser = await UsersManager.users.findOne({
            _id: String(employee.userId),
        });

        if (!employUser) {
            return returnData;
        }

        const fullName = joinStrings([employUser.firstName, employUser.lastName], " ");

        returnData = {
            id: "",
            employeeId: isNull<string>(employee.id, ""),
            employeeFullName: fullName,
            employeeEmail: employUser.email,
            employeeAvatar: getAvatar(employUser.id, employUser.avatar, fullName),
            employeeUserId: isNull<string>(employee.userId, ""),
            disabledItems: [],
        };

        const schedulesDisabled = await this.schedulesDisabled.schedulesDisabledList
            .find({
                employeeId: String(employee.id),
                companyId: String(data.companyId),
            })
            .sort({ startDate: 1 });

        returnData.disabledItems = schedulesDisabled;

        return returnData;
    }

    public async findOne(data: FindSchedulesDisabledParams): Promise<ISchedulesDisabled | null> {
        return await this.schedulesDisabled.findOne(data);
    }

    public async delete(data: DeleteSchedulesDisabledParams): Promise<void> {
        await validateSessionUser({ sessionUser: data.sessionUser });

        await validatePermissionsByCompany({
            sessionUser: data.sessionUser,
            companyId: data.companyId,
        });

        await this.schedulesDisabled.delete(String(data.id));
    }
}

const SchedulesDisabledList = new SchedulesDisabledManager();

export default SchedulesDisabledList;
