import { SchedulesEnabledAdapterMongoose } from "./SchedulesEnabled.Adapter.Mongoose";
import {
    CheckSchedulesEnabledParams,
    CreateSchedulesEnabledParams,
    DeleteSchedulesEnabledByCompanyParams,
    DeleteSchedulesEnabledByEmployeeParams,
    DeleteSchedulesEnabledParams,
    EnableScheduleParams,
    FindSchedulesEnabledParams,
    ISchedulesEnabled,
    ISchedulesEnabledManager,
    SchedulesEnabledView,
} from "./SchedulesEnabled.Interface";
import { EmployeeRoles } from "../Employees/Employees.Interface";
import UsersManager from "../Users/Users";
import CompaniesManager from "../Companies/Companies";
import EmployeesList from "../Employees/Employee";
import { NoPermissionMessage } from "../../helpers/NoPermissionMessage";
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 { NotificationType } from "../../Models/SystemNotifications/SystemNotification.Interface";

dayjs.locale("es");

class SchedulesEnabledManager implements ISchedulesEnabledManager {
    schedulesEnabled: SchedulesEnabledAdapterMongoose;

    constructor() {
        this.schedulesEnabled = new SchedulesEnabledAdapterMongoose();
    }

    public async deleteSchedulesEnabledByEmployee(
        data: DeleteSchedulesEnabledByEmployeeParams
    ): Promise<void> {
        await this.schedulesEnabled.schedulesEnabledList.deleteMany({
            employeeId: data.employeeId,
        });
    }

    public async deleteSchedulesEnabledByCompany(data: DeleteSchedulesEnabledByCompanyParams): Promise<void> {
        await this.schedulesEnabled.schedulesEnabledList.deleteMany({ companyId: data.companyId });
    }

    public async createSchedulesEnabled(data: CreateSchedulesEnabledParams): Promise<ISchedulesEnabled> {
        const sessionUser = await UsersManager.users.findOne({ _id: data.sessionUser });

        if (!sessionUser) {
            throw new Error("No se ha encontrado el usuario o no tiene permisos para realizar esta acción");
        }

        const companyCheck = await CompaniesManager.companies.findOne({ _id: data.companyId });

        if (!companyCheck) {
            throw new Error("La compañia no existe");
        }

        if (!(await EmployeesList.checkPermission(companyCheck, data.sessionUser, EmployeeRoles.ADMIN))) {
            throw new Error(NoPermissionMessage());
        }

        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.");
        }

        const schedulesEnabledCheck = await this.schedulesEnabled.findOne({
            employeeId: data.employeeId,
            companyId: data.companyId,
            startDate: dayjs(data.startDate).startOf("day").toDate(),
            endDate: dayjs(data.endDate).endOf("day").toDate(),
        });

        if (schedulesEnabledCheck) {
            return schedulesEnabledCheck;
        }

        const newEmplyeesEnabled = await this.schedulesEnabled.create({
            employeeId: data.employeeId,
            companyId: data.companyId,
            startDate: dayjs(data.startDate).startOf("day").toDate(),
            endDate: dayjs(data.endDate).endOf("day").toDate(),
            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 habilito un nuevo periodo de trabajo",
                message: `La organización ${companyCheck.name} ha habilitado las reservas para su usuario desde ${strFechaInicio} hasta ${strFechaFin}.`,
                type: NotificationType.SCHEDULE,
                code: String(employeeCheck._id)
            });

