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

import static org.assertj.core.api.Assertions.assertThat;

import java.awt.geom.Rectangle2D;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;

import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.knecon.fforesight.service.layoutparser.internal.api.queue.LayoutParsingType;
import com.knecon.fforesight.service.layoutparser.processor.LayoutParsingPipeline;
import com.knecon.fforesight.service.layoutparser.processor.model.ClassificationDocument;
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.ImageType;
import com.knecon.fforesight.service.layoutparser.processor.model.graph.textblock.TextBlock;
import com.knecon.fforesight.service.layoutparser.processor.model.image.ClassifiedImage;
import com.knecon.fforesight.service.layoutparser.processor.model.table.Cell;
import com.knecon.fforesight.service.layoutparser.processor.model.table.TablePageBlock;
import com.knecon.fforesight.service.layoutparser.processor.model.text.TextPositionSequence;
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.SectionsBuilderService;
import com.knecon.fforesight.service.layoutparser.processor.services.classification.RedactManagerClassificationService;
import com.knecon.fforesight.service.layoutparser.processor.services.factory.DocumentGraphFactory;
import com.knecon.fforesight.service.layoutparser.server.utils.AbstractTest;

import lombok.SneakyThrows;

public class PdfSegmentationServiceTest extends AbstractTest {

    @Autowired
    private LayoutParsingPipeline layoutParsingPipeline;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private RedactManagerClassificationService redactManagerClassificationService;

    @Autowired
    private SectionsBuilderService sectionsBuilderService;


    @SneakyThrows
    public ClassificationDocument buildClassificationDocument(File originDocument, TableServiceResponse tableServiceResponse) {

        ClassificationDocument classificationDocument = layoutParsingPipeline.parseLayout(LayoutParsingType.REDACT_MANAGER,
                originDocument,
                new ImageServiceResponse(),
                tableServiceResponse,
                new VisualLayoutParsingResponse(),
                Map.of("file","document"));

        redactManagerClassificationService.classifyDocument(classificationDocument);

        sectionsBuilderService.buildSections(classificationDocument);

        return classificationDocument;
    }


    @SneakyThrows
    public ClassificationDocument buildClassificationDocument(File originDocument) {

        return buildClassificationDocument(originDocument, new TableServiceResponse());
    }


    @Test
    public void tablesToHtmlDebugger() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource("files/syngenta/CustomerFiles/SinglePages/T5_Page16_VV-640252.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());

        toHtml(document, "/tmp/T5.html");

    }


