import { ALL_QUEUES, LoggerPort, PromisesPort, QueueMessageType, QueueMessageTypes, QueuePort } from '@humand-packages/common';
import { Service } from 'typedi';
import { getConfig } from '../../../../apiConfig/configSetup';
import { PortsDI } from '../../../../config/portsDI';
import { DeadLetterQueueRepositoryPort } from '../../../deferredExecution/business/ports/deadLetterQueueRepositoryPort';
import { BaseQueueConsumer } from '../../../deferredExecution/business/services/baseQueueConsumer';
import { TimeTrackingFaceAssignmentQueueMessageTypeValues } from '../constants/timeTrackingFaceAssignmentQueueMessageTypes';
import { TimeTrackingFaceAssignmentQueueConsumerPort } from '../ports/timeTrackingFaceAssignmentQueueConsumerPort';

@Service()
export class TimeTrackingFaceAssignmentQueueConsumer extends BaseQueueConsumer {
    constructor(
        queuePort: QueuePort,
        promisesPort: PromisesPort,
        @PortsDI(TimeTrackingFaceAssignmentQueueConsumerPort)
        private readonly queueConsumerPort: TimeTrackingFaceAssignmentQueueConsumerPort,
        @PortsDI(DeadLetterQueueRepositoryPort) protected readonly dlqRepository: DeadLetterQueueRepositoryPort,
        logger: LoggerPort,
    ) {
        const config = getConfig();
        super(
            {
                dlqMessageAttempts: config.awsSqsTimeTrackingFaceAssignmentsDlqMessageAttempts!,
                intervalSeconds: config.awsSqsTimeTrackingFaceAssignmentsIntervalSeconds!,
                maxConcurrentMessages: config.awsSqsTimeTrackingFaceAssignmentsMaxConcurrentMessages!,
                maxConcurrentTasks: config.awsSqsTimeTrackingFaceAssignmentsMaxConcurrentTasks!,
                queueConfig: {
                    dlqUrl: config.awsSqsTimeTrackingFaceAssignmentsQueueDlqUrl!,
                    maxNumberOfMessages: config.awsSqsTimeTrackingFaceAssignmentsMaxNumberMessages!,
                    url: config.awsSqsTimeTrackingFaceAssignmentsQueueUrl!,
                    visibilityTimeout: config.awsSqsTimeTrackingFaceAssignmentsVisibilityTimeout!,
                    waitTimeSeconds: config.awsSqsTimeTrackingFaceAssignmentsWaitTimeSeconds!,
                },
            },
            queuePort,
            promisesPort,
            logger,
            dlqRepository,
            ALL_QUEUES.TIME_TRACKING_FACE_ASSIGNMENTS,
        );
    }

    private readonly messageHandlers: Record<
        TimeTrackingFaceAssignmentQueueMessageTypeValues,
        (message: QueueMessageType<TimeTrackingFaceAssignmentQueueMessageTypeValues>) => Promise<unknown>
    > = {
        [QueueMessageTypes.TIME_TRACKING_FACE_ASSIGNMENT]: (message) =>
            this.runMessageHandler(message, () =>
                this.queueConsumerPort.handleFaceAssignment(message as QueueMessageType<QueueMessageTypes.TIME_TRACKING_FACE_ASSIGNMENT>),
            ),
    };

    protected invokeQueueMessageHandlers(message: QueueMessageType<QueueMessageTypes>): Promise<unknown> {
        const typedMessage = message as QueueMessageType<TimeTrackingFaceAssignmentQueueMessageTypeValues>;
        const messageType = typedMessage.messageType;
        const handler = messageType ? this.messageHandlers[messageType] : undefined;

        if (!handler && message.receiptHandle) {
            this.logger.warn(
                message,
                `[TimeTrackingFaceAssignmentQueueConsumer] Removing message from queue because no handler was found. Message type: ${message.messageType}`,
            );
            return this.queuePort.delete(this.config.queueConfig.url, message.receiptHandle);
        }

        this.logger.info(
            { instanceId: (message.messageBody as { instanceId?: number })?.instanceId, messageId: message.messageId, messageType },
            '[TimeTrackingFaceAssignmentQueueConsumer] Processing message from queue',
        );

        return handler!(typedMessage).then((result) => {
            this.logger.info({ messageType: message.messageType }, '[TimeTrackingFaceAssignmentQueueConsumer] Message processed successfully');
            return result;
        });
    }

    protected invokeDlqMessageHandlers(message: QueueMessageType<QueueMessageTypes>): Promise<unknown> {
        const typedMessage = message as QueueMessageType<TimeTrackingFaceAssignmentQueueMessageTypeValues>;
        const messageType = typedMessage.messageType;
        const handler = messageType ? this.messageHandlers[messageType] : undefined;

        if (!handler && message.receiptHandle) {
            this.logger.warn(
                message,
                `[TimeTrackingFaceAssignmentQueueConsumer] Removing message from DLQ because no handler was found. Message type: ${message.messageType}`,
            );
            return this.queuePort.delete(this.config.queueConfig.dlqUrl!, message.receiptHandle);
        }

        this.logger.info(
            { instanceId: (message.messageBody as { instanceId?: number })?.instanceId, messageId: message.messageId, messageType },
            '[TimeTrackingFaceAssignmentQueueConsumer] Processing message from DLQ (retry)',
        );

        return handler!(typedMessage).then((result) => {
            this.logger.info({ messageType: message.messageType }, '[TimeTrackingFaceAssignmentQueueConsumer] DLQ message processed successfully');
            return result;
        });
    }
}
