import {
    ApprovalUsersGatheringInterface,
    EventEmitterPort,
    Events,
    BaseAdapter,
    ISO_DATE,
    BadRequestError,
    ErrorCodes,
    HierarchicalRelations,
    ISOLATION_LEVELS,
} from '@humand-packages/common';
import { chunk } from 'lodash';
import moment from 'moment';
import { Service } from 'typedi';
import { PortsDI } from '../../../../config/portsDI';
import { FeatureFlagCode } from '../../../featureFlags/business/enums/featureFlagCode';
import { FeatureFlagService } from '../../../featureFlags/business/services/featureFlagService';
import { Instance } from '../../../instances/business/models/instance';
import { InstancesService } from '../../../instances/business/services/instancesService';
import { User } from '../../../users/business/models/user';
import { NightShiftAssignment } from '../../infrastructure/constants/setting';
import { TimeTrackingConfigRepositoryPort } from '../../infrastructure/ports/timeTracking/timeTrackingConfigRepositoryPort';
import TimeTrackingPoliciesRepositoryPort from '../../infrastructure/ports/TimeTrackingPoliciesRepositoryPort';
import TimeTrackingPolicyApprovalStepsRepositoryPort from '../../infrastructure/ports/TimeTrackingPolicyApprovalStepsRepositoryPort';
import { CATEGORIZED_HOURS_USER_CHUNK_SIZE, CategorizedHoursActionType } from '../constants/categorizedHoursActionTypes';
import { TimeTrackingPolicyAutoCloseMode } from '../enum/timeTracking/policy';
import TimeTrackingPoliciesFilters from '../interfaces/timeTrackingPoliciesFilters';
import TimeTrackingPoliciesOptions from '../interfaces/timeTrackingPoliciesOptions';
import { TimeTrackingPolicyApprovalStepResponsibleRelationForCreation } from '../models/policies/approvalSteps/responsibles/relation';
import { TimeTrackingPolicyApprovalStepResponsibleSegmentationItemForCreation } from '../models/policies/approvalSteps/responsibles/segmentationItem';
import { TimeTrackingPolicyApprovalStepResponsibleUserForCreation } from '../models/policies/approvalSteps/responsibles/user';
import {
    TimeTrackingPolicyApprovalStep,
    TimeTrackingPolicyApprovalStepForCreation,
} from '../models/policies/approvalSteps/timeTrackingPolicyApprovalStep';
import { ScheduleCategorizedHoursParams } from '../models/scheduleCategorizedHoursParams';
import {
    TimeTrackingPolicy,
    TimeTrackingPolicyAssociation,
    TimeTrackingPolicyForCreation,
    TimeTrackingPolicyWithApprovalSteps,
} from '../models/timeTrackingPolicy';
import { TimeTrackingPolicyHourCategoryAssociation } from '../models/TimeTrackingPolicyHourCategory';
import { TimeTrackingSetting } from '../models/timeTrackingSetting';
import ScheduleCategorizedHoursPort from '../ports/scheduleCategorizedHoursPort';
import { TimeTrackingPoliciesPort } from '../ports/timeTrackingPoliciesPort';
import TimeTrackingSettingService from './setting';

@Service()
export default class TimeTrackingPoliciesAdapter extends BaseAdapter implements TimeTrackingPoliciesPort {
    constructor(
        @PortsDI(TimeTrackingPoliciesRepositoryPort) private readonly timeTrackingPoliciesRepository: TimeTrackingPoliciesRepositoryPort,
        private readonly settingService: TimeTrackingSettingService,
        private readonly eventEmitter: EventEmitterPort,
        private readonly instanceService: InstancesService,
        @PortsDI(TimeTrackingConfigRepositoryPort) private readonly timeTrackingConfigService: TimeTrackingConfigRepositoryPort,
        private readonly featureFlagService: FeatureFlagService,
        @PortsDI(ScheduleCategorizedHoursPort) private readonly scheduleCategorizedHoursPort: ScheduleCategorizedHoursPort,
        @PortsDI(TimeTrackingPolicyApprovalStepsRepositoryPort)
        private readonly approvalStepsRepository: TimeTrackingPolicyApprovalStepsRepositoryPort,
    ) {
        super();
        this.setupEventHandlers();
    }

    private setupEventHandlers() {
        this.eventEmitter.listenEvent(Events.TIME_TRACKING_SETTING_UPDATED, this.handleSettingsUpdated);
    }

