package com.knecon.fforesight.service.layoutparser.server.graph;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.junit.jupiter.api.Test;

import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentData;
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentPageProto.AllDocumentPages;
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentPositionDataProto.AllDocumentPositionData;
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentStructureProto.DocumentStructure;
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentStructureWrapper;
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentTextDataProto.AllDocumentTextData;
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.EntryDataProto.EntryData;
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeTypeProto.NodeType;
import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Document;
import com.knecon.fforesight.service.layoutparser.processor.model.graph.nodes.Table;
import com.knecon.fforesight.service.layoutparser.processor.services.mapper.DocumentDataMapper;
import com.knecon.fforesight.service.layoutparser.processor.services.mapper.DocumentGraphMapper;
import com.knecon.fforesight.service.layoutparser.processor.services.mapper.PropertiesMapper;
import com.knecon.fforesight.service.layoutparser.server.utils.BuildDocumentTest;
import com.knecon.fforesight.tenantcommons.TenantContext;

import lombok.SneakyThrows;

public class DocumentGraphMappingTest extends BuildDocumentTest {

    @Test
    @SneakyThrows
    public void testGraphMapping() {

        String filename = "files/syngenta/CustomerFiles/SYNGENTA_EFSA_sanitisation_GFL_v1_moreSections.pdf";

        Document document = buildGraph(filename);
        DocumentData documentData = DocumentDataMapper.toDocumentData(document);

        storageService.storeProtoObject(TenantContext.getTenantId(), filename + "_PAGES" + ".proto", documentData.getDocumentPages());
        storageService.storeProtoObject(TenantContext.getTenantId(), filename + "_TEXT" + ".proto", documentData.getDocumentTextData());
        storageService.storeProtoObject(TenantContext.getTenantId(), filename + "_POSITIONS" + ".proto", documentData.getDocumentPositions());
        storageService.storeProtoObject(TenantContext.getTenantId(), filename + "_STRUCTURE" + ".proto", documentData.getDocumentStructure());

        AllDocumentPages pageData = storageService.readProtoObject(TenantContext.getTenantId(), filename + "_PAGES" + ".proto", AllDocumentPages.parser());
        AllDocumentTextData atomicTextBlockData = storageService.readProtoObject(TenantContext.getTenantId(), filename + "_TEXT" + ".proto", AllDocumentTextData.parser());
        AllDocumentPositionData atomicPositionBlockData = storageService.readProtoObject(TenantContext.getTenantId(),
                                                                                         filename + "_POSITIONS" + ".proto",
                                                                                         AllDocumentPositionData.parser());
        DocumentStructure documentTreeData = storageService.readProtoObject(TenantContext.getTenantId(), filename + "_STRUCTURE" + ".proto", DocumentStructure.parser());

        DocumentData documentData2 = DocumentData.builder()
                .documentPages(pageData)
                .documentStructureWrapper(new DocumentStructureWrapper(documentTreeData))
                .documentTextData(atomicTextBlockData)
                .documentPositions(atomicPositionBlockData)
                .build();
        Document newDocument = DocumentGraphMapper.toDocumentGraph(documentData2);

        assertTrue(allTablesHavePositiveNumberOfRowsAndColumns(document));
        assertTrue(allTablesHavePositiveNumberOfRowsAndColumns(documentData));
        assertTrue(allTablesHavePositiveNumberOfRowsAndColumns(documentData2));
        assertTrue(allTablesHavePositiveNumberOfRowsAndColumns(newDocument));

        assertEquals(document.toString(), newDocument.toString());
        assertEquals(document.getDocumentTree().toString(), newDocument.getDocumentTree().toString());
    }


    private static boolean allTablesHavePositiveNumberOfRowsAndColumns(Document document) {

        return document.streamAllSubNodes()
                .filter(semanticNode -> semanticNode instanceof Table)
                .map(semanticNode -> (Table) semanticNode)
                .allMatch(tableNode -> tableNode.getNumberOfCols() > 0 && tableNode.getNumberOfRows() > 0);
    }


    private static boolean allTablesHavePositiveNumberOfRowsAndColumns(DocumentData documentData) {

        return documentData.getDocumentStructureWrapper().streamAllEntries()
                .filter(entryData -> entryData.getType().equals(NodeType.TABLE))
                .map(EntryData::getPropertiesMap)
                .map(properties -> {
                    var builder = Table.builder();
                    PropertiesMapper.parseTableProperties(properties, builder);
                    return builder.build();
                })
                .allMatch(tableNode -> tableNode.getNumberOfCols() > 0 && tableNode.getNumberOfRows() > 0);
    }

}
