package eu.dnetlib.validator2.engine.builtins

import eu.dnetlib.validator2.engine.RuleContext
import eu.dnetlib.validator2.engine.XMLHelper
import eu.dnetlib.validator2.engine.contexts.NotConfusedFieldsContext
import org.w3c.dom.Document
import spock.lang.Specification
import spock.lang.Unroll

class XMLNotConfusedFieldsRuleSpecification extends Specification {

    static final String XPATH1 = "//node/attr[@id='xpath1']/text()"
    static final String XPATH2 = "//node/attr[@id='xpath2']/text()"
    static final String XPATH3 = "//node/attr[@id='xpath3']/text()"
    static final String XPATH4 = "//node/attr[@id='xpath4']/text()" // Confused with xpath3

    def ruleBuilder1 = XMLNotConfusedFieldsRule.builder()
    def ruleBuilder2 = XMLNotConfusedFieldsRule.builder()

    def "Builder works"() {
        given:
        def id = "1"
        def fields = "xpath1,xpath2,xpath3"

        when: "setting each property explicitly and calling build()"
        def rule1 = ruleBuilder1.
                setId(id).
                setFields(fields).
                build()

        then:
        ruleContextMatchesProps(rule1.getContext(), id, fields, traversal, xpaths)

        when: "building from map containing properties"
        def rule2 = ruleBuilder2.buildFrom([
                (RuleContext.ID_PROPERTY_NAME)          : id,
                (NotConfusedFieldsContext.PROPERTY_NAME): fields
        ])

        then:
        ruleContextMatchesProps(rule2.getContext(), id, fields, traversal, xpaths)

        where:
        xpaths = ["xpath1", "xpath2", "xpath3"]
        traversal = "all"
    }

    void ruleContextMatchesProps(context, id, fields, traversal, xpaths) {
        assert context.getIdProperty().getValue() == id
        assert context.getFieldsProperty().getValue() == fields
        assert context.getFieldsProperty().getXpaths() == xpaths
        assert context.getNodeListActionProperty().getValue() == traversal
    }

    def "Builder handles premature build() call"() {
        when:
        ruleBuilder1.build()

        then:
        thrown(IllegalStateException)

        when:
        ruleBuilder2.
                setFields("xpath1,xpath2,xpath3").
                build()

        then:
        thrown(IllegalStateException)
    }

    @Unroll
    def "Builder validates invalid input properties => id: #id, fields: #fields"() {
        when: "setting each property explicitly before calling build()"
        ruleBuilder1.
                setId(id).
                setFields(fields)

        then:
        thrown(expectedException)

        when: "reading properties from map"
        ruleBuilder2.buildFrom([
                (RuleContext.ID_PROPERTY_NAME)          : id,
                (NotConfusedFieldsContext.PROPERTY_NAME): fields
        ])

        then:
        thrown(expectedException)

        where:
        id   | fields          || expectedException
        ""   | "xpath1,xpath2" || IllegalArgumentException
        null | "xpath1,xpath2" || IllegalArgumentException
        "1"  | ""              || IllegalArgumentException
        "1"  | null            || IllegalArgumentException
        ""   | ""              || IllegalArgumentException
        null | null            || IllegalArgumentException
    }

    @Unroll
    def "Input fields: #fields, results in #result"() {
        given:
        Document doc = XMLHelper.parse("/eu/dnetlib/validator2/engine/xml-not-confused-fields.xml")
        assert doc != null

        when: "setting each property explicitly before calling build()"
        def rule1 = ruleBuilder1.
                setId(id).
                setFields(fields).
                build()

        then:
        rule1.test(doc) == result
        noExceptionThrown()

        when: "building from map containing properties"
        def rule2 = ruleBuilder2.buildFrom([
                (RuleContext.ID_PROPERTY_NAME)          : id,
                (NotConfusedFieldsContext.PROPERTY_NAME): fields
        ])

        then:
        rule2.test(doc) == result
        noExceptionThrown()

        where: "only xpath4 and xpath3 have confused fields"
        id  | fields                                              || result
        "1" | XPATH1                                              || true
        "1" | XPATH1 + ',' + XPATH2                               || true
        "1" | XPATH3 + ',' + XPATH2                               || true
        "1" | XPATH1 + ',' + XPATH4                               || true
        "1" | XPATH2 + ',' + XPATH4                               || true
        "1" | XPATH2 + ',' + XPATH1 + ',' + XPATH3                || true
        "1" | XPATH3 + ',' + XPATH1 + ',' + XPATH2                || true
        "1" | XPATH1 + ',' + XPATH2 + ',' + XPATH3                || true
        "1" | XPATH1 + ',' + XPATH2 + ',' + XPATH4                || true
        "1" | XPATH4 + ',' + XPATH3                               || false
        "1" | XPATH1 + ',' + XPATH2 + ',' + XPATH3 + ',' + XPATH4 || false
    }

}
