package eu.dnetlib.validator2.validation.guideline.openaire

import eu.dnetlib.validator2.engine.Helper
import eu.dnetlib.validator2.engine.XMLHelper
import eu.dnetlib.validator2.validation.XMLApplicationProfile
import org.w3c.dom.Document
import spock.lang.IgnoreIf
import spock.lang.Specification

import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import java.util.function.Predicate
import java.util.zip.ZipEntry

@IgnoreIf({ ZIP_DIR == null })
class ZippedXMLRecordsSpecification extends Specification {

    static final String ZIP_DIR = System.getProperty("ZIP_DIR")

    private static final int BUFFER_SIZE = 1024 * 16

    private static final XMLApplicationProfile LV3 = new LiteratureGuidelinesV3Profile()
    private static final XMLApplicationProfile LV4 = new LiteratureGuidelinesV4Profile()
    private static final XMLApplicationProfile DA2 = new DataArchiveGuidelinesV2Profile()

    def "Log sequential validation performance"() {
        given:
        File zipFile = new File(ZIP_DIR, dataSource + ".zip")
        long totalDocs = 0
        long totalMillis = 0

        when:
        Predicate<ZipEntry> filter = Helper.ZIP_ENTRY_IS_FILE
        XMLHelper.forEachZippedXMLDocument(zipFile, BUFFER_SIZE, filter) { String id, Document doc ->
            long start = System.currentTimeMillis()
            //TODO: Do something with the result
            profile.validate(id, doc)
            long end = System.currentTimeMillis()
            totalMillis += end - start
            totalDocs++
        }
        println("Validated sequentially $totalDocs xml records of data source $dataSource in ${totalMillis / 1000} sec")

        then:
        totalDocs == count

        where:
        dataSource                                  | count  | profile
        "r3d100010214_EASY_v2_data_oai_datacite"    | 124747 | DA2
        "3631_Agritrop_v3_literature_oai_dc"        | 107099 | LV3
        "4272_MediaRep_v4_literature_oai_openaire"  | 12841  | LV4
        "3484_UPCommons_v4_literature_oai_openaire" | 115670 | LV4
    }

    def "Log sequential zip parsing performance"() {
        given:
        File zipFile = new File(ZIP_DIR, dataSource + ".zip")
        long totalDocs = 0

        when:
        Predicate<ZipEntry> filter = Helper.ZIP_ENTRY_IS_FILE
        long start = System.currentTimeMillis()
        XMLHelper.forEachZippedXMLDocument(zipFile, BUFFER_SIZE, filter) { String id, Document doc ->
            totalDocs++
        }
        long end = System.currentTimeMillis()
        long totalMillis = end - start
        println("Parsed $totalDocs xml records from zip file of data source $dataSource in ${totalMillis / 1000} sec")

        then:
        totalDocs == count

        where:
        dataSource                                  | count  | profile
        "r3d100010214_EASY_v2_data_oai_datacite"    | 124747 | DA2
        "3631_Agritrop_v3_literature_oai_dc"        | 107099 | LV3
        "4272_MediaRep_v4_literature_oai_openaire"  | 12841  | LV4
        "3484_UPCommons_v4_literature_oai_openaire" | 115670 | LV4
    }

    def "Log sequential zip parsing performance along with validation performance"() {
        given:
        File zipFile = new File(ZIP_DIR, dataSource + ".zip")
        long totalDocs = 0
        long validationTotalMillis = 0

        when:
        Predicate<ZipEntry> filter = Helper.ZIP_ENTRY_IS_FILE
        long combinedStart = System.currentTimeMillis()
        XMLHelper.forEachZippedXMLDocument(zipFile, BUFFER_SIZE, filter) { String id, Document doc ->
            long validationStart = System.currentTimeMillis()
            //TODO: Do something with the result
            profile.validate(id, doc)
            long validationEnd = System.currentTimeMillis()
            validationTotalMillis += validationEnd - validationStart
            totalDocs++
        }
        long combinedEnd = System.currentTimeMillis()
        long combinedTotalMillis = combinedEnd - combinedStart
        long zipParsingTotalMillis = combinedTotalMillis - validationTotalMillis
        println("Processed $totalDocs xml records of data source $dataSource in ${combinedTotalMillis / 1000} sec")
        println("Validated $totalDocs xml records of data source $dataSource in ${validationTotalMillis / 1000} sec")
        println("Parsed $totalDocs xml records from zip file of data source $dataSource in ${zipParsingTotalMillis / 1000} sec")

        then:
        totalDocs == count

        where:
        dataSource                                  | count  | profile
        "r3d100010214_EASY_v2_data_oai_datacite"    | 124747 | DA2
        "3631_Agritrop_v3_literature_oai_dc"        | 107099 | LV3
        "4272_MediaRep_v4_literature_oai_openaire"  | 12841  | LV4
        "3484_UPCommons_v4_literature_oai_openaire" | 115670 | LV4
    }

    def "Log concurrent total validation performance"() {
        given:
        File zipFile = new File(ZIP_DIR, dataSource + ".zip")
        def futures = []
        def executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors())
        long maxAwaitTime = 60 // (in minutes) at executor shutdown for each data source

        when:
        Predicate<ZipEntry> filter = Helper.ZIP_ENTRY_IS_FILE
        long validationStart = System.currentTimeMillis()
        XMLHelper.forEachZippedXMLDocument(zipFile, BUFFER_SIZE, filter) { String id, Document doc ->
            futures << executor.submit {
                profile.validate(id, doc)
            }
        }

        then:
        futures.size() == count
        executor.shutdown()
        boolean finishedWithinAwaitTime = executor.awaitTermination(maxAwaitTime, TimeUnit.MINUTES)
        if (finishedWithinAwaitTime) {
            long validationEnd = System.currentTimeMillis()
            long totalMillis = validationEnd - validationStart
            println("Validated concurrently ${futures.size()} xml records of data source $dataSource in ${totalMillis / 1000} sec")
        } else {
            executor.shutdownNow()
            println("Executor tasks for data source $dataSource did not finish in await period of $maxAwaitTime minutes.")
        }

        where:
        dataSource                                  | count  | profile
        "r3d100010214_EASY_v2_data_oai_datacite"    | 124747 | DA2
        "3631_Agritrop_v3_literature_oai_dc"        | 107099 | LV3
        "4272_MediaRep_v4_literature_oai_openaire"  | 12841  | LV4
        "3484_UPCommons_v4_literature_oai_openaire" | 115670 | LV4
    }

}
