import { BaseAdapter, CalculateCategorizedHoursBody, LoggerPort, QueueMessageType, QueueMessageTypes } from '@humand-packages/common';
import { Service } from 'typedi';
import {
    CategorizedHoursActionType,
    CategorizedHoursSchedulerMessage,
    EntriesChangePayload,
    HourCategoryChangePayload,
    PolicyAssignationChangePayload,
    PolicyHourCategoriesChangePayload,
    ScheduleChangePayload,
} from '../../business/constants/categorizedHoursActionTypes';
import { TimeTrackingQueueConsumerPort } from '../../business/ports/timeTrackingQueueConsumerPort';
import { TimeTrackingHourCategorizationService } from '../../business/services/hourCategorizationService';
import { TimeTrackingCategorizedHourNotificationsService } from '../../business/services/timeTracking/timeTrackingCategorizedHourNotificationsService';
import { TimeTrackingCategorizedHourService } from '../../business/services/timeTracking/timeTrackingCategorizedHourService';

@Service()
export class TimeTrackingQueueConsumerAdapter extends BaseAdapter implements TimeTrackingQueueConsumerPort {
    constructor(
        private readonly hourCategorizationService: TimeTrackingHourCategorizationService,
        private readonly categorizedHourService: TimeTrackingCategorizedHourService,
        private readonly categorizedHourNotificationsService: TimeTrackingCategorizedHourNotificationsService,
        private readonly logger: LoggerPort,
    ) {
        super();
    }

    public handleCalculateCategorizedHours = async (
        message: QueueMessageType<QueueMessageTypes.TIME_TRACKING_CALCULATE_CATEGORIZED_HOURS>,
    ): Promise<void> => {
        const body = message.messageBody as unknown as CategorizedHoursSchedulerMessage | undefined;
        const actionType = body?.type ?? CategorizedHoursActionType.CALCULATE_CATEGORIZED_HOURS;
        const instanceId = body?.payload?.instanceId;

        try {
            switch (actionType) {
                case CategorizedHoursActionType.CALCULATE_CATEGORIZED_HOURS: {
                    const payload = (body?.payload ?? message.messageBody) as unknown as CalculateCategorizedHoursBody;
                    const compatibleMessage: QueueMessageType<QueueMessageTypes.TIME_TRACKING_CALCULATE_CATEGORIZED_HOURS> = {
                        ...message,
                        messageBody: payload,
                    };
                    await this.hourCategorizationService.handleCalculateCategorizedHoursQueue(compatibleMessage);
                    break;
                }
                case CategorizedHoursActionType.POLICY_ASSIGNATION_CHANGE:
                    await this.categorizedHourService.handlePolicyAsignationChanges(body!.payload as PolicyAssignationChangePayload);
                    break;
                case CategorizedHoursActionType.HOUR_CATEGORY_CHANGE: {
                    const hcPayload = body!.payload as HourCategoryChangePayload;
                    await this.categorizedHourService.handleHourCategoryChanges(hcPayload.instanceId, hcPayload.hourCategoryId);
                    break;
                }
                case CategorizedHoursActionType.HOUR_CATEGORY_DELETION: {
                    const hcdPayload = body!.payload as HourCategoryChangePayload;
                    await this.categorizedHourService.handleHourCategoryDeletion(hcdPayload.instanceId, hcdPayload.hourCategoryId);
                    break;
                }
                case CategorizedHoursActionType.ENTRIES_CHANGE: {
                    const ecPayload = body!.payload as EntriesChangePayload;
                    await this.categorizedHourService.handleEntriesChanges(ecPayload.instanceId, ecPayload.userId, ecPayload.dateString);
                    break;
                }
                case CategorizedHoursActionType.POLICY_HOUR_CATEGORIES_CHANGE: {
                    const phcPayload = body!.payload as PolicyHourCategoriesChangePayload;
                    await this.categorizedHourService.handlePolicyHourCategoriesChange(phcPayload);
                    break;
                }
                case CategorizedHoursActionType.SCHEDULE_CHANGE: {
                    const scPayload = body!.payload as ScheduleChangePayload;
                    await this.categorizedHourService.handleScheduleChanges(
                        scPayload.instanceId,
                        scPayload.userIds,
                        scPayload.startDate,
                        scPayload.endDate,
                    );
                    break;
                }
            }
            this.logger.info(
                { actionType, instanceId, messageId: message.messageId },
                '[TimeTrackingQueueConsumerAdapter] Message processed successfully',
            );
        } catch (error) {
            this.logger.error(
                { actionType, error, instanceId, messageId: message.messageId },
                '[TimeTrackingQueueConsumerAdapter] Error processing message',
            );
            throw error;
        }
    };

    public handleNotifyBulkCategorizedHourReviewed = async (
        message: QueueMessageType<QueueMessageTypes.TIME_TRACKING_NOTIFY_BULK_CATEGORIZED_HOUR_REVIEWED>,
    ): Promise<void> => {
        if (!message.messageBody) {
            this.logger.warn(
                { messageId: message.messageId },
                '[TimeTrackingQueueConsumerAdapter] Bulk categorized-hour notification received empty message body',
            );
            return;
        }
        const { instanceId, reviewedSteps } = message.messageBody;
        for (const { categorizedHourId, approvalStepId } of reviewedSteps) {
            try {
                await this.categorizedHourNotificationsService.notifyCategorizedHourStepReviewed(instanceId, categorizedHourId, approvalStepId);
            } catch (err) {
                this.logger.error(
                    { approvalStepId, categorizedHourId, err, instanceId, messageId: message.messageId },
                    '[TimeTrackingQueueConsumerAdapter] Bulk categorized-hour notification failed',
                );
            }
        }
    };
}
