package com.iqser.red.service.peristence.v1.server.integration.tests;

import static org.assertj.core.api.Assertions.assertThat;

import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockMultipartFile;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.iqser.red.service.pdftron.redaction.v1.api.model.RedactionResultDetail;
import com.iqser.red.service.pdftron.redaction.v1.api.model.RedactionResultMessage;
import com.iqser.red.service.peristence.v1.server.integration.client.DossierClient;
import com.iqser.red.service.peristence.v1.server.integration.client.DownloadClient;
import com.iqser.red.service.peristence.v1.server.integration.client.FileClient;
import com.iqser.red.service.peristence.v1.server.integration.client.ReportTemplateClient;
import com.iqser.red.service.peristence.v1.server.integration.service.DossierTemplateTesterAndProvider;
import com.iqser.red.service.peristence.v1.server.integration.service.DossierTesterAndProvider;
import com.iqser.red.service.peristence.v1.server.integration.service.FileTesterAndProvider;
import com.iqser.red.service.peristence.v1.server.integration.utils.AbstractPersistenceServerServiceTest;
import com.iqser.red.service.persistence.management.v1.processor.service.download.DownloadReportMessageReceiver;
import com.iqser.red.service.persistence.management.v1.processor.service.download.RedactionResultMessageReceiver;
import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.DossierTemplateModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.FileStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.PrepareDownloadWithOptionRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DownloadFileType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.ReportTemplate;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.Dossier;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.WorkflowStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.download.DownloadStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.download.DownloadStatusValue;
import com.iqser.red.service.redaction.report.v1.api.model.ReportResultMessage;
import com.iqser.red.service.redaction.report.v1.api.model.StoredFileInformation;
import com.iqser.red.storage.commons.service.StorageService;
import com.knecon.fforesight.tenantcommons.TenantContext;

import lombok.AccessLevel;
import lombok.SneakyThrows;
import lombok.experimental.FieldDefaults;

@FieldDefaults(level = AccessLevel.PRIVATE)
public class DownloadPreparationTest extends AbstractPersistenceServerServiceTest {

    protected static final String USER_ID = "1";
    @Autowired
    DownloadReportMessageReceiver downloadReportMessageReceiver;

    @Autowired
    RedactionResultMessageReceiver redactionResultMessageReceiver;

    @Autowired
    StorageService storageService;

    @Autowired
    FileTesterAndProvider fileTesterAndProvider;

    @Autowired
    DossierTesterAndProvider dossierTesterAndProvider;

    @Autowired
    DownloadClient downloadClient;

    @Autowired
    DossierTemplateTesterAndProvider dossierTemplateTesterAndProvider;

    @Autowired
    ReportTemplateClient reportTemplateClient;

    @Autowired
    DossierClient dossierClient;

    @Autowired
    FileClient fileClient;

    DossierWithSingleFile testData;


    @BeforeEach
    public void createTestData() {

        testData = new DossierWithSingleFile();
    }


    @Test
    @SneakyThrows
    public void testReceiveDownloadPackage() {

        testData.forwardFileToApprovedState();

        uploadMockReportTemplate(testData);

        var availableTemplates = reportTemplateClient.getAvailableReportTemplates(testData.getDossierTemplateId());
        assertThat(availableTemplates).isNotEmpty();

        createDossierInService(testData, availableTemplates);

        var updatedDossier = dossierClient.getDossier(testData.getDossierId(), false, false);
        assertThat(updatedDossier.getReportTemplateIds()).isNotEmpty();

        downloadClient.prepareDownload(PrepareDownloadWithOptionRequest.builder()
                .dossierId(testData.getDossierId())
                .downloadFileTypes(Set.of(DownloadFileType.ORIGINAL))
                .fileIds(Collections.singletonList(testData.file.getId()))
                .redactionPreviewColor("#aaaaaa")
                .build());

        List<DownloadStatus> downloadStatuses = downloadClient.getDownloadStatus().getDownloadStatus();
        assertThat(downloadStatuses).hasSize(1);

        DownloadStatus firstDownloadStatus = downloadStatuses.get(0);
        assertThat(firstDownloadStatus.getLastDownload()).isNull();

        String downloadId = firstDownloadStatus.getStorageId();

        addStoredFileInformationToStorage(testData.file, availableTemplates, downloadId);

        setupTenantContext();

        ReportResultMessage reportResultMessage = new ReportResultMessage();
        reportResultMessage.setUserId(USER_ID);
        reportResultMessage.setDownloadId(downloadId);
        downloadReportMessageReceiver.receive(reportResultMessage);

        redactionResultMessageReceiver.receive(RedactionResultMessage.builder()
                .downloadId(downloadId)
                .dossierId(testData.getDossierId())
                .fileId(testData.getFileId())
                .redactionResultDetails(Collections.emptyList())
                .build());

        List<DownloadStatus> finalDownloadStatuses = downloadClient.getDownloadStatus().getDownloadStatus();
        assertThat(finalDownloadStatuses).hasSize(1);
        DownloadStatus finalDownloadStatus = finalDownloadStatuses.get(0);
        assertThat(finalDownloadStatus.getStatus()).isEqualTo(DownloadStatusValue.READY);
        assertThat(finalDownloadStatus.getFileSize()).isGreaterThan(0);

        clearTenantContext();
    }


