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

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.compress.utils.FileNameUtils;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.iqser.red.commons.jackson.ObjectMapperFactory;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.DateFormatsExportModel;
import com.iqser.red.service.persistence.management.v1.processor.exception.BadRequestException;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.ComponentMappingImportModel;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.ExportFilename;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.ImportTemplateResult;
import com.iqser.red.service.persistence.management.v1.processor.dataexchange.models.RulesExportModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.WatermarkModel;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentDefinition;
import com.iqser.red.service.persistence.service.v1.api.shared.model.component.ComponentMappingMetadata;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DossierAttributeConfig;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.DossierTemplate;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.FileAttributesGeneralConfiguration;
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.ReportTemplateUploadRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.configuration.Colors;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.DossierStatusInfo;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.dossier.file.FileAttributeConfig;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.legalbasis.LegalBasis;

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

@RequiredArgsConstructor
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
public class DossierTemplateArchiveReader {

    String userId;
    String dossierTemplateId;
    boolean updateExistingDossierTemplate;

    ObjectMapper objectMapper = ObjectMapperFactory.create();
    EntityTypeArchiveReader entityTypeArchiveReader = new EntityTypeArchiveReader();
    ImportTemplateResult importTemplateResult = new ImportTemplateResult();

    Map<String, ReportTemplate> reportTemplateMap = new HashMap<>();
    Map<String, byte[]> reportTemplateBytesMap = new HashMap<>();
    List<String> reportTemplateFilenameList = new ArrayList<>();
    Map<String, byte[]> mappingDataMap = new HashMap<>();
    List<ComponentMappingMetadata> mappingMetadataList = new LinkedList<>();


    public ImportTemplateResult buildResult() {

        importTemplateResult.setEntityTypeImportModel(entityTypeArchiveReader.getEntityTypeImportModel());
        importTemplateResult.setUserId(userId);
        importTemplateResult.setUpdateExistingTemplate(updateExistingDossierTemplate);
        importTemplateResult.setDossierTemplateId(dossierTemplateId == null ? "" : dossierTemplateId);

        for (var reportZe : reportTemplateBytesMap.entrySet()) {
            if (reportTemplateFilenameList.contains(reportZe.getKey())) {
                var report = reportTemplateMap.get(reportZe.getKey());
                importTemplateResult.getReportTemplateUploadRequests()
                        .add(ReportTemplateUploadRequest.builder()
                                     .fileName(report.getFileName())
                                     .activeByDefault(report.isActiveByDefault())
                                     .multiFileReport(report.isMultiFileReport())
                                     .dossierTemplateId(dossierTemplateId != null ? dossierTemplateId : "")
                                     .template(reportZe.getValue())
                                     .build());

            }
        }

        for (var metadata : mappingMetadataList) {
            String fileName = metadata.getName() + ".csv";
            if (mappingDataMap.containsKey(fileName)) {
                importTemplateResult.componentMappings.add(new ComponentMappingImportModel(metadata, mappingDataMap.get(fileName)));
            }
        }
        if (importTemplateResult.getDossierTemplate() == null) {
            throw new BadRequestException("Provided archive is faulty");
        }
        return importTemplateResult;
    }