    private handleSettingsUpdated = async (params: { instanceId: number; setting: { nightShiftAssignment: string } }) => {
        const instance = await this.instanceService.findByPk(params.instanceId);
        const currentDate = moment.tz(instance.getTimezone()).format(ISO_DATE);
        const affectedPoliciesIds = await this.timeTrackingPoliciesRepository.getTransaction(async () => {
            return await this.timeTrackingPoliciesRepository.updateSettingsInPolicies(params.instanceId, {
                nightShiftAssignment: params.setting.nightShiftAssignment,
            });
        });
        const hasTimeTrackingDesertEagleEnabledFF = await this.getTimeTrackingDesertEagleEnabledFeatureFlag(params.instanceId);
        for (const policyId of affectedPoliciesIds) {
            const policy = await this.timeTrackingPoliciesRepository.getPolicyByIdOrFail(params.instanceId, policyId);
            if (hasTimeTrackingDesertEagleEnabledFF) {
                await this.timeTrackingConfigService.updatePolicy(params.instanceId, policy);
            }
            this.eventEmitter.emitEvent(Events.TIME_TRACKING_POLICY_ASSIGNED, {
                instanceId: params.instanceId,
                policy: {
                    clockingInKioskEnabled: policy.clockingInKioskEnabled,
                    notificationsEnabled: policy.notificationsEnabled,
                },
                startDate: currentDate,
                userIds: policy.userIds,
            });
        }
    };

    public getPolicyByIdOrFail = async (user: User, policyId: number): Promise<TimeTrackingPolicyWithApprovalSteps> => {
        const policy = await this.timeTrackingPoliciesRepository.getPolicyByIdOrFail(user.instanceId, policyId);
        const policyWithApprovalSteps = new TimeTrackingPolicyWithApprovalSteps(policy);
        const policySpecificSteps = await this.approvalStepsRepository.listPolicyApprovalSteps({ instanceId: user.instanceId, policyId });
        if (policySpecificSteps.length > 0) {
            policyWithApprovalSteps.approvalSteps = policySpecificSteps;
        } else {
            policyWithApprovalSteps.approvalSteps = await this.getApprovalSteps(user.instanceId, null);
        }
        return policyWithApprovalSteps;
    };

    public listPolicies = async (
        policiesFilters: TimeTrackingPoliciesFilters,
        options: TimeTrackingPoliciesOptions,
    ): Promise<{ count: number; rows: TimeTrackingPolicy[] }> => {
        return await this.timeTrackingPoliciesRepository.listPolicies(policiesFilters, options);
    };

    public listPolicyAssociations = async (
        policiesFilters: TimeTrackingPoliciesFilters,
        options: TimeTrackingPoliciesOptions,
    ): Promise<{ count: number; rows: TimeTrackingPolicyAssociation[] }> => {
        return await this.timeTrackingPoliciesRepository.listPolicyAssociations(policiesFilters, options);
    };

    public listActivePolicyAssociations = async (instanceId: number, userIds: number[]): Promise<TimeTrackingPolicyAssociation[]> => {
        return await this.timeTrackingPoliciesRepository.listActivePolicyAssociations(instanceId, userIds);
    };

    public listPolicyAssociationsEndedAt = async (
        instanceId: number,
        toDate: string,
        userIds: number[],
    ): Promise<TimeTrackingPolicyAssociation[]> => {
        return await this.timeTrackingPoliciesRepository.listPolicyAssociationsEndedAt(instanceId, toDate, userIds);
    };

    public createPolicy = async (instanceId: number, policy: TimeTrackingPolicyForCreation): Promise<TimeTrackingPolicy> => {
        const settings = await this.settingService.getByInstanceId(instanceId);
        policy.nightShift = settings.nightShiftAssignment === NightShiftAssignment.SCHEDULE_INIT.toString();
        return await this.timeTrackingPoliciesRepository.createPolicy(instanceId, policy);
    };

