import {
    CalculateMetricsParams,
    DeleteMetricsByCompany,
    IMetricsManager,
    MetricsParams,
} from "./Metrics.Interface";
import { IMetricsDocument, MetricsAdapterMongoose } from "./Metrics.Adapter.Mongoose";
import PlansList from "../../Models/Plans/Plans";
import PlanSubscriptionsList from "../../Models/PlanSubscriptions/PlanSubscriptons";
import CompaniesList from "../Companies/Companies";
import EmployeesList from "../Employees/Employee";
import ServiceList from "../Services/Service";
import AppointmentList from "../Appointments/Appointments";
import ClientsList from "../Clients/Clients";
import RepeatsList from "../Repeats/Repeats";
import { isNull } from "../../helpers/IsNull";

class MetricsManager implements IMetricsManager {
    metrics: MetricsAdapterMongoose;

    constructor() {
        this.metrics = new MetricsAdapterMongoose();
    }

    public async calculateMetrics(data: CalculateMetricsParams): Promise<void> {
        //TODO: calcular las metricas...
        const metrics = await this.getMetrics(data.userId);

        const organizations = await CompaniesList.companies.find({
            ownerId: data.userId,
        });

        let employeesLength = 0;
        let servicesLength = 0;
        let appointmentsLength = 0;
        let clientsLength = 0;
        let repeatsLength = 0;

        for (const org of organizations) {
            const employees = await EmployeesList.employees.find({
                companyId: String(org.id),
            });
            employeesLength += employees.length;

            const services = await ServiceList.services.find({
                companyId: String(org.id),
            });

            servicesLength += services.length;

            //TODO: Revisar si el parametro del mes esta bien o necesita incrementar en uno.
            //ya que no recuerdo si empieza en 0 o en 1.
            appointmentsLength += await AppointmentList.countAppointmentsByMonth({
                year: metrics.year,
                month: metrics.month,
                companyId: String(org.id),
            });

            const clients = await ClientsList.clients.find({
                companyId: String(org.id),
            });

            clientsLength += clients.length;

            const repeats = await RepeatsList.repeats.find({
                companyId: String(org.id),
            });

            repeatsLength += repeats.length;
        }

        metrics.organizationsCount = organizations.length;
        metrics.employeesCount = employeesLength;
        metrics.servicesCount = servicesLength;
        metrics.appointmentsCount = appointmentsLength;
        metrics.clientsCount = clientsLength;
        metrics.repeatsCount = repeatsLength;
        await metrics.save();
    }

    public async deleteMetricsByCompany(data: DeleteMetricsByCompany): Promise<void> {
        const companyCheck = await CompaniesList.companies.findOne({ _id: data.companyId });

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

        await this.metrics.reset(String(companyCheck.ownerId));

        //await this.metrics.metricsList.deleteMany({ userId: String(companyCheck.ownerId) });
    }

    public async reset(userId: string): Promise<void> {
        await this.metrics.reset(userId);
    }

    public async getMetrics(userId: string): Promise<IMetricsDocument> {
        return await this.metrics.getMetrics(userId);
    }

    public async canAddOrganization(userId: string): Promise<boolean> {
        const subscription = await PlanSubscriptionsList.findOne({
            sessionUser: userId,
        });
        if (!subscription) {
            return false;
        }
        const plan = await PlansList.plans.findOne({
            _id: subscription.planId,
        });
        if (!plan) {
            return false;
        }
        if (plan.limitOrganizations < 0) {
            return true;
        }
        const metrics = await this.getMetrics(userId);
        if (metrics.organizationsCount >= plan.limitOrganizations) {
            return false;
        }
        return true;
    }

    public async canAddService(userId: string): Promise<boolean> {
        const subscription = await PlanSubscriptionsList.findOne({
            sessionUser: userId,
        });
        if (!subscription) {
            return false;
        }
        const plan = await PlansList.plans.findOne({
            _id: subscription.planId,
        });
        if (!plan) {
            return false;
        }
        if (plan.limitServices < 0) {
            return true;
        }
        const metrics = await this.getMetrics(userId);
        if (metrics.servicesCount >= plan.limitServices) {
            return false;
        }
        return true;
    }

    public async canAddEmployee(userId: string): Promise<boolean> {
        const subscription = await PlanSubscriptionsList.findOne({
            sessionUser: userId,
        });
        if (!subscription) {
            return false;
        }
        const plan = await PlansList.plans.findOne({
            _id: subscription.planId,
        });
        if (!plan) {
            return false;
        }
        if (plan.limitEmployees < 0) {
            return true;
        }
        const metrics = await this.getMetrics(userId);
        if (metrics.employeesCount >= plan.limitEmployees) {
            return false;
        }
        return true;
    }

    public async canAddClient(userId: string): Promise<boolean> {
        const subscription = await PlanSubscriptionsList.findOne({
            sessionUser: userId,
        });
        if (!subscription) {
            return false;
        }
        const plan = await PlansList.plans.findOne({
            _id: subscription.planId,
        });
        if (!plan) {
            return false;
        }

        if (plan.limitClients < 0) {
            return true;
        }
        const metrics = await this.getMetrics(userId);
        if (metrics.clientsCount >= plan.limitClients) {
            return false;
        }
        return true;
    }

    public async canAddRepeat(userId: string): Promise<boolean> {
        const subscription = await PlanSubscriptionsList.findOne({
            sessionUser: userId,
        });
        if (!subscription) {
            return false;
        }
        const plan = await PlansList.plans.findOne({
            _id: subscription.planId,
        });
        if (!plan) {
            return false;
        }

        if (plan.limitRepeats < 0) {
            return true;
        }
        const metrics = await this.getMetrics(userId);
        if (isNull<number>(metrics.repeatsCount, 0) >= plan.limitRepeats) {
            return false;
        }
        return true;
    }

    public async canAddAppointment(userId: string): Promise<boolean> {
        const subscription = await PlanSubscriptionsList.findOne({
            sessionUser: userId,
        });
        if (!subscription) {
            return false;
        }
        const plan = await PlansList.plans.findOne({
            _id: subscription.planId,
        });
        if (!plan) {
            return false;
        }
        if (plan.limitAppointments < 0) {
            return true;
        }
        const metrics = await this.getMetrics(userId);
        if (metrics.appointmentsCount >= plan.limitAppointments) {
            return false;
        }
        return true;
    }

    public async addAppointment(data: MetricsParams): Promise<void> {
        await this.metrics.addAppointment(data);
    }

    public async addClient(data: MetricsParams): Promise<void> {
        await this.metrics.addClient(data);
    }

    public async addRepeat(data: MetricsParams): Promise<void> {
        await this.metrics.addRepeat(data);
    }

    public async addEmployee(data: MetricsParams): Promise<void> {
        await this.metrics.addEmployee(data);
    }

    public async addOrganization(data: MetricsParams): Promise<void> {
        await this.metrics.addOrganization(data);
    }

    public async addService(data: MetricsParams): Promise<void> {
        await this.metrics.addService(data);
    }
}

const MetricsList = new MetricsManager();

export default MetricsList;
