package match_test

import (
	"testing"

	"git.codemonkeysoftware.net/b/peachy-go/csexp"
	"git.codemonkeysoftware.net/b/peachy-go/csexp/match"
	"github.com/shoenig/test"
	"github.com/shoenig/test/must"
)

func TestMatchLiteral(t *testing.T) {
	t.Run("same", func(t *testing.T) {
		atom := csexp.Atom("hello")
		err := match.MustCompile(atom.String()).Match(atom)
		must.NoError(t, err)
	})
	t.Run("different", func(t *testing.T) {
		atom := csexp.Atom("hello")
		pattern := csexp.Atom("goodbye").String()

		err := match.MustCompile(pattern).Match(atom)

		var mismatch match.MismatchError
		must.ErrorAs(t, err, &mismatch)
		test.Equal(t, mismatch.Got, csexp.Sexp(atom))
		test.StrContains(t, mismatch.Expected, pattern)
	})
}

func TestMatchList(t *testing.T) {
	t.Run("both empty", func(t *testing.T) {
		err := match.MustCompile("()").Match(csexp.List(nil))
		must.NoError(t, err)
	})
	t.Run("same length", func(t *testing.T) {
		list := csexp.List{csexp.Atom("a"), csexp.Atom("b"), csexp.Atom("c")}
		err := match.MustCompile(list.String()).Match(list)
		must.NoError(t, err)
	})
	t.Run("sexp too long", func(t *testing.T) {
		list := csexp.List{csexp.Atom("a"), csexp.Atom("b"), csexp.Atom("c")}
		pattern := list[0:2].String()

		err := match.MustCompile(pattern).Match(list)

		var mismatch match.MismatchError
		must.ErrorAs(t, err, &mismatch)
		test.Equal(t, mismatch.Got, csexp.Sexp(list))
		test.StrContains(t, mismatch.Expected, "list of 2 items")
	})
	t.Run("sexp too short", func(t *testing.T) {
		list := csexp.List{csexp.Atom("a"), csexp.Atom("b"), csexp.Atom("c")}
		shortList := list[0:2]

		err := match.MustCompile(list.String()).Match(shortList)

		var mismatch match.MismatchError
		must.ErrorAs(t, err, &mismatch)
		test.Equal(t, mismatch.Got, csexp.Sexp(shortList))
		test.StrContains(t, mismatch.Expected, "list of 3 items")
	})
}

func TestExtract(t *testing.T) {
	matcher := match.MustCompile("(3:abc%s(%s3:jkl)%s3:pqr%s)")
	sexp, err := csexp.Parse([]byte("(3:abc3:def(3:ghi3:jkl)3:mno3:pqr3:stu)"))
	must.NoError(t, err)
	t.Run("correct number of operands", func(t *testing.T) {
		var def, ghi, mno, stu string
		err := matcher.Match(sexp, &def, &ghi, &mno, &stu)
		must.NoError(t, err)
		test.EqOp(t, "def", def)
		test.EqOp(t, "ghi", ghi)
		test.EqOp(t, "mno", mno)
		test.EqOp(t, "stu", stu)
	})
	t.Run("too many operands", func(t *testing.T) {
		var def, ghi, mno, stu, extra string
		err := matcher.Match(sexp, &def, &ghi, &mno, &stu, &extra)
		must.ErrorContains(t, err, "too many operands")
		test.EqOp(t, "def", def)
		test.EqOp(t, "ghi", ghi)
		test.EqOp(t, "mno", mno)
		test.EqOp(t, "stu", stu)
	})
	t.Run("too few operands", func(t *testing.T) {
		var def, ghi, mno string
		err := matcher.Match(sexp, &def, &ghi, &mno)
		must.ErrorContains(t, err, "too few operands")
		test.EqOp(t, "def", def)
		test.EqOp(t, "ghi", ghi)
		test.EqOp(t, "mno", mno)
	})
}