    private emitPolicyAssignmentChangeEvents = (
        instanceId: number,
        policy: TimeTrackingPolicy,
        usersNewlyAssigned: number[],
        usersNowUnassigned: number[],
        fromDate: string,
    ) => {
        const policyPayload = {
            clockingInKioskEnabled: policy.clockingInKioskEnabled,
            notificationsEnabled: policy.notificationsEnabled,
        };
        if (usersNewlyAssigned.length > 0) {
            this.eventEmitter.emitEvent(Events.TIME_TRACKING_POLICY_ASSIGNED, {
                instanceId,
                policy: policyPayload,
                startDate: fromDate,
                userIds: usersNewlyAssigned,
            });
        }
        // Users who left this policy without replacement. Users moved to another policy are
        // handled by the ASSIGNED event on the destination policy (kiosk listener has an else
        // branch for kiosk-disabled destinations), so emitting UNASSIGNED for them here would
        // cause spurious side-effects (e.g. "tracking disabled" push).
        if (usersNowUnassigned.length > 0) {
            this.eventEmitter.emitEvent(Events.TIME_TRACKING_POLICY_UNASSIGNED, {
                instanceId,
                policy: policyPayload,
                startDate: fromDate,
                userIds: usersNowUnassigned,
            });
        }
    };

    public createAndAssociatePolicy = async (
        instanceId: number,
        policy: TimeTrackingPolicyForCreation,
        userIds: number[],
        fromDate: string,
        allUsers: boolean,
        policyHourCategories: Array<TimeTrackingPolicyHourCategoryAssociation>,
        approvalUsers?: ApprovalUsersGatheringInterface[],
    ): Promise<TimeTrackingPolicy> => {
        const { newPolicy, usersNewlyAssigned, usersNowUnassigned } = await this.timeTrackingPoliciesRepository.getTransaction(async () => {
            const newPolicy = await this.createPolicy(instanceId, policy);
            const { usersNewlyAssigned, usersNowUnassigned } = await this.associatePolicy(
                newPolicy.id,
                userIds,
                fromDate,
                allUsers,
                instanceId,
                false,
            );
            if (policyHourCategories && policyHourCategories.length > 0) {
                await this.timeTrackingPoliciesRepository.associatePolicyHourCategories(newPolicy.id, policyHourCategories, instanceId);
            }
            if (approvalUsers && approvalUsers.length > 0) {
                await this.updateApprovalSteps(instanceId, approvalUsers, newPolicy.id);
            }
            return { newPolicy, usersNewlyAssigned, usersNowUnassigned };
        });
        const hasTimeTrackingDesertEagleEnabledFF = await this.getTimeTrackingDesertEagleEnabledFeatureFlag(instanceId);
        if (hasTimeTrackingDesertEagleEnabledFF) {
            await this.timeTrackingConfigService.associatePolicy(instanceId, newPolicy, usersNewlyAssigned, usersNowUnassigned);
        }
        this.emitPolicyAssignmentChangeEvents(instanceId, newPolicy, usersNewlyAssigned, usersNowUnassigned, fromDate);
        return newPolicy;
    };

