import UsersManager from "../Users/Users";
import { IUserDocument } from "../Users/Users.Adapter.Mongoose";
import CompaniesList from "../Companies/Companies";
import { EmployeeRoles } from "../Employees/Employees.Interface";
import EmployeeManager from "../Employees/Employee";
import { NoPermissionMessage } from "../../helpers/NoPermissionMessage";

import {
    CreateCashFlowParams,
    DeleteByClientAccountMovementParams,
    DeleteMovementByPaymentParams,
    DeleteMovementParams,
    FindCashFlowParams,
    ICashFlow,
    ICashFlowManager,
    PaginateCashFlowParams,
    PaginateCashFlowResults,
} from "./CashFlow.Interface";
import { CashFlowAdapterMongoose } from "./CashFlow.Adapter.Mongoose";
import { CLIENT_ACCOUNT_MOVEMENT_TYPES } from "../ClientAccountMovements/ClientAccountMovements.Interface";
import PlanSubscriptionsList from "../PlanSubscriptions/PlanSubscriptons";
import { PlanFeatures } from "../Plans/Plans.interface";

class CashFlowManager implements ICashFlowManager {
    movements: CashFlowAdapterMongoose;

    constructor() {
        this.movements = new CashFlowAdapterMongoose();
    }

    public async getCashFlowBalance(data: FindCashFlowParams): Promise<number> {
        const userSession = await this.validateSessionUser(data.sessionUser);
        await this.validatePermissions(String(userSession.id), data.companyId);
        const movements = await this.movements.find(data);
        return movements.reduce((acc, movement) => acc + movement.amount, 0);
    }

    public async delete(data: DeleteMovementParams): Promise<void> {
        const userSession = await this.validateSessionUser(data.sessionUser);
        await this.validatePermissions(String(userSession.id), data.companyId);
        await this.movements.delete(data.id);
    }

    public async deleteByPayment(data: DeleteMovementByPaymentParams): Promise<void> {
        const userSession = await this.validateSessionUser(data.sessionUser);
        await this.validatePermissions(String(userSession.id), data.companyId);
        await this.movements.movementList.deleteOne({ paymentId: data.paymentId });
    }

    public async deleteByClientAccountMovement(
        data: DeleteByClientAccountMovementParams
    ): Promise<void> {
        const userSession = await this.validateSessionUser(data.sessionUser);
        await this.validatePermissions(String(userSession.id), data.companyId);
        await this.movements.movementList.deleteOne({
            clientAccountMovementId: data.clientAccountMovementId,
        });
    }

    private async validateSessionUser(sessionUserId: string): Promise<IUserDocument> {
        const sessionUser = await UsersManager.users.findOne({
            _id: sessionUserId,
        });

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

        return sessionUser;
    }

    private async validatePermissions(sessionUserId: string, companyId: string): Promise<void> {
        const company = await CompaniesList.companies.findOne({
            _id: companyId,
        });

        if (!company) {
            throw new Error("No se ha encontrado la organización");
        }

        // Verificar si el usuario de sesión es un empleado con permisos de administrador
        const hasAdminPermission = await EmployeeManager.checkRoleById(
            companyId,
            sessionUserId,
            EmployeeRoles.ADMIN
        );

        if (!hasAdminPermission) {
            throw new Error(NoPermissionMessage());
        }

        const canAccess = await PlanSubscriptionsList.checkFeature({
            userId: String(company.ownerId),
            feature: PlanFeatures.PAYMENTS,
        });

        if (!canAccess) {
            throw new Error("La organizacion no tiene un plan que soporte el módulo de pagos");
        }
    }

    public async add(data: CreateCashFlowParams): Promise<ICashFlow> {
        const userSession = await this.validateSessionUser(data.sessionUser);
        await this.validatePermissions(String(userSession.id), data.companyId);

        if (data.type === CLIENT_ACCOUNT_MOVEMENT_TYPES.DEPOSIT) {
            data.amount = Math.abs(data.amount);
        } else {
            data.amount = -Math.abs(data.amount);
        }

        const movement = await this.movements.create({
            ...data,
            ...{ createdAt: new Date() },
        });

        return movement;
    }

    public async paginate(data: PaginateCashFlowParams): Promise<PaginateCashFlowResults> {
        const balance = await this.getCashFlowBalance(data);

        const result = await this.movements.paginate(data);

        return {
            ...result,
            balance,
        };
    }
}

export default new CashFlowManager();
