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

import org.apache.commons.lang3.StringUtils;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.bind.annotation.RestController;

import com.iqser.red.service.persistence.management.v1.processor.exception.ConflictException;
import com.iqser.red.service.persistence.management.v1.processor.service.manualredactions.ManualRedactionService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.FileStatusPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.settings.FileManagementServiceSettings;
import com.iqser.red.service.persistence.service.v1.api.shared.model.AnalyzeResult;
import com.iqser.red.service.persistence.service.v1.api.shared.model.UntouchedDocumentResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileErrorInfo;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.ProcessingStatus;
import com.iqser.red.service.search.v1.model.IndexMessageType;

import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RestController
@RequiredArgsConstructor
public class FileStatusProcessingUpdateService {

    private final RetryTemplate retryTemplate;
    private final FileStatusService fileStatusService;
    private final IndexingService indexingService;
    private final DossierPersistenceService dossierPersistenceService;
    private final AnalysisFlagsCalculationService analysisFlagsCalculationService;
    private final ManualRedactionService manualRedactionService;
    private final FileManagementServiceSettings settings;
    private final FileStatusPersistenceService fileStatusPersistenceService;


    public void analysisSuccessful(String dossierId, String fileId, AnalyzeResult analyzeResult) {

        var dossier = dossierPersistenceService.getAndValidateDossier(dossierId);

        switch (analyzeResult.getMessageType()) {
            
            case ANALYSE:
            case REANALYSE:
            default:
                retryTemplate.execute(retryContext -> {
                    log.info("Analysis Successful for dossier {} and file {}, Attempt to update status: {}", dossierId, fileId, retryContext.getRetryCount());
                    fileStatusService.setStatusSuccessful(fileId, analyzeResult);
                    return null;
                });

                if (!analyzeResult.isWasReanalyzed()) {
                    indexingService.addToIndexingQueue(IndexMessageType.INSERT, dossier.getDossierTemplateId(), dossierId, fileId, 2);
                }

                manualRedactionService.updateProcessedDate(analyzeResult.getManualRedactions());
                analysisFlagsCalculationService.calculateFlags(dossierId, fileId);

                if (analyzeResult.getAddedFileAttributes() != null && !analyzeResult.getAddedFileAttributes().isEmpty()) {
                    fileStatusPersistenceService.addFileAttributes(dossierId, fileId, analyzeResult.getAddedFileAttributes());
                }
        }

    }

    @Transactional
    public void preprocessingSuccessful(String dossierId, String fileId, UntouchedDocumentResponse untouchedDocumentResponse) {

        fileStatusService.updateProcessingStatusPreprocessed(dossierId, fileId, untouchedDocumentResponse.isHasHighlights(), untouchedDocumentResponse.getFileSize());
    }


    public void preProcessing(String dossierId, String fileId) {

        var fileEntity = fileStatusPersistenceService.getStatus(fileId);

        retryTemplate.execute(retryContext -> {
            log.info("Preprocessing dossier {} and file {}, Attempt to update status: {}", dossierId, fileId, retryContext.getRetryCount());
            fileStatusService.setStatusPreProcessing(fileId,
                    fileEntity.getProcessingStatus().equals(ProcessingStatus.PRE_PROCESSING) ? fileEntity.getProcessingErrorCounter() + 1 : 0);
            return null;
        });

        var updatedFileEntity = fileStatusPersistenceService.getStatus(fileId);
        if (updatedFileEntity.getProcessingErrorCounter() > settings.getMaxErrorRetries()) {
            throw new ConflictException(String.format("Max Processing Retries exhausted for dossier %s and file %s with errorCount: %s",
                    dossierId,
                    fileId,
                    updatedFileEntity.getProcessingErrorCounter()));
        }
    }


    public void preprocessingFailed(String dossierId, String fileId, FileErrorInfo fileErrorInfo) {
        if (fileErrorInfo != null && StringUtils.isEmpty(fileErrorInfo.getCause())) {
            fileErrorInfo.setCause("preprocessingFailed");
        }
        setStatusError(dossierId, fileId, fileErrorInfo);
    }


    private void setStatusError(String dossierId, String fileId, FileErrorInfo fileErrorInfo) {

        retryTemplate.execute(retryContext -> {
            log.warn("Retrying {} time to set ERROR status for file {} in dossier {} with reason {} ", retryContext.getRetryCount(), fileId, dossierId, fileErrorInfo != null ? fileErrorInfo.getCause() : null);
            fileStatusService.setStatusError(fileId, fileErrorInfo);
            return null;
        });
    }


    public void ocrProcessing(String dossierId, String fileId) {

        var fileEntity = fileStatusPersistenceService.getStatus(fileId);

        retryTemplate.execute(retryContext -> {
            log.info("Ocr processing dossier {} and file {}, Attempt to update status: {}", dossierId, fileId, retryContext.getRetryCount());
            fileStatusService.setStatusOcrProcessing(fileId,
                    fileEntity.getProcessingStatus().equals(ProcessingStatus.OCR_PROCESSING) ? fileEntity.getProcessingErrorCounter() + 1 : 0);
            return null;
        });

        var updatedFileEntity = fileStatusPersistenceService.getStatus(fileId);
        if (updatedFileEntity.getProcessingErrorCounter() > settings.getMaxErrorRetries()) {
            throw new ConflictException(String.format("Max Ocr Retries exhausted for dossier %s and file %s with errorCount: %s",
                    dossierId,
                    fileId,
                    updatedFileEntity.getProcessingErrorCounter()));
        }
    }


    public void ocrSuccessful(String dossierId, String fileId) {

        retryTemplate.execute(retryContext -> {
            log.info("OCR Successful for dossier {} and file {}, Attempt to update status: {}", dossierId, fileId, retryContext.getRetryCount());

            fileStatusService.setStatusFullReprocess(dossierId, fileId, false, true);

            return null;
        });
    }


    public void analysisFailed(String dossierId, String fileId, FileErrorInfo fileErrorInfo) {

        if (fileErrorInfo != null && StringUtils.isEmpty(fileErrorInfo.getCause())) {
            fileErrorInfo.setCause("analysisFailed");
        }
        setStatusError(dossierId, fileId, fileErrorInfo);
    }


    public void ocrFailed(String dossierId, String fileId, FileErrorInfo fileErrorInfo) {

        if (fileErrorInfo != null && StringUtils.isEmpty(fileErrorInfo.getCause())) {
            fileErrorInfo.setCause("ocrFailed");
        }
        setStatusError(dossierId, fileId, fileErrorInfo);
    }


    public void indexing(String dossierId, String fileId) {

        retryTemplate.execute(retryContext -> {
            log.info("Indexing dossier {} and file {}, Attempt to update status: {}", dossierId, fileId, retryContext.getRetryCount());
            fileStatusService.setStatusIndexing(fileId);
            return null;
        });
    }


    public void indexingSuccessful(String dossierId, String fileId) {

        retryTemplate.execute(retryContext -> {
            log.info("Indexing Successful for dossier {} and file {}, Attempt to update status: {}", dossierId, fileId, retryContext.getRetryCount());
            fileStatusService.setStatusIndexingSuccessful(fileId);
            return null;
        });
    }


    public void indexingFailed(String dossierId, String fileId, FileErrorInfo fileErrorInfo) {

        if (fileErrorInfo != null && StringUtils.isEmpty(fileErrorInfo.getCause())) {
            fileErrorInfo.setCause("indexingFailed");
        }
        setStatusError(dossierId, fileId, fileErrorInfo);
    }

}
