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

import java.time.OffsetDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.ColorsEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.FileAttributesGeneralConfigurationEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.configuration.TypeEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierAttributeConfigEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.DossierTemplateEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.FileAttributeConfigEntity;
import com.iqser.red.service.persistence.management.v1.processor.entity.dossier.ReportTemplateEntity;
import com.iqser.red.service.persistence.management.v1.processor.exception.NotFoundException;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DictionaryPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierAttributeConfigPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierStatusPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.DossierTemplatePersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.EntryPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.FileAttributeConfigPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.LegalBasisMappingPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.ReportTemplatePersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.RulesPersistenceService;
import com.iqser.red.service.persistence.management.v1.processor.service.persistence.repository.DossierTemplateRepository;
import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.WatermarkModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.CloneDossierTemplateRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DossierTemplateStatus;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.CreateOrUpdateDossierStatusRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.legalbasis.LegalBasis;
import com.iqser.red.storage.commons.service.StorageService;
import com.knecon.fforesight.databasetenantcommons.providers.utils.MagicConverter;
import com.knecon.fforesight.tenantcommons.TenantContext;

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

@Slf4j
@Service
@RequiredArgsConstructor
public class DossierTemplateCloneService {

    private final DossierTemplateRepository dossierTemplateRepository;
    private final LegalBasisMappingPersistenceService legalBasisMappingPersistenceService;
    private final RulesPersistenceService rulesPersistenceService;
    private final DossierTemplatePersistenceService dossierTemplatePersistenceService;
    private final DossierAttributeConfigPersistenceService dossierAttributeConfigPersistenceService;
    private final DictionaryPersistenceService dictionaryPersistenceService;
    private final EntryPersistenceService entryPersistenceService;
    private final FileAttributeConfigPersistenceService fileAttributeConfigPersistenceService;
    private final ReportTemplatePersistenceService reportTemplatePersistenceService;
    private final ColorsService colorsService;
    private final StorageService storageService;
    private final DossierStatusPersistenceService dossierStatusPersistenceService;
    private final WatermarkService watermarkService;
    private final FileManagementStorageService fileManagementStorageService;


    @Transactional
    public DossierTemplateEntity cloneDossierTemplate(String dossierTemplateId, CloneDossierTemplateRequest cloneDossierTemplateRequest) {

        dossierTemplatePersistenceService.validateDossierTemplateNameIsUnique(cloneDossierTemplateRequest.getName());

        DossierTemplateEntity clonedDossierTemplate = new DossierTemplateEntity();

        dossierTemplateRepository.findById(dossierTemplateId).ifPresentOrElse(dossierTemplate -> {

            OffsetDateTime now = OffsetDateTime.now().truncatedTo(ChronoUnit.MILLIS);
            clonedDossierTemplate.setId(UUID.randomUUID().toString());
            clonedDossierTemplate.setName(cloneDossierTemplateRequest.getName());
            clonedDossierTemplate.setDescription(cloneDossierTemplateRequest.getDescription() == null || cloneDossierTemplateRequest.getDescription()
                    .isEmpty() ? dossierTemplate.getDescription() : cloneDossierTemplateRequest.getDescription());
            clonedDossierTemplate.setDateAdded(now);
            clonedDossierTemplate.setDateModified(now);
            clonedDossierTemplate.setCreatedBy(cloneDossierTemplateRequest.getCloningUserId());
            clonedDossierTemplate.setModifiedBy(cloneDossierTemplateRequest.getCloningUserId());
            clonedDossierTemplate.setValidFrom(cloneDossierTemplateRequest.getValidFrom() == null ? dossierTemplate.getValidFrom() : cloneDossierTemplateRequest.getValidFrom());
            clonedDossierTemplate.setValidTo(cloneDossierTemplateRequest.getValidTo() == null ? dossierTemplate.getValidTo() : cloneDossierTemplateRequest.getValidTo());
            clonedDossierTemplate.setSoftDeleteTime(dossierTemplate.getSoftDeleteTime());
            clonedDossierTemplate.setDownloadFileTypes(cloneDossierTemplateRequest.getDownloadFileTypes() == null || cloneDossierTemplateRequest.getDownloadFileTypes()
                    .isEmpty() ? dossierTemplate.getDownloadFileTypes() : cloneDossierTemplateRequest.getDownloadFileTypes());
            //set rules
            cloneRules(dossierTemplate.getId(), clonedDossierTemplate.getId());
            //set legal basis
            cloneLegalBasisMapping(dossierTemplate.getId(), clonedDossierTemplate.getId());

            clonedDossierTemplate.setDossierTemplateStatus(DossierTemplateStatus.valueOf(dossierTemplatePersistenceService.computeDossierTemplateStatus(clonedDossierTemplate)
                    .name()));
            dossierTemplatePersistenceService.insertDossierTemplate(clonedDossierTemplate);

            //set dictionaries
            cloneDictionariesWithEntries(dossierTemplate.getId(), clonedDossierTemplate.getId());

            //set dossier attributes
            cloneDossierAttributesConfig(dossierTemplate.getId(), clonedDossierTemplate.getId());

            //set file attributes
            cloneFileAttributesGeneralConfig(dossierTemplate.getId(), clonedDossierTemplate.getId());
            cloneFileAttributesConfig(dossierTemplate.getId(), clonedDossierTemplate.getId());

            //set report templates
            cloneReportTemplates(dossierTemplate, clonedDossierTemplate.getId());

            //set colors
            cloneColors(dossierTemplate.getId(), clonedDossierTemplate.getId());

            //set dossier status
            cloneDossierStates(dossierTemplate.getId(), clonedDossierTemplate.getId());

            // set the watermark configurations
            cloneWatermarks(dossierTemplate.getId(), clonedDossierTemplate.getId());

        }, () -> {
            throw new NotFoundException(String.format(DossierTemplatePersistenceService.DOSSIER_TEMPLATE_NOT_FOUND_MESSAGE, dossierTemplateId));
        });

        return clonedDossierTemplate;
    }


