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

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.fasterxml.jackson.core.JsonProcessingException;
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.entity.dossier.DossierEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.ReportTemplateEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.download.DownloadStatusEntity;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.model.DownloadJob;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DownloadStatusPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.FileStatusPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.ReportTemplateRepository;
import com.knecon.fforesight.databasetenantcommons.providers.utils.MagicConverter;
import com.iqser.red.service.persistence.management.v1.processor.utils.StorageIdUtils;
import com.iqser.red.service.persistence.service.v1.api.shared.model.common.JSONPrimitive;
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.DownloadRequest;
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.DownloadWithOptionRequest;

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

@Slf4j
@Service
@RequiredArgsConstructor
public class DownloadService {

    private final DownloadStatusPersistenceService downloadStatusPersistenceService;
    private final FileStatusPersistenceService fileStatusPersistenceService;
    private final DossierPersistenceService dossierPersistenceService;
    private final ReportTemplateRepository reportTemplateRepository;
    private final ObjectMapper objectMapper;
    private final RabbitTemplate rabbitTemplate;


    @Transactional
    public JSONPrimitive<String> prepareDownload(DownloadRequest request) {

        var existingFileStatuses = fileStatusPersistenceService.getStatusesForDossier(request.getDossierId());
        var filesInNotApprovedState = existingFileStatuses.stream()
                .filter(f -> request.getFileIds().contains(f.getId()) && !WorkflowStatus.APPROVED.equals(f.getWorkflowStatus()))
                .toList();
        if (!filesInNotApprovedState.isEmpty()) {
            throw new BadRequestException("At least one file is not in Approved state");
        }
        var dossier = dossierPersistenceService.getActiveOrArchivedDossier(request.getDossierId());

        DownloadWithOptionRequest requestWithOptions = DownloadWithOptionRequest.builder()
                .userId(request.getUserId())
                .dossierId(request.getDossierId())
                .fileIds(request.getFileIds())
                .downloadFileTypes(dossier.getDownloadFileTypes())
                .reportTemplateIds(dossier.getReportTemplates().stream().map(ReportTemplateEntity::getTemplateId).collect(Collectors.toList()))
                .build();
        return this.startPrepareDownload(requestWithOptions, dossier, existingFileStatuses);
    }


    private JSONPrimitive<String> startPrepareDownload(DownloadWithOptionRequest request, DossierEntity dossier, List<FileEntity> existingFileStatuses) {

        var downloadArchiveMimeType = "application/zip";

        String downloadFilename = buildName(request.getDossierId(), request.getFileIds(), request.getUserId(), downloadArchiveMimeType, existingFileStatuses);
        String storageId = StorageIdUtils.getStorageId(request.getUserId(), request.getDossierId(), downloadFilename);

        downloadStatusPersistenceService.createStatus(request.getUserId(),
                storageId,
                dossier,
                downloadFilename,
                downloadArchiveMimeType,
                request.getFileIds(),
                request.getDownloadFileTypes(),
                request.getReportTemplateIds(),
                request.getRedactionPreviewColor());
        addToDownloadQueue(DownloadJob.builder().storageId(storageId).userId(request.getUserId()).build(), 1);

        return new JSONPrimitive<>(storageId);
    }


    private String buildName(String dossierId, List<String> fileIds, String userId, String mimeType, List<FileEntity> existingFileStatuses) {

        var dossier = dossierPersistenceService.getActiveOrArchivedDossier(dossierId);

        Set<String> existingFilenames = downloadStatusPersistenceService.getStatusesByUser(userId).stream().map(DownloadStatusEntity::getFilename).collect(Collectors.toSet());

        if (fileIds.size() == 1) {
            var file = fileStatusPersistenceService.getStatus(fileIds.iterator().next());

            String cleanName = getCleanName(file.getFilename());

            String name = cleanName + getExtension(mimeType);
            int i = 1;
            while (existingFilenames.contains(name)) {
                name = cleanName + " (" + i + ")" + getExtension(mimeType);
                i++;
            }
            return name;
        }

        int i = 1;
        if (existingFileStatuses.size() != fileIds.size()) {
            String name = dossier.getDossierName() + ".bulk" + getExtension(mimeType);
            while (existingFilenames.contains(name)) {
                name = dossier.getDossierName() + ".bulk (" + i + ")" + getExtension(mimeType);
                i++;
            }
            return name;
        } else {
            String name = dossier.getDossierName() + ".full" + getExtension(mimeType);
            while (existingFilenames.contains(name)) {
                name = dossier.getDossierName() + ".full (" + i + ")" + getExtension(mimeType);
                i++;
            }
            return name;
        }
    }


    private void addToDownloadQueue(DownloadJob downloadJob, int priority) {

            rabbitTemplate.convertAndSend(MessagingConfiguration.DOWNLOAD_QUEUE, downloadJob, message -> {
                message.getMessageProperties().setPriority(priority);
                return message;
            });
    }


    private String getCleanName(String fileName) {

        var lastIndexOfDot = fileName.lastIndexOf(".");
        String cleanName;
        if (lastIndexOfDot > 0) {
            cleanName = fileName.substring(0, fileName.lastIndexOf("."));
        } else {
            cleanName = fileName;
        }
        return cleanName;
    }


    private String getExtension(String mimeType) {

        if ("application/zip".equalsIgnoreCase(mimeType)) {
            return ".zip";
        }
        return ".entity";
    }


    public JSONPrimitive<String> prepareDownload(DownloadWithOptionRequest request) {

        var existingFileStatuses = fileStatusPersistenceService.getStatusesForDossier(request.getDossierId());
        var dossier = dossierPersistenceService.getActiveOrArchivedDossier(request.getDossierId());
        // check reports list and download types so both can be valid

        if (request.getReportTemplateIds() != null && !request.getReportTemplateIds().isEmpty()) {
            //validate reports ids
            var reportTemplates = reportTemplateRepository.findAllById(request.getReportTemplateIds());
            if (reportTemplates.isEmpty() && (request.getDownloadFileTypes() == null || request.getDownloadFileTypes().isEmpty())) {
                throw new BadRequestException(" Invalid reports ids and no download types");
            }
            request.setReportTemplateIds(reportTemplates.stream().map(ReportTemplateEntity::getTemplateId).collect(Collectors.toList()));
        }
        return this.startPrepareDownload(request, dossier, existingFileStatuses);
    }


    public List<DownloadStatus> getDownloadStatus(String userId) {

        return MagicConverter.convert(downloadStatusPersistenceService.getStatusesByUser(userId), DownloadStatus.class);
    }


    public void setDownloaded(JSONPrimitive<String> setDownloadedRequest) {

        downloadStatusPersistenceService.updateLastDownload(setDownloadedRequest.getValue());
    }


    public void deleteDownloadStatus(JSONPrimitive<String> setDownloadedRequest) {

        downloadStatusPersistenceService.deleteStatus(setDownloadedRequest.getValue());
    }

}