    public updatePolicy = async (
        instance: Instance,
        user: User,
        policyId: number,
        newPolicyAttributes: Partial<TimeTrackingPolicyForCreation>,
        policyHourCategories: Array<TimeTrackingPolicyHourCategoryAssociation>,
    ): Promise<TimeTrackingPolicy> => {
        const oldPolicy = await this.timeTrackingPoliciesRepository.getPolicyByIdOrFail(user.instanceId, policyId);
        if (newPolicyAttributes.autoCloseMode && newPolicyAttributes.autoCloseMode !== TimeTrackingPolicyAutoCloseMode.FIXED_HOURS) {
            newPolicyAttributes.autoCloseFixedHoursValue = null;
            newPolicyAttributes.autoCloseFixedMinutesValue = null;
        }

        // Merge old and new values to validate autoCloseLimitHours constraint
        const mergedPolicy = {
            ...oldPolicy,
            ...(Object.fromEntries(
                Object.entries(newPolicyAttributes).filter(([_, value]) => value !== undefined),
            ) as Partial<TimeTrackingPolicyForCreation>),
        };

        // Enforce dependency rule: saveRemoteWorkLocation must be false when enableRemoteWork is false
        if (!mergedPolicy.enableRemoteWork) {
            mergedPolicy.saveRemoteWorkLocation = false;
        }

        this.validateAutoCloseLimitHours(mergedPolicy);

        const hasPolicyHourCategoryChanges = policyHourCategories !== undefined && policyHourCategories !== null;
        const result = await this.timeTrackingPoliciesRepository.getTransaction(async () => {
            let removedHourCategoryIds: number[] = [];
            if (hasPolicyHourCategoryChanges) {
                removedHourCategoryIds = await this.timeTrackingPoliciesRepository.associatePolicyHourCategories(
                    policyId,
                    policyHourCategories,
                    instance.getId(),
                );
            }
            const currentDate = moment.tz(instance.getTimezone()).format(ISO_DATE);
            const latenessToleranceChanged = mergedPolicy.latenessTolerance !== oldPolicy.latenessTolerance;
            const updatedPolicy = await this.timeTrackingPoliciesRepository.updatePolicy(mergedPolicy, latenessToleranceChanged ? currentDate : null);
            if (hasPolicyHourCategoryChanges && oldPolicy.userIds.length > 0) {
                const items: ScheduleCategorizedHoursParams[] = chunk(oldPolicy.userIds, CATEGORIZED_HOURS_USER_CHUNK_SIZE).map((userChunk) => ({
                    payload: {
                        date: currentDate,
                        instanceId: instance.getId(),
                        policyId,
                        removedHourCategoryIds,
                        userIds: userChunk,
                    },
                    type: CategorizedHoursActionType.POLICY_HOUR_CATEGORIES_CHANGE,
                }));
                await this.scheduleCategorizedHoursPort.bulkSchedule(items);
            }
            return updatedPolicy;
        });
        const hasTimeTrackingDesertEagleEnabledFF = await this.getTimeTrackingDesertEagleEnabledFeatureFlag(instance.getId());
        if (hasTimeTrackingDesertEagleEnabledFF) {
            await this.timeTrackingConfigService.updatePolicy(instance.getId(), result);
        }
        this.eventEmitter.emitEvent(Events.TIME_TRACKING_POLICY_UPDATED, {
            instanceId: instance.getId(),
            newPolicy: {
                clockingInKioskEnabled: result.clockingInKioskEnabled,
                notificationsEnabled: result.notificationsEnabled,
            },
            oldPolicy: {
                clockingInKioskEnabled: oldPolicy.clockingInKioskEnabled,
                notificationsEnabled: oldPolicy.notificationsEnabled,
            },
            userIds: oldPolicy.userIds,
        });
        return result;
    };

    public associatePolicy = async (
        policyId: number,
        userIds: number[],
        fromDate: string,
        allUsers: boolean,
        instanceId: number,
        emitEvent: boolean = true,
    ): Promise<{ usersNewlyAssigned: number[]; usersNowUnassigned: number[] }> => {
        const policy = await this.timeTrackingPoliciesRepository.getPolicyByIdOrFail(instanceId, policyId);
        const { usersNewlyAssigned, usersNowUnassigned } = await this.timeTrackingPoliciesRepository.getTransaction(async () => {
            const result = await this.timeTrackingPoliciesRepository.associatePolicy(policyId, userIds, fromDate, allUsers);
            const items: ScheduleCategorizedHoursParams[] = [];
            for (const userChunk of chunk(result.usersNewlyAssigned, CATEGORIZED_HOURS_USER_CHUNK_SIZE)) {
                items.push({
                    payload: { assignedUserIds: userChunk, date: fromDate, instanceId, policyId, unassignedUserIds: [] },
                    type: CategorizedHoursActionType.POLICY_ASSIGNATION_CHANGE,
                });
            }
            for (const userChunk of chunk(result.usersNowUnassigned, CATEGORIZED_HOURS_USER_CHUNK_SIZE)) {
                items.push({
                    payload: { assignedUserIds: [], date: fromDate, instanceId, policyId, unassignedUserIds: userChunk },
                    type: CategorizedHoursActionType.POLICY_ASSIGNATION_CHANGE,
                });
            }
            if (items.length > 0) {
                await this.scheduleCategorizedHoursPort.bulkSchedule(items);
            }
            return result;
        });
        if (emitEvent) {
            const hasTimeTrackingDesertEagleEnabledFF = await this.getTimeTrackingDesertEagleEnabledFeatureFlag(instanceId);
            if (hasTimeTrackingDesertEagleEnabledFF) {
                await this.timeTrackingConfigService.associatePolicy(instanceId, policy, usersNewlyAssigned, usersNowUnassigned);
            }
            this.emitPolicyAssignmentChangeEvents(instanceId, policy, usersNewlyAssigned, usersNowUnassigned, fromDate);
        }
        return { usersNewlyAssigned, usersNowUnassigned };
    };