    private void cloneRules(String dossierTemplateId, String clonedDossierTemplateId) {

        for (RuleFileType ruleFileType : RuleFileType.values()) {
            try {
                rulesPersistenceService.setRules(rulesPersistenceService.getRules(dossierTemplateId, ruleFileType).getValue(), clonedDossierTemplateId, ruleFileType);
            } catch (NotFoundException e) {
                log.debug("No {} rules found, will not be cloned!", ruleFileType);
            }
        }
    }


    private void cloneLegalBasisMapping(String dossierTemplateId, String clonedDossierTemplateId) {

        legalBasisMappingPersistenceService.setLegalBasisMapping(clonedDossierTemplateId,
                MagicConverter.convert(legalBasisMappingPersistenceService.getLegalBasisMapping(dossierTemplateId), LegalBasis.class));
    }


    private void cloneDictionariesWithEntries(String dossierTemplateId, String clonedDossierTemplateId) {

        var types = dictionaryPersistenceService.getAllTypesForDossierTemplate(dossierTemplateId, false);
        for (TypeEntity t : types) {
            var type = dictionaryPersistenceService.addType(t.getType(),
                    clonedDossierTemplateId,
                    t.getHexColor(),
                    t.getRecommendationHexColor(),
                    t.getSkippedHexColor(),
                    t.getRank(),
                    t.isHint(),
                    t.isCaseInsensitive(),
                    t.isRecommendation(),
                    t.getDescription(),
                    t.isAddToDictionaryAction(),
                    t.getLabel(),
                    null,
                    t.isHasDictionary(),
                    t.isSystemManaged(),
                    t.isAutoHideSkipped(),
                    t.isDossierDictionaryOnly());
            entryPersistenceService.cloneEntries(t.getId(), type.getId());
        }
    }


    private void cloneDossierAttributesConfig(String dossierTemplateId, String clonedDossierTemplateId) {

        List<DossierAttributeConfigEntity> dacList = new ArrayList<>();
        var dossierAttributesConfig = dossierAttributeConfigPersistenceService.getDossierAttributes(dossierTemplateId);
        dossierAttributesConfig.forEach(dossierAttributeConfigEntity -> {
            var dac = DossierAttributeConfigEntity.builder()
                    .label(dossierAttributeConfigEntity.getLabel())
                    .editable(dossierAttributeConfigEntity.isEditable())
                    .placeholder(dossierAttributeConfigEntity.getPlaceholder())
                    .type(dossierAttributeConfigEntity.getType())
                    .build();
            dacList.add(dac);
        });
        dossierAttributeConfigPersistenceService.setDossierAttributesConfig(clonedDossierTemplateId, dacList);
    }