    @SneakyThrows
    @SuppressWarnings("checkstyle:EmptyBlock")
    public void handleZipEntryData(ZipEntryData zipEntryData) {

        if (entityTypeArchiveReader.handleZipEntryData(zipEntryData)) {
            // do nothing, as entity type reader already matched
        } else if (zipEntryData.getFileName().contains(ExportFilename.DOSSIER_TEMPLATE_META.getFilename())) {
            DossierTemplate dossierTemplate = objectMapper.readValue(zipEntryData.data(), DossierTemplate.class);
            importTemplateResult.setDossierTemplate(dossierTemplate);
        } else if (zipEntryData.getFileName().contains(ExportFilename.WATERMARK.getFilename())) {
            List<WatermarkModel> watermarkList = objectMapper.readValue(zipEntryData.data(), new TypeReference<>() {
            });
            importTemplateResult.getWatermarks().addAll(watermarkList);
        } else if (zipEntryData.getFileName().contains(ExportFilename.COLORS.getFilename())) {
            Colors colors = objectMapper.readValue(zipEntryData.data(), Colors.class);
            importTemplateResult.setColors(colors);
        } else if (zipEntryData.getFileName().contains(ExportFilename.DOSSIER_STATUS.getFilename())) {
            List<DossierStatusInfo> dossierStatusInfoList = objectMapper.readValue(zipEntryData.data(), new TypeReference<>() {
            });
            importTemplateResult.getDossierStatusInfos().addAll(dossierStatusInfoList);
        } else if (zipEntryData.getFileName().contains(ExportFilename.DOSSIER_ATTRIBUTES_CONFIG.getFilename())) {
            List<DossierAttributeConfig> dossierAttributeConfigs = objectMapper.readValue(zipEntryData.data(), new TypeReference<>() {
            });
            importTemplateResult.getDossierAttributesConfigs().addAll(dossierAttributeConfigs);
        } else if (zipEntryData.getFileName().contains(ExportFilename.FILE_ATTRIBUTE_CONFIG.getFilename())) {
            List<FileAttributeConfig> fileAttributeConfigs = objectMapper.readValue(zipEntryData.data(), new TypeReference<>() {
            });
            importTemplateResult.getFileAttributesConfigs().addAll(fileAttributeConfigs);
        } else if (zipEntryData.getFileName().contains(ExportFilename.LEGAL_BASIS.getFilename())) {
            List<LegalBasis> legalBasisList = objectMapper.readValue(zipEntryData.data(), new TypeReference<>() {
            });
            importTemplateResult.getLegalBases().addAll(legalBasisList);
        } else if (zipEntryData.getFileName().contains(ExportFilename.FILE_ATTRIBUTE_GENERAL_CONFIG.getFilename())) {
            FileAttributesGeneralConfiguration fileAttributesGeneralConfiguration = objectMapper.readValue(zipEntryData.data(), FileAttributesGeneralConfiguration.class);
            importTemplateResult.setFileAttributesGeneralConfiguration(fileAttributesGeneralConfiguration);
        } else if (zipEntryData.getFileName().contains(ExportFilename.RULES.getFilename())) {
            if (zipEntryData.getFileName().endsWith(".txt")) {
                String rules = objectMapper.readValue(new String(zipEntryData.data()), String.class);
                importTemplateResult.setRuleSet(rules);
            } else if (zipEntryData.getFileName().endsWith(".drl")) {
                String rules = new String(zipEntryData.data());
                importTemplateResult.setRuleSet(rules);
            } else if (zipEntryData.getFileName().endsWith(".json")) {
                RulesExportModel rulesExportModel = objectMapper.readValue(zipEntryData.data(), RulesExportModel.class);
                importTemplateResult.setRulesExportModel(rulesExportModel);
            } else {
                throw new BadRequestException("Rule file is not in TXT or DRL format. " + FileNameUtils.getExtension(zipEntryData.entry().getName()) + " format is not supported.");
            }
        } else if (zipEntryData.getFileName().contains(ExportFilename.COMPONENT_RULES.getFilename())) {
            if (zipEntryData.getFileName().endsWith(".txt")) {
                String rules = objectMapper.readValue(new String(zipEntryData.data()), String.class);
                importTemplateResult.setComponentRuleSet(rules);
            } else if (zipEntryData.getFileName().endsWith(".drl")) {
                String rules = new String(zipEntryData.data());
                importTemplateResult.setComponentRuleSet(rules);
            } else if (zipEntryData.getFileName().endsWith(".json")) {
                RulesExportModel componentRuleExportModel = objectMapper.readValue(zipEntryData.data(), RulesExportModel.class);
                importTemplateResult.setComponentRulesExportModel(componentRuleExportModel);
            } else {
                throw new BadRequestException("Component rule file is not in TXT or DRL format. " + zipEntryData.getExtension() + " format is not supported.");
            }
        } else if (zipEntryData.getFileName().contains(ExportFilename.DATE_FORMATS.getFilename())) {
            if (zipEntryData.getFileName().endsWith(".txt")) {
                String dateFormats = new String(zipEntryData.data());
                importTemplateResult.setDateFormats(dateFormats);
            } else if (zipEntryData.getFileName().endsWith(".json")) {
                DateFormatsExportModel dateFormatsExportModel = objectMapper.readValue(zipEntryData.data(), DateFormatsExportModel.class);
                importTemplateResult.setDateFormatsExportModel(dateFormatsExportModel);
            } else {
                throw new BadRequestException("Date formats file is not in TXT format. " + zipEntryData.getExtension() + " format is not supported.");
            }
        } else if (zipEntryData.getFileName().contains(ExportFilename.REPORT_TEMPLATE.getFilename())) {
            List<ReportTemplate> reportTemplateList = objectMapper.readValue(zipEntryData.data(), new TypeReference<>() {
            });
            reportTemplateList.forEach(rt -> reportTemplateMap.put(buildReportTemplateKey(rt), rt));

            reportTemplateList.stream()
                    .map(DossierTemplateArchiveReader::buildReportTemplateKey)
                    .forEach(reportTemplateFilenameList::add);
        } else if (zipEntryData.getParentFolder().equals(ExportFilename.COMPONENT_MAPPINGS_FOLDER.getFilename())) {
            if (zipEntryData.getFileName().contains(ExportFilename.COMPONENT_MAPPINGS_FILE.getFilename())) {
                List<ComponentMappingMetadata> tempMappingMetadataList = objectMapper.readValue(zipEntryData.data(), new TypeReference<>() {
                });
                mappingMetadataList.addAll(tempMappingMetadataList);
            } else if (zipEntryData.getFileName().endsWith(".csv")) {
                String fileName = zipEntryData.getFileName().replace(ExportFilename.COMPONENT_MAPPINGS_FOLDER.getFilename() + "/", "");
                mappingDataMap.put(fileName, zipEntryData.data());
            }
        } else if (zipEntryData.getFileName().contains(ExportFilename.COMPONENTS.getFilename())) {
            ComponentDefinition componentDefinition = objectMapper.readValue(zipEntryData.data(), ComponentDefinition.class);
            importTemplateResult.getComponentDefinitions().add(componentDefinition);
        } else {
            reportTemplateBytesMap.put(zipEntryData.getFileName(), zipEntryData.data());
        }
    }


    private static String buildReportTemplateKey(ReportTemplate rt) {

        return rt.isMultiFileReport() ? rt.getFileName() + ExportFilename.REPORT_TEMPLATE_MULTI_FILE.getFilename() : rt.getFileName();
    }

}
