package com.iqser.red.service.persistence.management.v1.processor.service.download;

import static com.iqser.red.service.persistence.management.v1.processor.configuration.MessagingConfiguration.X_ERROR_INFO_HEADER;

import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.stereotype.Service;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.iqser.red.service.persistence.management.v1.processor.configuration.MessagingConfiguration;
import com.iqser.red.service.persistence.management.v1.processor.model.DownloadJob;
import com.iqser.red.service.persistence.management.v1.processor.service.websocket.WebsocketService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DownloadStatusPersistenceService;
import com.iqser.red.service.persistence.service.v1.api.shared.model.download.DownloadStatusValue;
import com.iqser.red.service.redaction.report.v1.api.model.ReportRequestMessage;
import com.iqser.red.service.redaction.report.v1.api.model.ReportResultMessage;

import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
@RequiredArgsConstructor
public class DownloadDLQMessageReceiver {

    private final DownloadStatusPersistenceService downloadStatusPersistenceService;
    private final RetryTemplate retryTemplate;
    private final ObjectMapper objectMapper;
    private final static int MAX_ERROR_CAUSE_LENGTH = 1024;
    private final WebsocketService websocketService;


    @RabbitListener(queues = MessagingConfiguration.DOWNLOAD_COMPRESSION_DLQ)
    public void handleCompressionDlqMessage(DownloadJob downloadJob) {

        log.warn("Handling download compression in DLQ userId: {} storageId: {} - setting status to error!", downloadJob.getUserId(), downloadJob.getStorageId());
        setDownloadFailed(downloadJob.getUserId(), downloadJob.getStorageId());
    }


    @SneakyThrows
    @RabbitListener(queues = MessagingConfiguration.DOWNLOAD_DLQ)
    public void handleDlqMessage(Message failedMessage) {

        var downloadJob = objectMapper.readValue(failedMessage.getBody(), DownloadJob.class);
        String errorCause = failedMessage.getMessageProperties().getHeader(X_ERROR_INFO_HEADER);
        errorCause = errorCause.substring(0, MAX_ERROR_CAUSE_LENGTH - 1);
        if (errorCause == null) {
            errorCause = "Unknown error occured in download queue!";
        }
        log.warn("Handling download job in DLQ userId: {} storageId: {} - setting status to error! cause: {}", downloadJob.getUserId(), downloadJob.getStorageId(), errorCause);
        setDownloadFailed(downloadJob.getUserId(), downloadJob.getStorageId(), errorCause);
    }


    @SneakyThrows
    @RabbitListener(queues = MessagingConfiguration.REPORT_REQUEST_DLQ)
    public void handleReportDlqMessage(Message failedMessage) {

        var reportRequestMessage = objectMapper.readValue(failedMessage.getBody(), ReportRequestMessage.class);
        String errorCause = failedMessage.getMessageProperties().getHeader(X_ERROR_INFO_HEADER);
        if (errorCause == null) {
            errorCause = "Unknown error occured in report redaction queue!";
        } else if (errorCause.length() > MAX_ERROR_CAUSE_LENGTH) {
            errorCause = errorCause.substring(0, MAX_ERROR_CAUSE_LENGTH - 1);
        }

        log.warn("Handling report request in DLQ userId: {} storageId: {} - setting status to error!", reportRequestMessage.getUserId(), reportRequestMessage.getDownloadId());
        setDownloadFailed(reportRequestMessage.getUserId(), reportRequestMessage.getDownloadId(), errorCause);
    }


    @RabbitListener(queues = MessagingConfiguration.REPORT_RESPONSE_DLQ)
    public void handleReportResponseDlqMessage(ReportResultMessage reportResultMessage) {

        log.warn("Handling report response in DLQ userId: {} storageId: {} - setting status to error!", reportResultMessage.getUserId(), reportResultMessage.getDownloadId());
        setDownloadFailed(reportResultMessage.getUserId(), reportResultMessage.getDownloadId());
    }


    private void setDownloadFailed(String userId, String downloadId) {

        retryTemplate.execute(retryContext -> {
            log.warn("Retrying {} time to set FAILED status for downloadJob userId: {} storageId: {}", retryContext.getRetryCount(), userId, downloadId);
            downloadStatusPersistenceService.updateStatus(downloadId, DownloadStatusValue.FAILED, "error occured in unknown queue");
            return null;
        });
        websocketService.sendDownloadEvent(downloadId, userId, DownloadStatusValue.FAILED);
    }


    private void setDownloadFailed(String userId, String downloadId, String errorCause) {

        retryTemplate.execute(retryContext -> {
            log.warn("Retrying {} time to set FAILED status for downloadJob userId: {} storageId: {}", retryContext.getRetryCount(), userId, downloadId);
            downloadStatusPersistenceService.updateStatus(downloadId, DownloadStatusValue.FAILED, errorCause);
            return null;
        });
        websocketService.sendDownloadEvent(downloadId, userId, DownloadStatusValue.FAILED);
    }

}