    public eliminatePolicy = async (policyId: number, instance: Instance) => {
        const policy = await this.timeTrackingPoliciesRepository.getPolicyByIdOrFail(instance.getId(), policyId);
        const userIds = policy.userIds;
        const startDate = moment.tz(instance.getTimezone()).format(ISO_DATE);
        await this.timeTrackingPoliciesRepository.getTransaction(async () => {
            await this.timeTrackingPoliciesRepository.eliminatePolicy(policyId, instance.getId());
            if (userIds.length > 0) {
                const items: ScheduleCategorizedHoursParams[] = chunk(userIds, CATEGORIZED_HOURS_USER_CHUNK_SIZE).map((userChunk) => ({
                    payload: { assignedUserIds: [], date: startDate, instanceId: instance.getId(), policyId, unassignedUserIds: userChunk },
                    type: CategorizedHoursActionType.POLICY_ASSIGNATION_CHANGE,
                }));
                await this.scheduleCategorizedHoursPort.bulkSchedule(items);
            }
        });
        const hasTimeTrackingDesertEagleEnabledFF = await this.getTimeTrackingDesertEagleEnabledFeatureFlag(instance.getId());
        if (hasTimeTrackingDesertEagleEnabledFF) {
            await this.timeTrackingConfigService.deletePolicy(instance.getId(), policyId, userIds);
        }
        this.eventEmitter.emitEvent(Events.TIME_TRACKING_POLICY_UNASSIGNED, {
            instanceId: instance.getId(),
            policy: {
                clockingInKioskEnabled: policy.clockingInKioskEnabled,
                notificationsEnabled: policy.notificationsEnabled,
            },
            startDate,
            userIds,
        });
    };

    public getClockingInKioskEnabledUserIds = async (instanceId: number, userIds: number[] = []): Promise<number[]> => {
        const policyUsers = await this.timeTrackingPoliciesRepository.listPoliciesUsers(instanceId, {
            policyFilters: { clockingInKioskEnabled: true },
            ...(userIds.length ? { policyUserFilters: { userIds } } : {}),
        });
        return policyUsers.map((policyUser) => policyUser.userId);
    };

    public getUserIdsWithPolicies = async (instanceId: number, userIds: number[] = []): Promise<number[]> => {
        const policyUsers = await this.timeTrackingPoliciesRepository.listPoliciesUsers(instanceId, {
            policyUserFilters: { userIds },
        });
        return policyUsers.map((policyUser) => policyUser.userId);
    };

    public getPoliciesOfInstance = async (instanceId: number): Promise<TimeTrackingPolicy[]> => {
        return await this.timeTrackingPoliciesRepository.getPoliciesOfInstance(instanceId);
    };

    public updateSettingsInPolicies = async (instanceId: number, settings: TimeTrackingSetting): Promise<number[]> => {
        return await this.timeTrackingPoliciesRepository.updateSettingsInPolicies(instanceId, settings);
    };

    private getTimeTrackingDesertEagleEnabledFeatureFlag = async (instanceId: number): Promise<boolean> => {
        const featureFlag = await this.featureFlagService.getBooleanFeatureFlag(FeatureFlagCode.TIME_TRACKING_DESERT_EAGLE_ENABLED, instanceId);
        return featureFlag.value;
    };

    private validateAutoCloseLimitHours = (
        policy: Partial<TimeTrackingPolicyForCreation> & Pick<TimeTrackingPolicy, 'autoCloseMode' | 'autoCloseLimitHours'>,
    ): void => {
        if (policy.autoCloseMode !== TimeTrackingPolicyAutoCloseMode.FIXED_HOURS) {
            return;
        }

        const autoCloseLimitHours = policy.autoCloseLimitHours ?? 0;
        const autoCloseFixedHoursValue = policy.autoCloseFixedHoursValue ?? 0;
        const autoCloseFixedMinutesValue = policy.autoCloseFixedMinutesValue ?? 0;

        const fixedTotalInHours = autoCloseFixedHoursValue + autoCloseFixedMinutesValue / 60;

        if (autoCloseLimitHours < fixedTotalInHours) {
            throw new BadRequestError(
                'autoCloseLimitHours must be greater than or equal to (autoCloseFixedHoursValue + autoCloseFixedMinutesValue / 60)',
                ErrorCodes.BAD_REQUEST,
            );
        }
    };

    public listPoliciesByIds = async (policyIds: number[]): Promise<TimeTrackingPolicy[]> => {
        return await this.timeTrackingPoliciesRepository.listPoliciesByIds(policyIds);
    };

