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 { TimeTrackingQueueMessageTypeValues } from '../constants/timeTrackingQueueMessageTypes';
import { TimeTrackingQueueConsumerPort } from '../ports/timeTrackingQueueConsumerPort';

@Service()
export class TimeTrackingQueueConsumer extends BaseQueueConsumer {
    constructor(
        queuePort: QueuePort,
        promisesPort: PromisesPort,
        @PortsDI(TimeTrackingQueueConsumerPort)
        private readonly queueConsumerPort: TimeTrackingQueueConsumerPort,
        @PortsDI(DeadLetterQueueRepositoryPort) protected readonly dlqRepository: DeadLetterQueueRepositoryPort,
        logger: LoggerPort,
    ) {
        const config = getConfig();
        super(
            {
                dlqMessageAttempts: config.awsSqsTimeTrackingCategorizedHoursDlqMessageAttempts!,
                intervalSeconds: config.awsSqsTimeTrackingCategorizedHoursIntervalSeconds!,
                maxConcurrentMessages: config.awsSqsTimeTrackingCategorizedHoursMaxConcurrentMessages!,
                maxConcurrentTasks: config.awsSqsTimeTrackingCategorizedHoursMaxConcurrentTasks!,
                queueConfig: {
                    dlqUrl: config.awsSqsTimeTrackingCategorizedHoursQueueDlqUrl!,
                    maxNumberOfMessages: config.awsSqsTimeTrackingCategorizedHoursMaxNumberMessages!,
                    url: config.awsSqsTimeTrackingCategorizedHoursQueueUrl!,
                    visibilityTimeout: config.awsSqsTimeTrackingCategorizedHoursVisibilityTimeout!,
                    waitTimeSeconds: config.awsSqsTimeTrackingCategorizedHoursWaitTimeSeconds!,
                },
            },
            queuePort,
            promisesPort,
            logger,
            dlqRepository,
            ALL_QUEUES.TIME_TRACKING_CATEGORIZED_HOURS,
        );
    }

    private readonly messageHandlers: Record<
        TimeTrackingQueueMessageTypeValues,
        (message: QueueMessageType<TimeTrackingQueueMessageTypeValues>) => Promise<unknown>
    > = {
        [QueueMessageTypes.TIME_TRACKING_CALCULATE_CATEGORIZED_HOURS]: (message) =>
            this.runMessageHandler(message, () =>
                this.queueConsumerPort.handleCalculateCategorizedHours(
                    message as QueueMessageType<QueueMessageTypes.TIME_TRACKING_CALCULATE_CATEGORIZED_HOURS>,
                ),
            ),
        [QueueMessageTypes.TIME_TRACKING_NOTIFY_BULK_CATEGORIZED_HOUR_REVIEWED]: (message) =>
            this.runMessageHandler(message, () =>
                this.queueConsumerPort.handleNotifyBulkCategorizedHourReviewed(
                    message as QueueMessageType<QueueMessageTypes.TIME_TRACKING_NOTIFY_BULK_CATEGORIZED_HOUR_REVIEWED>,
                ),
            ),
    };

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

        if (!handler && message.receiptHandle) {
            this.logger.warn(
                message,
                `[TimeTrackingQueueConsumer] 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 },
            '[TimeTrackingQueueConsumer] Processing message from queue',
        );

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

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

        if (!handler && message.receiptHandle) {
            this.logger.warn(
                message,
                `[TimeTrackingQueueConsumer] 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 },
            '[TimeTrackingQueueConsumer] Processing message from DLQ (retry)',
        );

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