import { TimeTrackingConfigService } from '@humand/desert-eagle-grpc-ts-client';
import { Empty } from '@humand/desert-eagle-grpc-ts-client/dist/generated/google/protobuf/empty';
import { BasePort, Envs, getGrpcCredentials, LoggerPort, PromisifiedFunction, promisifyGrpcRequest } from '@humand-packages/common';

import { Service } from 'typedi';
import { getConfig } from '../../../../../../apiConfig/configSetup';

import { TimeTrackingPolicy } from '../../../../business/models/timeTrackingPolicy';
import { timeTrackingPolicyToTimeTrackingPolicyGrpc } from '../../../mappers/timeTracking/timeTrackingConfigMapper';
import { TimeTrackingConfigRepositoryPort } from '../../../ports/timeTracking/timeTrackingConfigRepositoryPort';

@Service()
export default class TimeTrackingConfigRepositoryAdapter extends BasePort implements TimeTrackingConfigRepositoryPort {
    constructor(private readonly logger: LoggerPort) {
        super();
        const config = getConfig();
        const env = config.env as Envs;
        this.client = new TimeTrackingConfigService.TimeTrackingConfigServiceClient(
            config.desertEagleServiceAddress || ':50051',
            getGrpcCredentials(env),
        );
        this.grpcAssociatePolicy = promisifyGrpcRequest(this.client.associatePolicy.bind(this.client));
        this.grpcUpdatePolicy = promisifyGrpcRequest(this.client.updatePolicy.bind(this.client));
        this.grpcDeletePolicy = promisifyGrpcRequest(this.client.deletePolicy.bind(this.client));
        this.grpcUpdateUserSetting = promisifyGrpcRequest(this.client.updateUserSetting.bind(this.client));
        this.grpcUpdateReferenceImage = promisifyGrpcRequest(this.client.updateReferenceImage.bind(this.client));
        this.grpcDeleteReferenceImage = promisifyGrpcRequest(this.client.deleteReferenceImage.bind(this.client));
    }

    private readonly client: TimeTrackingConfigService.TimeTrackingConfigServiceClient;
    private readonly grpcAssociatePolicy: PromisifiedFunction<TimeTrackingConfigService.AssociateTimeTrackingPolicyRequest, Empty>;
    private readonly grpcUpdatePolicy: PromisifiedFunction<TimeTrackingConfigService.UpdateTimeTrackingPolicyRequest, Empty>;
    private readonly grpcDeletePolicy: PromisifiedFunction<TimeTrackingConfigService.DeleteTimeTrackingPolicyRequest, Empty>;
    private readonly grpcUpdateUserSetting: PromisifiedFunction<TimeTrackingConfigService.UpdateTimeTrackingKioskPinRequest, Empty>;
    private readonly grpcUpdateReferenceImage: PromisifiedFunction<TimeTrackingConfigService.UpdateFaceRecognitionReferenceImageRequest, Empty>;
    private readonly grpcDeleteReferenceImage: PromisifiedFunction<TimeTrackingConfigService.DeleteFaceRecognitionReferenceImageRequest, Empty>;

    async associatePolicy(instanceId: number, policy: TimeTrackingPolicy, assignedUserIds: number[], unassignedUserIds: number[]): Promise<void> {
        const grpcPolicy = timeTrackingPolicyToTimeTrackingPolicyGrpc(policy);
        const req = {
            assignedUserIds: assignedUserIds,
            instanceId,
            policy: grpcPolicy,
            unassignedUserIds: unassignedUserIds,
        } as TimeTrackingConfigService.AssociateTimeTrackingPolicyRequest;
        try {
            await this.grpcAssociatePolicy(req);
        } catch (error) {
            this.logger.error(error, `Error associating time tracking policy id ${policy.id} for instance ${instanceId}`);
        }
    }

    async updatePolicy(instanceId: number, policy: TimeTrackingPolicy): Promise<void> {
        const grpcPolicy = timeTrackingPolicyToTimeTrackingPolicyGrpc(policy);
        const req = {
            instanceId,
            policy: grpcPolicy,
        } as TimeTrackingConfigService.UpdateTimeTrackingPolicyRequest;
        try {
            await this.grpcUpdatePolicy(req);
        } catch (error) {
            this.logger.error(error, `Error updating time tracking policy id ${policy.id} for instance ${instanceId}`);
        }
    }

    async deletePolicy(instanceId: number, policyId: number, userIds: number[]): Promise<void> {
        const req = {
            instanceId,
            policyId,
            userIds,
        } as TimeTrackingConfigService.DeleteTimeTrackingPolicyRequest;
        try {
            await this.grpcDeletePolicy(req);
        } catch (error) {
            this.logger.error(error, `Error deleting time tracking policy id ${policyId} for instance ${instanceId}`);
        }
    }

    async updateUserSetting(instanceId: number, userId: number, kioskPin: string | null): Promise<void> {
        const req = {
            instanceId,
            kioskPin: kioskPin ?? '',
            userId,
        } as TimeTrackingConfigService.UpdateTimeTrackingKioskPinRequest;
        try {
            await this.grpcUpdateUserSetting(req);
        } catch (error) {
            this.logger.error(error, `Error updating user setting for user ${userId} for instance ${instanceId}`);
        }
    }

    async updateReferenceImage(instanceId: number, userId: number, referenceImage: string): Promise<void> {
        const req = {
            instanceId,
            referenceImage,
            userId,
        } as TimeTrackingConfigService.UpdateFaceRecognitionReferenceImageRequest;
        try {
            await this.grpcUpdateReferenceImage(req);
        } catch (error) {
            this.logger.error(error, `Error updating reference image for user ${userId} for instance ${instanceId}`);
        }
    }

    async deleteReferenceImage(instanceId: number, userId: number): Promise<void> {
        const req = {
            instanceId,
            userId,
        } as TimeTrackingConfigService.DeleteFaceRecognitionReferenceImageRequest;
        try {
            await this.grpcDeleteReferenceImage(req);
        } catch (error) {
            this.logger.error(error, `Error deleting reference image for user ${userId} for instance ${instanceId}`);
        }
    }
}