    private void createDossierInService(DossierWithSingleFile testData, List<ReportTemplate> availableTemplates) {

        dossierClient.createDossierOrUpdateDossier(DossierRequest.builder()
                .dossierId(testData.getDossierId())
                .dossierName(testData.dossier.getDossierName())
                .description(testData.dossier.getDescription())
                .ownerId(testData.dossier.getOwnerId())
                .memberIds(testData.dossier.getMemberIds())
                .approverIds(testData.dossier.getApproverIds())
                .downloadFileTypes(testData.dossier.getDownloadFileTypes())
                .watermarkId(testData.dossier.getWatermarkId())
                .dueDate(testData.dossier.getDueDate())
                .dossierTemplateId(testData.dossier.getDossierTemplateId())
                .reportTemplateIds(availableTemplates.stream().map(ReportTemplate::getTemplateId).collect(Collectors.toSet()))
                .build());
    }


    private void uploadMockReportTemplate(DossierWithSingleFile testData) {

        var template = new MockMultipartFile("test.docx", "zzz".getBytes());
        reportTemplateClient.uploadTemplate(template, testData.getDossierTemplateId(), true, true);
    }


    @SneakyThrows
    private void addStoredFileInformationToStorage(FileStatus file, List<ReportTemplate> availableTemplates, String downloadId) {

        var storedFileInformationstorageId = downloadId.substring(0, downloadId.length() - 3) + "/REPORT_INFO.json";
        String reportStorageId = "XYZ";
        var sivList = new ArrayList<StoredFileInformation>();

        var siv = new StoredFileInformation();
        siv.setFileId(file.getId());
        siv.setStorageId(reportStorageId);
        siv.setTemplateId(availableTemplates.iterator().next().getTemplateId());
        sivList.add(siv);

        storageService.storeObject(TenantContext.getTenantId(), storedFileInformationstorageId, new ByteArrayInputStream(new ObjectMapper().writeValueAsBytes(sivList)));
        storageService.storeObject(TenantContext.getTenantId(), reportStorageId, new ByteArrayInputStream(new byte[]{1, 2, 3, 4}));
    }


    @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
    private final class DossierWithSingleFile {

        DossierTemplateModel dossierTemplate = dossierTemplateTesterAndProvider.provideTestTemplate();

        Dossier dossier = dossierTesterAndProvider.provideTestDossier(dossierTemplate);

        FileStatus file = fileTesterAndProvider.testAndProvideFile(dossier);


        public String getDossierTemplateId() {

            return dossierTemplate.getId();
        }


        public String getDossierId() {

            return dossier.getId();
        }


        public String getFileId() {

            return file.getFileId();
        }


        public void forwardFileToApprovedState() {

            fileTesterAndProvider.markFileAsProcessed(getFileId());

            fileClient.setStatusApproved(getDossierId(), getFileId());

            assertThatTestFileIsApproved();
        }


        private void assertThatTestFileIsApproved() {

            var persistedFileStatus = fileClient.getFileStatus(getDossierId(), file.getId());
            assertThat(persistedFileStatus.getWorkflowStatus()).isEqualTo(WorkflowStatus.APPROVED);
        }

    }

}
