package matcher

import (
	"fmt"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/types"
)

var _ = Describe("test all collection assertions", func() {
	dualAssertion := func() types.GomegaMatcher { return BeNumerically(">=", 3) }

	Context("All", func() { // All asserts All elements should pass.
		array := []int{1, 2, 4, 6}
		slice := array[:]

		It("should passed when used with array, as all elements passed", func() {
			r, err := All(func() types.GomegaMatcher { return BeNumerically(">=", 1) }).Match(array)

			Expect(err).ShouldNot(HaveOccurred())
			Expect(r).Should(BeTrue())
		})
		It("should passed when used with slice, as all elements passed", func() {
			r, err := All(func() types.GomegaMatcher { return BeNumerically(">=", 1) }).Match(slice)

			Expect(err).ShouldNot(HaveOccurred())
			Expect(r).Should(BeTrue())
		})
		It("should failed when any element failed", func() {
			assertion := All(dualAssertion)
			r, err := assertion.Match(array)
			failureMessage := assertion.FailureMessage(array)

			fmt.Println(failureMessage)

			Expect(err).ShouldNot(HaveOccurred())
			Expect(r).Should(BeFalse())
			Expect(failureMessage).ShouldNot(BeEmpty())
		})
		It("should failed when chained with ShouldNot", func() { // Any
			// All(BeNumerically, ">=", 1)
			assertion := All(func() types.GomegaMatcher { return BeNumerically(">=", 1) })
			r, err := assertion.Match(array)
			r = !r // such negation is done by the driver of Match, as in gomega@v1.10.1/internal/assertion/assertion.go:63
			failureMessage := assertion.NegatedFailureMessage(array)

			fmt.Println(failureMessage)

			Expect(err).ShouldNot(HaveOccurred())
			Expect(r).Should(BeFalse())
			Expect(failureMessage).ShouldNot(BeEmpty())
		})

		It("should panic when not call Match first", func() {
			Expect(func() {
				Any(func() types.GomegaMatcher { return BeNumerically(">=", 3) }).FailureMessage(array)
			}).Should(PanicWithOutput("on All, Match should be called first"))
		})
	})

	Context("Any", func() { // Any asserts any element should pass.
		array := []int{1, 2, 4, 6}
		slice := array[:]

		It("should passed when used with array, and some element failed", func() {
			r, err := Any(func() types.GomegaMatcher { return BeNumerically(">=", 3) }).Match(array)

			Expect(err).ShouldNot(HaveOccurred())
			Expect(r).Should(BeTrue())
		})
		It("should passed when used with slice, and some element failed", func() {
			r, err := Any(func() types.GomegaMatcher { return BeNumerically(">=", 3) }).Match(slice)

			Expect(err).ShouldNot(HaveOccurred())
			Expect(r).Should(BeTrue())
		})
		It("should failed when no elements passed", func() {
			assertion := Any(func() types.GomegaMatcher { return BeNumerically(">=", 7) })
			r, err := assertion.Match(array)
			failureMessage := assertion.FailureMessage(array)

			fmt.Println(failureMessage)

			Expect(err).ShouldNot(HaveOccurred())
			Expect(r).Should(BeFalse())
			Expect(failureMessage).ShouldNot(BeEmpty())
		})
		It("should as if All were used when chained with ShouldNot", func() { // All
			assertion := Any(dualAssertion)
			r, err := assertion.Match(array)
			r = !r // such negation is done by the driver of Match, as in gomega@v1.10.1/internal/assertion/assertion.go:63P
			failureMessage := assertion.NegatedFailureMessage(array)

			fmt.Println(failureMessage)

			Expect(err).ShouldNot(HaveOccurred())
			Expect(r).Should(BeFalse())
		})

		It("should panic when not call Match first", func() {
			Expect(func() {
				Any(func() types.GomegaMatcher {
					return BeNumerically(">=", 3)
				}).FailureMessage(array)
			}).Should(PanicWithOutput("on Any, Match should be called first"))
		})
	})
})