    @Test
    public void tablesToHtmlDebuggerWithCVResponse() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource("files/cv_tables/ScanRotationBorder.pdf");
        ClassPathResource cvTablesResource = new ClassPathResource("files/cv_tables/ScanRotationBorder.TABLES.json");
        var tableServiceResponse = objectMapper.readValue(cvTablesResource.getInputStream(), TableServiceResponse.class);

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile(), tableServiceResponse);

        toHtml(document, "/tmp/ScanRotationBorder.html");
    }


    @Test
    public void testHeaderAndFooter() throws IOException {

        String fileName = "files/syngenta/CustomerFiles/54 Fludioxonil - EU AIR3 - Document E1 - Listing of Community and Member States MRLs.pdf";
        String textToSearch = "Annex to Regulation 283/2013 Annex to Regulation 284/2013";
        ClassPathResource pdfFileResource = new ClassPathResource(fileName);

        List<PageContents> textPositionPerPage = PageContentExtractor.getSortedPageContents(fileName);
        var textPositions = textPositionPerPage.stream()
                .flatMap(t -> t.getSortedTextPositionSequences()
                        .stream()
                        .map(TextPositionSequence::toString))
                .collect(Collectors.joining(" "));
        assertThat(textPositions.contains(textToSearch)).isFalse();

        ClassificationDocument classificationDocument = buildClassificationDocument(pdfFileResource.getFile());

        assertThat(classificationDocument.getHeaders()
                           .get(0).getTextBlocks().size()).isEqualTo(3);
        assertThat(classificationDocument.getHeaders()
                           .get(0).getTextBlocks()
                           .get(0).getSequences().size()).isEqualTo(8);
        assertThat(classificationDocument.getHeaders()
                           .get(0).getTextBlocks()
                           .get(0).toString()).isEqualTo(textToSearch);

        Document document = DocumentGraphFactory.buildDocumentGraph(LayoutParsingType.REDACT_MANAGER, classificationDocument);

        TextBlock leafTextBlock = document.getFirstPage().getHeader().getLeafTextBlock();
        assertThat(leafTextBlock.getSearchText().contains(textToSearch)).isTrue();

    }


    @Disabled
    @Test
    public void testScanRotationBorderIsIgnored() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource("files/cv_tables/ScanRotationBorder.pdf");
        ClassPathResource cvTablesResource = new ClassPathResource("files/cv_tables/ScanRotationBorder.TABLES.json");
        var tableServiceResponse = objectMapper.readValue(cvTablesResource.getInputStream(), TableServiceResponse.class);

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile(), tableServiceResponse);
        assertThat(document.getSections()
                           .stream()
                           .flatMap(paragraph -> paragraph.getTables()
                                   .stream())
                           .collect(Collectors.toList())).isNotEmpty();
        var tables = document.getSections().stream().flatMap(paragraph -> paragraph.getTables().stream()).toList();

        // Quality of the table parsing is not good, because the file is rotated at scanning.
        // We only asset that the table border is not the page border.
        tables.forEach(table -> {
            assertThat(table.getMinX()).isGreaterThan(75);
            assertThat(table.getMaxX()).isLessThan(512);
        });
    }


    @Test
    @SneakyThrows
    public void testMapping() {

        ClassPathResource responseJson = new ClassPathResource("files/image_response.json");
        ImageServiceResponse imageServiceResponse = objectMapper.readValue(responseJson.getInputStream(), ImageServiceResponse.class);

        Map<Integer, List<ClassifiedImage>> images = new HashMap<>();
        imageServiceResponse.getData()
                .forEach(imageMetadata -> images.computeIfAbsent(imageMetadata.getPosition().getPageNumber(), x -> new ArrayList<>())
                        .add(new ClassifiedImage(new Rectangle2D.Double(imageMetadata.getPosition().getX1(),
                                imageMetadata.getPosition().getY1(),
                                imageMetadata.getGeometry().getWidth(),
                                imageMetadata.getGeometry().getHeight()),
                                ImageType.valueOf(imageMetadata.getClassification().getLabel().toUpperCase(Locale.ROOT)),
                                imageMetadata.isAlpha(),
                                imageMetadata.getPosition().getPageNumber())));

        System.out.println("object");
    }


    @Test
    public void testPDFSegmentationWithComplexTable() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource("files/syngenta/CustomerFiles/SinglePages/Spanning Cells - Page131_S-Metolachlor_RAR_01_Volume_1_2018-09-06.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());
        assertThat(document.getSections().stream().flatMap(paragraph -> paragraph.getTables().stream()).collect(Collectors.toList())).isNotEmpty();
        TablePageBlock table = document.getSections().stream().flatMap(paragraph -> paragraph.getTables().stream()).toList().get(0);
        assertThat(table.getColCount()).isEqualTo(6);
        assertThat(table.getRowCount()).isEqualTo(13);
        assertThat(table.getRows().stream().mapToInt(List::size).sum()).isEqualTo(6 * 13);
    }


    @Test
    public void testTableExtraction() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource(
                "files/syngenta/CustomerFiles/SinglePages/Merge Table - Page5_26 A8637C - EU AIR3 - LCP Section 10 - Ecotoxicological studies on the plant protection product - Reference list.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());
        assertThat(document.getSections()
                           .stream()
                           .flatMap(paragraph -> paragraph.getTables()
                                   .stream())
                           .collect(Collectors.toList())).isNotEmpty();
        TablePageBlock firstTable = document.getSections()
                .stream()
                .flatMap(paragraph -> paragraph.getTables()
                        .stream())
                .toList()
                .get(0);
        assertThat(firstTable.getColCount()).isEqualTo(8);
        assertThat(firstTable.getRowCount()).isEqualTo(1);
        TablePageBlock secondTable = document.getSections()
                .stream()
                .flatMap(paragraph -> paragraph.getTables()
                        .stream())
                .toList()
                .get(1);
        assertThat(secondTable.getColCount()).isEqualTo(8);
        assertThat(secondTable.getRowCount()).isEqualTo(2);
        List<List<Cell>> firstTableHeaderCells = firstTable.getRows()
                .get(0)
                .stream()
                .map(Collections::singletonList)
                .collect(Collectors.toList());
        assertThat(secondTable.getRows()
                           .stream()
                           .allMatch(row -> row.stream()
                                   .map(Cell::getHeaderCells)
                                   .toList().equals(firstTableHeaderCells))).isTrue();
    }


    @Test
    public void testMultiPageMetadataPropagation() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource(
                "files/syngenta/CustomerFiles/SinglePages/Merge Multi Page Table - Page4_Page5_51 Fludioxonil_RAR_02_Volume_2_2018-02-21.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());
        assertThat(document.getSections()
                           .stream()
                           .flatMap(paragraph -> paragraph.getTables()
                                   .stream())
                           .collect(Collectors.toList())).isNotEmpty();
        TablePageBlock firstTable = document.getSections()
                .stream()
                .flatMap(paragraph -> paragraph.getTables()
                        .stream())
                .toList()
                .get(0);
        assertThat(firstTable.getColCount()).isEqualTo(9);
        assertThat(firstTable.getRowCount()).isEqualTo(5);
        TablePageBlock secondTable = document.getSections()
                .stream()
                .flatMap(paragraph -> paragraph.getTables()
                        .stream())
                .toList()
                .get(1);
        assertThat(secondTable.getColCount()).isEqualTo(9);
        assertThat(secondTable.getRowCount()).isEqualTo(6);
        List<List<Cell>> firstTableHeaderCells = firstTable.getRows()
                .get(firstTable.getRowCount() - 1)
                .stream()
                .map(Cell::getHeaderCells)
                .collect(Collectors.toList());
        assertThat(secondTable.getRows()
                           .stream()
                           .allMatch(row -> row.stream()
                                   .map(Cell::getHeaderCells)
                                   .toList().equals(firstTableHeaderCells))).isTrue();
    }


    @Test
    public void testHeaderCellsForRotatedTable() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource(
                "files/syngenta/CustomerFiles/SinglePages/Rotated Table Headers - Page4_65 Mesotrione - EU AIR3 - LCA Section 1 Supplement Reference List.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());
        assertThat(document.getSections()
                           .stream()
                           .flatMap(paragraph -> paragraph.getTables()
                                   .stream())
                           .collect(Collectors.toList())).isNotEmpty();
        TablePageBlock firstTable = document.getSections()
                .stream()
                .flatMap(paragraph -> paragraph.getTables()
                        .stream())
                .toList()
                .get(0);
        assertThat(firstTable.getColCount()).isEqualTo(8);
        assertThat(firstTable.getRowCount()).isEqualTo(1);
        TablePageBlock secondTable = document.getSections()
                .stream()
                .flatMap(paragraph -> paragraph.getTables()
                        .stream())
                .toList()
                .get(1);
        assertThat(secondTable.getColCount()).isEqualTo(8);
        assertThat(secondTable.getRowCount()).isEqualTo(6);
        List<List<Cell>> firstTableHeaderCells = firstTable.getRows()
                .get(0)
                .stream()
                .map(Collections::singletonList)
                .collect(Collectors.toList());
        assertThat(secondTable.getRows()
                           .stream()
                           .allMatch(row -> row.stream()
                                   .map(Cell::getHeaderCells)
                                   .toList().equals(firstTableHeaderCells))).isTrue();
    }


    @Test
    public void testDoc56Page170() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource("files/syngenta/CustomerFiles/SinglePages/Page170_56 Fludioxonil_RAR_12_Volume_3CA_B-7_2018-02-21.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());

        validateTableSize(document, 4);

        validateTable(document, 0, 1, 1, 0, 0);
        validateTable(document, 1, 2, 2, 0, 0);
        validateTable(document, 2, 4, 19, 12, 0);
        validateTable(document, 3, 2, 12, 0, 0);

    }


    @Test
    public void testDoc211() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource("files/211.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());

        validateTableSize(document, 4);

        validateTable(document, 0, 5, 4, 0, 0);
        validateTable(document, 1, 5, 15, 14, 0);
        validateTable(document, 2, 5, 14, 11, 0);
        validateTable(document, 3, 5, 3, 0, 0);

    }


    @Test
    public void testVV931175Page1() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource("files/syngenta/CustomerFiles/SinglePages/Page1_VV-931175.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());

        validateTableSize(document, 1);

        validateTable(document, 0, 8, 8, 0, 0);

        List<List<String>> values = Arrays.asList(Arrays.asList("Annex point Reference within DAR/RAR",
                        "Author, date",
                        "Study title",
                        "Analytical method Author, date, No.",
                        "Technique, LOQ of the method, validated working range",
                        "Method meets analytical validation criteria",
                        "Remarks (in case validation criteria are not met)",
                        "Acceptability of the method"),
                Arrays.asList("Part (a) Methods in soil, water, sediment, air and any additional matrices used in support of environmental fate studies",
                        "Part (a) Methods in soil, water, sediment, air and any additional matrices used in support of environmental fate studies",
                        "Part (a) Methods in soil, water, sediment, air and any additional matrices used in support of environmental fate studies",
                        "Part (a) Methods in soil, water, sediment, air and any additional matrices used in support of environmental fate studies",
                        "Part (a) Methods in soil, water, sediment, air and any additional matrices used in support of environmental fate studies",
                        "Part (a) Methods in soil, water, sediment, air and any additional matrices used in support of environmental fate studies",
                        "Part (a) Methods in soil, water, sediment, air and any additional matrices used in support of environmental fate studies",
                        "Part (a) Methods in soil, water, sediment, air and any additional matrices used in support of environmental fate studies"),
                Arrays.asList("CA 7.1.2.1.1 DAR (2009)",
                        "Evans P.G. 2001 TMJ4569B, VV-323245",
                        "Azoxystrobin Laboratory Degradation Study in Three Soil Types, Sampled from Holland and the United Kingdom",
                        "Method: RAM 269 Johnson R.I., Tummon O.J., Earl M. 1995 RJ1864B, VV-377731 Johnson R.I., Tummon O.J., Earl M. 1998 RAM 269/02, VV-124072 Johnson R.I., Tummon O.J., Earl M. 2000 RAM 269/03, VV-123986 Validation: Robinson N.J. 2001 TMJ4617B, VV-895845",
                        "LC-MS/MS LOQ: 0.01 mg/kg (R401553 (SYN50165 7), R402173 (SYN501114 )) or 0.02 mg/kg (azoxystrobin, R230310, R234886) Working range: 0.02-1.0 or 0.01-0.5 mg/kg (depending on analyte) Other supporting quantificati on methods: HPLC-UV GC-MSD",
                        "Y",
                        "N/A",
                        "Y"));

        validateTable(document, 0, values);

    }


    @Test
    public void testDoc27Page6() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource(
                "files/syngenta/CustomerFiles/SinglePages/Page6_27 A8637C - EU AIR3 - MCP Section 1 - Identity of the plant protection product.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());

        toHtml(document, "/tmp/html.html");

        validateTableSize(document, 4);

        validateTable(document, 0, 3, 2, 0, 0);
        validateTable(document, 1, 3, 2, 0, 0);
        validateTable(document, 2, 3, 3, 0, 0);
        validateTable(document, 3, 3, 3, 0, 0);

    }


    @Test
    @Disabled // FIXME Fake Redactions leads to more cells, no solution for this currently
    public void testDocA20622APartB9Page185() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource("files/syngenta/CustomerFiles/SinglePages/Page185_A20622A izRMS (CZ) fRR Part B9.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());

        validateTableSize(document, 1);

        validateTable(document, 0, 7, 4, 0, 0);
    }


    @Test
    public void testDocA20622APartB9Page185FixedDoc() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource("files/syngenta/CustomerFiles/SinglePages/Page185_fixed_A20622A izRMS (CZ) fRR Part B9.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());

        validateTableSize(document, 1);

        validateTable(document, 0, 7, 4, 0, 0);
    }


    @Test
    public void testDocA20622APartB7Page123() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource("files/syngenta/CustomerFiles/SinglePages/Page123_A20622A izZRMS (CZ) fRR Part B7.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());

        validateTableSize(document, 6);

        validateTable(document, 0, 2, 1, 0, 0);
        validateTable(document, 1, 2, 1, 0, 0);
        validateTable(document, 2, 2, 5, 0, 0);
        validateTable(document, 3, 2, 5, 0, 0);
        validateTable(document, 4, 2, 4, 0, 0);
        validateTable(document, 5, 2, 1, 0, 0);

    }


    @Test
    public void testDoc77Page111() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource("files/syngenta/CustomerFiles/SinglePages/Page11_77 Pirimicarb_RAR_08_Volume_3CA_B-6_2017-12-04.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());

        validateTableSize(document, 3);

        validateTable(document, 0, 7, 9, 0, 0);
        validateTable(document, 1, 2, 1, 0, 0);
        validateTable(document, 2, 2, 10, 0, 0);

    }


    @Test
    public void testDoc95Page532() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource("files/syngenta/CustomerFiles/SinglePages/Page532_95 Trinexapac-ethyl_RAR_08_Volume_3CA_B-6_2018-01-10.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());

        validateTableSize(document, 1);
        validateTable(document, 0, 9, 9, 0, 0);

    }


    @Test
    public void testDoc52Page175() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource("files/syngenta/CustomerFiles/SinglePages/Page175_52 Fludioxonil_RAR_07_Volume_3CA_B-5_2018-02-21.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());

        validateTableSize(document, 1);

        validateTable(document, 0, 9, 5, 6, 0);

    }


    @Test
    public void testDoc52Page174() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource("files/syngenta/CustomerFiles/SinglePages/Page174_52 Fludioxonil_RAR_07_Volume_3CA_B-5_2018-02-21.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());

        validateTableSize(document, 1);
        validateTable(document, 0, 9, 6, 7, 0);

    }


    @Test
    public void testDoc19Page35() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource(
                "files/syngenta/CustomerFiles/SinglePages/Page35_19 Chlorothalonil RAR 08 Volume 3CA B 6b metabolites Oct 2017.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());

        validateTableSize(document, 1);
        validateTable(document, 0, 10, 6, 0, 0);

    }


    @Test
    public void testDoc19Page161() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource(
                "files/syngenta/CustomerFiles/SinglePages/Page161_19 Chlorothalonil RAR 08 Volume 3CA B 6b metabolites Oct 2017.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());

        validateTableSize(document, 2);
        validateTable(document, 0, 2, 2, 0, 0);
        validateTable(document, 1, 1, 1, 0, 0);

    }


    @Test
    public void testDoc47Page30() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource(
                "files/syngenta/CustomerFiles/SinglePages/Page30_47 Cyprodinil - EU AIR3 - MCA Section 5 Supplement - Toxicological and metabolism studies on the active substance.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());

        validateTableSize(document, 2);

        validateTable(document, 0, 7, 8, 1, 0);
        validateTable(document, 1, 7, 8, 1, 0);

    }


    @Test
    public void testDoc49Page61() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource(
                "files/syngenta/CustomerFiles/SinglePages/Page61_49 Cyprodinil - EU AIR3 - MCA Section 8 Supplement - Ecotoxicological studies on the active substance.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());

        validateTableSize(document, 2);

        validateTable(document, 0, 4, 17, 0, 0);
        validateTable(document, 1, 7, 12, 0, 0);

    }


    @Test
    public void testDoc81Page54() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource(
                "files/syngenta/CustomerFiles/SinglePages/Page54_81 Pirimicarb_RAR_20_Volume_3CP_A10788A (_Pirimor_)_B-9_2017-12-04.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());

        validateTableSize(document, 2);

        validateTable(document, 0, 5, 14, 4, 0);
        validateTable(document, 1, 7, 12, 0, 0);

    }


    @Test
    public void testDoc88Page134() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource("files/syngenta/CustomerFiles/SinglePages/Page134_85 Pydiflumetofen_DAR_08_Volume_3CA_B-6_2017-07-26.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());

        validateTableSize(document, 2);

        validateTable(document, 0, 5, 17, 3, 0);
        validateTable(document, 1, 5, 16, 2, 0);

    }


    @Test
    public void testDocThiabendazolePage18() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource("files/syngenta/CustomerFiles/SinglePages/Page18_Thiabendazole DAR Addendum for ED_April_2020.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());

        validateTableSize(document, 4);

        validateTable(document, 0, 4, 4, 0, 0);
        validateTable(document, 1, 1, 1, 0, 0);
        validateTable(document, 2, 2, 3, 0, 0);
        validateTable(document, 3, 1, 1, 0, 0);

    }


    @Test
    public void testDoc15Page18() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource(
                "files/syngenta/CustomerFiles/SinglePages/Page18_15 - Pretilachlor - Acute Oral Toxicity (Up and Down Procedure) - Rat.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());

        validateTableSize(document, 1);

        validateTable(document, 0, 11, 8, 0, 0);

    }


    @Test
    public void testDoc28Page23() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource(
                "files/syngenta/CustomerFiles/SinglePages/Page23_28 A8637C - EU AIR3 - MCP Section 10 - Ecotoxicological studies on the plant protection product.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());

        validateTableSize(document, 2);

        validateTable(document, 0, 6, 8, 0, 0);
        validateTable(document, 1, 6, 8, 0, 0);

    }


    @Test
    public void testDoc24Page17() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource("files/syngenta/CustomerFiles/SinglePages/Page17_24 - SYN549522 - Acute Oral Toxicity - Rats.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());

        validateTableSize(document, 1);

        validateTable(document, 0, 9, 5, 2, 0);

    }


    @Test
    public void testDoc30Page5() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource("files/SinglePages/Page5_30 - Dicamba - Acute Oral Toxicity - Rats.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());

        validateTableSize(document, 1);

        validateTable(document, 0, 3, 5, 0, 0);

    }


    @Test
    public void testT0() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource("files/SinglePages/T0 TableWithMergedCells.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());

        validateTableSize(document, 1);

        validateTable(document, 0, 6, 8, 0, 0);
    }


    @Test
    public void testT1() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource("files/SinglePages/T1 MultipleNestedTable.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());

        validateTableSize(document, 4);

        validateTable(document, 0, 3, 3, 0, 0);
        validateTable(document, 1, 3, 6, 2, 0);
        validateTable(document, 2, 3, 3, 1, 0);
        validateTable(document, 3, 3, 3, 0, 0);

    }


    @Test
    public void testT2() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource("files/SinglePages/T2 MultipleTables.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());

        validateTableSize(document, 6);

        validateTable(document, 0, 5, 5, 0, 0);
        validateTable(document, 1, 5, 6, 0, 0);
        validateTable(document, 2, 5, 5, 0, 0);
        validateTable(document, 3, 5, 5, 0, 0);
        validateTable(document, 4, 5, 5, 0, 0);
        validateTable(document, 5, 5, 5, 0, 0);

    }


    @Test
    public void testT3() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource("files/syngenta/CustomerFiles/SinglePages/T3_Page29_S-Metolachlor_RAR_01_Volume_1_2018-09-06.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());

        validateTableSize(document, 1);

        validateTable(document, 0, 6, 5, 0, 0);

    }


    @Test
    public void testT4() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource("files/syngenta/CustomerFiles/SinglePages/T4_Page16_138 IDD0000261736.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());

        validateTableSize(document, 1);

        validateTable(document, 0, 5, 8, 1, 0);

    }


    @Test
    public void testT5() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource("files/syngenta/CustomerFiles/SinglePages/T5_Page16_VV-640252.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());

        validateTableSize(document, 5);
        validateTable(document, 0, 1, 1, 0, 0);
        validateTable(document, 1, 1, 1, 0, 0);
        validateTable(document, 2, 1, 1, 0, 0);
        validateTable(document, 3, 1, 1, 0, 0);
        validateTable(document, 4, 1, 1, 0, 0);

    }

    @Test
    public void testMergedEntities_Page26() throws IOException {

        ClassPathResource pdfFileResource = new ClassPathResource("files/syngenta/CustomerFiles/SinglePages/Page26_fRR A23317A PI0015600 CEU core part B6 - CZ.pdf");

        ClassificationDocument document = buildClassificationDocument(pdfFileResource.getFile());

        validateTableSize(document, 1);

        validateTable(document, 0, 6, 6, 5, 0);

    }


    @SneakyThrows
    private void toHtml(ClassificationDocument document, String filename) {

        var tables = document.getSections().stream().flatMap(paragraph -> paragraph.getTables().stream()).toList();
        StringBuilder sb = new StringBuilder();

        int currentPage = 1;
        for (var table : tables) {
            if (currentPage != table.getPage()) {
                currentPage = table.getPage();
                sb.append("---------------------- Page ").append(currentPage).append("--------------\n");
            }
            sb.append("\n\n");
            sb.append(table.getTextAsHtml());
        }

        try (FileOutputStream fileOutputStream = new FileOutputStream(Path.of(filename).toFile())) {
            fileOutputStream.write(sb.toString().getBytes());
        }
    }


    private void validateTable(ClassificationDocument document, int tableIndex, int colCount, int rowCount, int emptyCellsCountCorrect, int emptyCellsCountIncorrect) {

        TablePageBlock table = document.getSections().stream().flatMap(paragraph -> paragraph.getTables().stream()).toList().get(tableIndex);
        List<List<Cell>> rows = table.getRows();
        int emptyCellsFoundFound = rows.stream().flatMap(List::stream).toList().stream().filter(f -> f.toString().isEmpty()).toList().size();

        for (List<Cell> row : table.getRows()) {
            row.forEach(r -> System.out.println(r.toString()));
        }
        assertThat(emptyCellsFoundFound).isEqualTo(emptyCellsCountCorrect + emptyCellsCountIncorrect);

        assertThat(table.getColCount()).isEqualTo(colCount);
        assertThat(table.getRowCount()).isEqualTo(rowCount);

    }


    private void validateTable(ClassificationDocument document, int tableIndex, List<List<String>> values) {

        TablePageBlock table = document.getSections().stream().flatMap(paragraph -> paragraph.getTables().stream()).toList().get(tableIndex);
        List<List<Cell>> rows = table.getRows();

        List<Cell> rowsFlattened = rows.stream().flatMap(List::stream).toList();
        List<String> valuesFlattened = values.stream().flatMap(List::stream).toList();

        for (int i = 0; i < valuesFlattened.size(); i++) {
            Cell cell = rowsFlattened.get(i);
            String value = valuesFlattened.get(i);
            assertThat(cell.toString()).isEqualTo(value);
        }

    }


    private void validateTableSize(ClassificationDocument document, int tableSize) {

        assertThat(document.getSections().stream().flatMap(paragraph -> paragraph.getTables().stream()).toList().size()).isEqualTo(tableSize);

    }

}
