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

import java.awt.Color;
import java.awt.geom.Rectangle2D;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.core.io.ClassPathResource;

import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentData;
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.DocumentStructure;
import com.knecon.fforesight.service.layoutparser.internal.api.data.redaction.NodeType;
import com.knecon.fforesight.service.layoutparser.internal.api.queue.LayoutParsingType;
import com.knecon.fforesight.service.layoutparser.processor.model.PageContents;
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.model.table.CleanRulings;
import com.knecon.fforesight.service.layoutparser.processor.python_api.model.image.ImageServiceResponse;
import com.knecon.fforesight.service.layoutparser.processor.python_api.model.table.TableServiceResponse;
import com.knecon.fforesight.service.layoutparser.processor.python_api.model.table.VisualLayoutParsingResponse;
import com.knecon.fforesight.service.layoutparser.processor.services.PageContentExtractor;
import com.knecon.fforesight.service.layoutparser.processor.services.RulingCleaningService;
import com.knecon.fforesight.service.layoutparser.processor.services.factory.DocumentGraphFactory;
import com.knecon.fforesight.service.layoutparser.processor.services.mapper.DocumentDataMapper;
import com.knecon.fforesight.service.layoutparser.processor.services.mapper.PropertiesMapper;
import com.knecon.fforesight.service.layoutparser.processor.utils.DrawingOptions;
import com.knecon.fforesight.service.layoutparser.processor.utils.RectangularIntersectionFinder;
import com.knecon.fforesight.service.layoutparser.server.utils.BuildDocumentTest;
import com.knecon.fforesight.service.layoutparser.server.utils.visualizations.PdfDraw;

import lombok.SneakyThrows;

public class RulingCleaningServiceTest extends BuildDocumentTest {

    @Test
    @Disabled
    @SneakyThrows
    public void textRectanglesFromRulingsExtraction() {

        String fileName = "files/syngenta/CustomerFiles/SinglePages/T5_Page16_VV-640252.pdf";
        String lineFileName = "/tmp/" + Path.of(fileName).getFileName().toString() + "_CELLS.pdf";
        List<PageContents> pageContents = PageContentExtractor.getSortedPageContents(fileName);
        RulingCleaningService rulingCleaningService = new RulingCleaningService();
        List<List<Rectangle2D>> rectanglesPerPage = new LinkedList<>();
        for (PageContents pageContent : pageContents) {
            CleanRulings cleanRulings = rulingCleaningService.getCleanRulings(Collections.emptyList(), pageContent.getRulings());
            List<Rectangle2D> rects = RectangularIntersectionFinder.find(cleanRulings.getHorizontals(), cleanRulings.getVerticals());
            rectanglesPerPage.add(rects);
        }

        PdfDraw.drawRectanglesPerPage(fileName, rectanglesPerPage, lineFileName, DrawingOptions.builder().stroke(true).strokeColor(Color.RED).build());
    }


    @Test
    @Disabled
    @SneakyThrows
    public void textRulingExtraction() {

        String fileName = "files/syngenta/CustomerFiles/SinglePages/Page35_19 Chlorothalonil RAR 08 Volume 3CA B 6b metabolites Oct 2017.pdf";
        String lineFileName = "/tmp/" + Path.of(fileName).getFileName().toString() + "_LINES.pdf";
        List<PageContents> pageContents = PageContentExtractor.getSortedPageContents(fileName);
        RulingCleaningService rulingCleaningService = new RulingCleaningService();
        List<CleanRulings> cleanRulingsPerPage = new LinkedList<>();
        for (PageContents pageContent : pageContents) {
            cleanRulingsPerPage.add(rulingCleaningService.getCleanRulings(Collections.emptyList(), pageContent.getRulings()));
        }
        var cleanRulings = cleanRulingsPerPage.stream().map(CleanRulings::getVerticals).collect(Collectors.toList());
        PdfDraw.drawLinesPerPage(fileName, cleanRulings, lineFileName);

    }


    @Test
    @SneakyThrows
    public void testTableExtraction() {

        ClassPathResource resource = new ClassPathResource("files");
        List<String> pdfFileNames = Files.walk(resource.getFile().toPath())
                .filter(path -> path.getFileName().toString().endsWith(".pdf"))
                .map(Path::toAbsolutePath)
                .map(Path::toString)
                .toList();

        for (String pdfFileName : pdfFileNames) {
            writeJsons(Path.of(pdfFileName));
        }
    }


    @SneakyThrows
    private void writeJsons(Path filename) {

        Document documentGraphBefore = DocumentGraphFactory.buildDocumentGraph(LayoutParsingType.REDACT_MANAGER_OLD,
                layoutParsingPipeline.parseLayout(LayoutParsingType.REDACT_MANAGER_OLD,
                        filename.toFile(),
                        new ImageServiceResponse(),
                        new TableServiceResponse(),
                        new VisualLayoutParsingResponse(),
                        Map.of("file",filename.toFile().toString())));
        Document documentGraphAfter = DocumentGraphFactory.buildDocumentGraph(LayoutParsingType.REDACT_MANAGER_OLD,
                layoutParsingPipeline.parseLayout(LayoutParsingType.REDACT_MANAGER_OLD,
                        filename.toFile(),
                        new ImageServiceResponse(),
                        new TableServiceResponse(),
                        new VisualLayoutParsingResponse(),
                        Map.of("file",filename.toFile().toString())));
        DocumentData documentDataBefore = DocumentDataMapper.toDocumentData(documentGraphBefore);
        DocumentData documentDataAfter = DocumentDataMapper.toDocumentData(documentGraphAfter);
        if (!compareStructures(documentDataBefore.getDocumentStructure(), documentDataAfter.getDocumentStructure())) {
            String tmpFileNameBefore = "/tmp/before." + filename.getFileName().toString();
            try (PDDocument pdDocument = Loader.loadPDF(filename.toFile())) {
                PdfDraw.drawDocumentGraph(pdDocument, documentGraphBefore);
                pdDocument.save(tmpFileNameBefore);
            }
            String tmpFileNameAfter = "/tmp/after." + filename.getFileName().toString();
            try (PDDocument pdDocument = Loader.loadPDF(filename.toFile())) {
                PdfDraw.drawDocumentGraph(pdDocument, documentGraphAfter);
                pdDocument.save(tmpFileNameAfter);

            }
        }
    }


    @SneakyThrows
    private boolean compareStructures(DocumentStructure structure1, DocumentStructure structure2) {

        List<Table> listStructure1 = structure1.streamAllEntries()
                .filter(entryData -> entryData.getType().equals(NodeType.TABLE))
                .map(DocumentStructure.EntryData::getProperties)
                .map(properties -> {
                    var builder = Table.builder();
                    PropertiesMapper.parseTableProperties(properties, builder);
                    return builder.build();
                })
                .toList();

        List<Table> listStructure2 = structure2.streamAllEntries()
                .filter(entryData -> entryData.getType().equals(NodeType.TABLE))
                .map(DocumentStructure.EntryData::getProperties)
                .map(properties -> {
                    var builder = Table.builder();
                    PropertiesMapper.parseTableProperties(properties, builder);
                    return builder.build();
                })
                .toList();

        for (int i = 0; i < listStructure1.size(); i++) {
            Table tableNode1 = listStructure1.get(i);
            Table tableNode2 = listStructure2.get(i);
            if (tableNode1.getNumberOfRows() != tableNode2.getNumberOfRows() || tableNode1.getNumberOfCols() != tableNode2.getNumberOfCols()) {
                return false;
            }
        }
        return true;
    }

}