    public getApprovalSteps = async (instanceId: number, policyId: number | null): Promise<TimeTrackingPolicyApprovalStep[]> => {
        const steps = await this.approvalStepsRepository.listPolicyApprovalSteps({ instanceId, policyId });
        if (steps.length === 0 && policyId === null) {
            return this.getOrCreateDefaultGlobalApprovalSteps(instanceId);
        }
        return steps;
    };

    private getOrCreateDefaultGlobalApprovalSteps = async (instanceId: number): Promise<TimeTrackingPolicyApprovalStep[]> => {
        return this.approvalStepsRepository.transactionWithAdvisoryLock(
            `default-global-approval-steps:${instanceId}`,
            async () => {
                const existingSteps = await this.approvalStepsRepository.listPolicyApprovalSteps({ instanceId, policyId: null });
                if (existingSteps.length > 0) {
                    return existingSteps;
                }
                const defaultStep = await this.createApprovalStep(
                    { designatedReviewer: false, itemIds: [], relations: [HierarchicalRelations.BOSS], userIds: [] },
                    1,
                    instanceId,
                    null,
                );
                return [defaultStep];
            },
            { exponentialBackoff: true, isolationLevel: ISOLATION_LEVELS.READ_COMMITTED, tries: 3 },
        );
    };

    public updateApprovalSteps = async (
        instanceId: number,
        approvalUsers: ApprovalUsersGatheringInterface[],
        policyId: number | null,
    ): Promise<TimeTrackingPolicyApprovalStep[]> => {
        if (policyId === null && approvalUsers.length < 1) {
            throw new BadRequestError('Global approval steps must have at least one step', ErrorCodes.BAD_REQUEST);
        }
        return this.approvalStepsRepository.getTransaction(async () => {
            await this.approvalStepsRepository.deletePolicyApprovalSteps({ instanceId, policyId });
            const newSteps: TimeTrackingPolicyApprovalStep[] = [];
            for (let index = 0; index < approvalUsers.length; index++) {
                const step = approvalUsers[index];
                if (!step.userIds?.length && !step.relations?.length && !step.itemIds?.length && !step.designatedReviewer) {
                    throw new BadRequestError(
                        'Approval step must have at least one responsible user, relation, segmentation item or designated reviewer',
                        ErrorCodes.BAD_REQUEST,
                    );
                }
                const newStep = await this.createApprovalStep(step, index + 1, instanceId, policyId);
                newSteps.push(newStep);
            }
            return newSteps;
        });
    };

    private createApprovalStep = async (
        approvalUsersGathering: ApprovalUsersGatheringInterface,
        position: number,
        instanceId: number,
        policyId: number | null,
    ): Promise<TimeTrackingPolicyApprovalStep> => {
        const { userIds, itemIds, relations, designatedReviewer } = approvalUsersGathering;
        return this.approvalStepsRepository.getTransaction(async () => {
            const policyApprovalStep = await this.approvalStepsRepository.createPolicyApprovalStep(
                new TimeTrackingPolicyApprovalStepForCreation({ designatedReviewer: designatedReviewer ?? null, instanceId, policyId, position }),
            );

            if (relations?.length) {
                policyApprovalStep.responsibleRelations = await this.approvalStepsRepository.createPolicyApprovalStepResponsibleRelations(
                    relations.map(
                        (relation) =>
                            new TimeTrackingPolicyApprovalStepResponsibleRelationForCreation({
                                approvalStepId: policyApprovalStep.id,
                                relation,
                            }),
                    ),
                );
            }

            if (itemIds?.length) {
                policyApprovalStep.responsibleSegmentationItems =
                    await this.approvalStepsRepository.createPolicyApprovalStepResponsibleSegmentationItems(
                        itemIds.map(
                            (itemId) =>
                                new TimeTrackingPolicyApprovalStepResponsibleSegmentationItemForCreation({
                                    approvalStepId: policyApprovalStep.id,
                                    segmentationItemId: itemId,
                                }),
                        ),
                    );
            }

            if (userIds?.length) {
                policyApprovalStep.responsibleUsers = await this.approvalStepsRepository.createPolicyApprovalStepResponsibleUsers(
                    userIds.map(
                        (userId) =>
                            new TimeTrackingPolicyApprovalStepResponsibleUserForCreation({
                                approvalStepId: policyApprovalStep.id,
                                userId,
                            }),
                    ),
                );
            }

            return policyApprovalStep;
        });
    };
}