    private void cloneFileAttributesGeneralConfig(String dossierTemplateId, String clonedDossierTemplateId) {

        try {
            var fileAttributesGeneralConfig = fileAttributeConfigPersistenceService.getFileAttributesGeneralConfiguration(dossierTemplateId);
            var fagc = FileAttributesGeneralConfigurationEntity.builder()
                    .dossierTemplateId(clonedDossierTemplateId)
                    .delimiter(fileAttributesGeneralConfig.getDelimiter())
                    .encoding(fileAttributesGeneralConfig.getEncoding())
                    .filenameMappingColumnHeaderName(fileAttributesGeneralConfig.getFilenameMappingColumnHeaderName())
                    .build();
            fileAttributeConfigPersistenceService.setFileAttributesGeneralConfig(clonedDossierTemplateId, fagc);
        } catch (NotFoundException e) {
            log.debug("No file attribute config found for cloning {}", dossierTemplateId);
        }
    }


    private void cloneFileAttributesConfig(String dossierTemplateId, String clonedDossierTemplateId) {

        List<FileAttributeConfigEntity> facList = new ArrayList<>();
        var fileAttributesConfig = fileAttributeConfigPersistenceService.getFileAttributes(dossierTemplateId);
        fileAttributesConfig.forEach(fileAttributeConfigEntity -> {
            var fac = FileAttributeConfigEntity.builder()
                    .csvColumnHeader(fileAttributeConfigEntity.getCsvColumnHeader())
                    .label(fileAttributeConfigEntity.getLabel())
                    .primaryAttribute(fileAttributeConfigEntity.isPrimaryAttribute())
                    .editable(fileAttributeConfigEntity.isEditable())
                    .filterable(fileAttributeConfigEntity.isFilterable())
                    .displayedInFileList(fileAttributeConfigEntity.isDisplayedInFileList())
                    .placeholder(fileAttributeConfigEntity.getPlaceholder())
                    .type(fileAttributeConfigEntity.getType())
                    .build();
            facList.add(fac);
        });
        fileAttributeConfigPersistenceService.setFileAttributesConfig(clonedDossierTemplateId, facList);
    }


    @SneakyThrows
    private void cloneReportTemplates(DossierTemplateEntity dossierTemplate, String clonedDossierTemplateId) {

        var reportTemplates = reportTemplatePersistenceService.findByDossierTemplateId(dossierTemplate.getId());
        for (ReportTemplateEntity rte : reportTemplates) {
            if (storageService.objectExists(TenantContext.getTenantId(), rte.getStorageId())) {
                var storedReportTemplate = fileManagementStorageService.getObject(TenantContext.getTenantId(), rte.getStorageId());
                String storageId = clonedDossierTemplateId + "/" + rte.getFileName();
                String templateId = UUID.randomUUID().toString();
                storageService.storeObject(TenantContext.getTenantId(), storageId, storedReportTemplate);
                storedReportTemplate.close();
                reportTemplatePersistenceService.insert(clonedDossierTemplateId, templateId, storageId, rte.getFileName(), rte.isActiveByDefault(), rte.isMultiFileReport());
            } else {
                log.debug("Deleted Report-template {} will not be cloned", rte.getTemplateId());
            }
        }
    }


    private void cloneColors(String dossierTemplateId, String clonedDossierTemplateId) {

        var colors = colorsService.getColors(dossierTemplateId);
        var clonedColors = new ColorsEntity();
        BeanUtils.copyProperties(colors, clonedColors);
        clonedColors.setDossierTemplateId(clonedDossierTemplateId);
        colorsService.saveColors(clonedColors);
    }


    private void cloneDossierStates(String dossierTemplateId, String clonedDossierTemplateId) {

        var states = dossierStatusPersistenceService.getAllDossierStatusForTemplate(dossierTemplateId);
        for (var state : states) {
            var clonedState = CreateOrUpdateDossierStatusRequest.builder()
                    .name(state.getName())
                    .description(state.getDescription())
                    .color(state.getColor())
                    .dossierTemplateId(clonedDossierTemplateId)
                    .rank(state.getRank())
                    .build();
            dossierStatusPersistenceService.createOrUpdateDossierStatus(clonedState);
        }
    }


    private void cloneWatermarks(String dossierTemplateId, String clonedDossierTemplateId) {

        var watermarkList = watermarkService.getWatermarksForDossierTemplateId(dossierTemplateId);
        for (var watermark : watermarkList) {
            var clonedWatermark = new WatermarkModel();
            BeanUtils.copyProperties(watermark, clonedWatermark, "id", "dossierTemplateId");
            clonedWatermark.setDossierTemplateId(clonedDossierTemplateId);
            watermarkService.createOrUpdateWatermark(clonedWatermark);
        }
    }

}