        return newEmplyeesEnabled;
    }

    public async enableSchedule(data: EnableScheduleParams): Promise<void> {
        const sessionUser = await UsersManager.users.findOne({ _id: data.sessionUser });

        if (!sessionUser) {
            throw new Error("No se ha encontrado el usuario o no tiene permisos para realizar esta acción");
        }

        const companyCheck = await CompaniesManager.companies.findOne({ _id: data.companyId });

        if (!companyCheck) {
            throw new Error("La compañia no existe");
        }

        if (!(await EmployeesList.checkPermission(companyCheck, data.sessionUser, EmployeeRoles.ADMIN))) {
            throw new Error(NoPermissionMessage());
        }

        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 schedulesEnabledCheck = await this.schedulesEnabled.findOne({
                employeeId: employeeId,
                companyId: data.companyId,
                startDate: dayjs(data.startDate).startOf("day").toDate(),
                endDate: dayjs(data.endDate).endOf("day").toDate(),
            });

            if (schedulesEnabledCheck) {
                continue;
            }

            await this.schedulesEnabled.create({
                employeeId: employeeId,
                companyId: data.companyId,
                startDate: dayjs(data.startDate).startOf("day").toDate(),
                endDate: dayjs(data.endDate).endOf("day").toDate(),
                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 habilito un nuevo periodo de trabajo",
                message: `La organización ${companyCheck.name} ha habilitado las reservas para su usuario desde ${strFechaInicio} hasta ${strFechaFin}.`,
                type: NotificationType.SCHEDULE,
                code: String(employeeCheck._id)
            });
        }
    }

    public async disableSchedule(data: EnableScheduleParams): Promise<void> {
        const sessionUser = await UsersManager.users.findOne({ _id: data.sessionUser });

        if (!sessionUser) {
            throw new Error("No se ha encontrado el usuario o no tiene permisos para realizar esta acción");
        }

        const companyCheck = await CompaniesManager.companies.findOne({ _id: data.companyId });

        if (!companyCheck) {
            throw new Error("La compañia no existe");
        }

        if (!(await EmployeesList.checkPermission(companyCheck, data.sessionUser, EmployeeRoles.ADMIN))) {
            throw new Error(NoPermissionMessage());
        }

        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 schedulesEnabledCheck = await this.schedulesEnabled.findOne({
                employeeId: employeeId,
                companyId: data.companyId,
                startDate: dayjs(data.startDate).startOf("day").toDate(),
                endDate: dayjs(data.endDate).endOf("day").toDate(),
            });

            if (schedulesEnabledCheck) {
                await this.schedulesEnabled.delete(String(schedulesEnabledCheck.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 deshabilitado un periodo de trabajo",
                message: `La organización ${companyCheck.name} ha deshabilitado las reservas para su usuario desde ${strFechaInicio} hasta ${strFechaFin}.`,
                type: NotificationType.SCHEDULE,
                code: String(employeeCheck._id)
            });
            }
        }
    }

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

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

    public async find(data: FindSchedulesEnabledParams): Promise<SchedulesEnabledView[]> {
        let employeeFilter: any = { companyId: data.companyId };
        if (data.employeeId) {
            employeeFilter = {
                ...employeeFilter,
                employeeId: data.employeeId,
            };
        }
        const employees = await EmployeesList.employees.find(employeeFilter);

        const returnData: SchedulesEnabledView[] = [];

        for (const employee of employees) {
            const schedulesEnabled = await this.schedulesEnabled.schedulesEnabledList.findOne({
                employeeId: employee.id,
                companyId: data.companyId,
                startDate: dayjs(data.startDate).startOf("day").toDate(),
                endDate: dayjs(data.endDate).endOf("day").toDate(),
            });

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

            if (!employUser) {
                continue;
            }

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

            returnData.push({
                id: schedulesEnabled ? schedulesEnabled.id : "",
                employeeId: isNull<string>(employee.id, ""),
                employeeFullName: fullName,
                employeeEmail: employUser.email,
                employeeAvatar: getAvatar(employUser.id, employUser.avatar, fullName),
                employeeUserId: isNull<string>(employee.userId, ""),
                startDate: data.startDate,
                endDate: data.endDate,
                enabled: schedulesEnabled ? true : false,
            });
        }

        return returnData;
    }

    public async findOne(data: FindSchedulesEnabledParams): Promise<ISchedulesEnabled | null> {
        return await this.schedulesEnabled.findOne(data);
    }

    public async delete(data: DeleteSchedulesEnabledParams): Promise<void> {
        const sessionUser = await UsersManager.users.findOne({ _id: data.sessionUser });

        if (!sessionUser) {
            throw new Error("No se ha encontrado el usuario o no tiene permisos para realizar esta acción");
        }

        const companyCheck = await CompaniesManager.companies.findOne({ _id: data.companyId });

        if (!companyCheck) {
            throw new Error("La compañia no existe");
        }

        if (!(await EmployeesList.checkPermission(companyCheck, data.sessionUser, EmployeeRoles.ADMIN))) {
            throw new Error(NoPermissionMessage());
        }

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

const SchedulesEnabledList = new SchedulesEnabledManager();

export default SchedulesEnabledList;
