/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
*/

package gremlingo

import (
	"fmt"
	"math"
	"reflect"
	"regexp"
	"strings"
	"testing"
	"time"

	"github.com/google/uuid"
)

func Test_GremlinLang(t *testing.T) {
	type test struct {
		name                      string
		assert                    func(g *GraphTraversalSource) *GraphTraversal
		equals                    string
		only                      bool
		skip                      bool
		wantErr                   bool
		containsRandomClassParams bool
	}
	tests := []test{
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal { return g.V() },
			equals: "g.V()",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal { return g.V("1", "2", "3", "4") },
			equals: "g.V(\"1\",\"2\",\"3\",\"4\")",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal { return g.V("3").ValueMap(true) },
			equals: "g.V(\"3\").valueMap(true)",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal { return g.V().Constant(5) },
			equals: "g.V().constant(5)",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal { return g.V().Constant(1.5) },
			equals: "g.V().constant(1.5D)",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal { return g.V().Constant("Hello") },
			equals: "g.V().constant(\"Hello\")",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal { return g.V().HasLabel("airport").Limit(5) },
			equals: "g.V().hasLabel(\"airport\").limit(5)",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal { return g.V().HasLabel(P.Within("a", "b", "c")) },
			equals: "g.V().hasLabel(within([\"a\",\"b\",\"c\"]))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().HasLabel("airport", "continent").Out().Limit(5)
			},
			equals: "g.V().hasLabel(\"airport\",\"continent\").out().limit(5)",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().HasLabel("airport").Out().Values("code").Limit(5)
			},
			equals: "g.V().hasLabel(\"airport\").out().values(\"code\").limit(5)",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V("3").As("a").Out("route").Limit(10).Where(P.Eq("a")).By("region")
			},
			equals: "g.V(\"3\").as(\"a\").out(\"route\").limit(10).where(eq(\"a\")).by(\"region\")",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V("3").Repeat(T__.Out("route").SimplePath()).Times(2).Path().By("code")
			},
			equals: "g.V(\"3\").repeat(__.out(\"route\").simplePath()).times(2).path().by(\"code\")",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().HasLabel("airport").Out().Has("region", "US-TX").Values("code").Limit(5)
			},
			equals: "g.V().hasLabel(\"airport\").out().has(\"region\",\"US-TX\").values(\"code\").limit(5)",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().HasLabel("airport").Union(T__.Values("city"), T__.Values("region")).Limit(5)
			},
			equals: "g.V().hasLabel(\"airport\").union(__.values(\"city\"),__.values(\"region\")).limit(5)",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal { return g.V("3").As("a").Out("route", "routes") },
			equals: "g.V(\"3\").as(\"a\").out(\"route\",\"routes\")",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal { return g.V().Where(T__.Values("runways").Is(5)) },
			equals: "g.V().where(__.values(\"runways\").is(5))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V("3").Repeat(T__.Out().SimplePath()).Until(T__.Has("code", "AGR")).Path().By("code").Limit(5)
			},
			equals: "g.V(\"3\").repeat(__.out().simplePath()).until(__.has(\"code\",\"AGR\")).path().by(\"code\").limit(5)",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal { return g.V().HasLabel("airport").Order().By(T__.Id()) },
			equals: "g.V().hasLabel(\"airport\").order().by(__.id())",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal { return g.V().HasLabel("airport").Order().By(T.Id) },
			equals: "g.V().hasLabel(\"airport\").order().by(T.id)",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().HasLabel("airport").Order().By(T__.Id(), Order.Desc)
			},
			equals: "g.V().hasLabel(\"airport\").order().by(__.id(),Order.desc)",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().HasLabel("airport").Order().By("code", Order.Desc)
			},
			equals: "g.V().hasLabel(\"airport\").order().by(\"code\",Order.desc)",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V("1", "2", "3").Local(T__.Out().Out().Dedup().Fold())
			},
			equals: "g.V(\"1\",\"2\",\"3\").local(__.out().out().dedup().fold())",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V("3").Out().Path().Count(Scope.Local)
			},
			equals: "g.V(\"3\").out().path().count(Scope.local)",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.E().Count()
			},
			equals: "g.E().count()",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V("5").OutE("route").InV().Path().Limit(10)
			},
			equals: "g.V(\"5\").outE(\"route\").inV().path().limit(10)",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V("5").PropertyMap().Select(Column.Keys)
			},
			equals: "g.V(\"5\").propertyMap().select(Column.keys)",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V("5").PropertyMap().Select(Column.Values)
			},
			equals: "g.V(\"5\").propertyMap().select(Column.values)",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V("3").Values("runways").Math("_ + 1")
			},
			equals: "g.V(\"3\").values(\"runways\").math(\"_ + 1\")",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V("3").Emit().Repeat(T__.Out().SimplePath()).Times(3).Limit(5).Path()
			},
			equals: "g.V(\"3\").emit().repeat(__.out().simplePath()).times(3).limit(5).path()",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Match(T__.As("a").Has("code", "LHR").As("b")).Select("b").By("code")
			},
			equals: "g.V().match(__.as(\"a\").has(\"code\",\"LHR\").as(\"b\")).select(\"b\").by(\"code\")",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Has("test-using-keyword-as-property", "repeat")
			},
			equals: "g.V().has(\"test-using-keyword-as-property\",\"repeat\")",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V("1").AddE("test").To(T__.V("4"))
			},
			equals: "g.V(\"1\").addE(\"test\").to(__.V(\"4\"))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Values("runways").Max()
			},
			equals: "g.V().values(\"runways\").max()",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Values("runways").Min()
			},
			equals: "g.V().values(\"runways\").min()",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Values("runways").Sum()
			},
			equals: "g.V().values(\"runways\").sum()",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Values("runways").Mean()
			},
			equals: "g.V().values(\"runways\").mean()",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.WithSack(0).V("3", "5").Sack(Operator.Sum).By("runways").Sack()
			},
			equals: "g.withSack(0).V(\"3\",\"5\").sack(Operator.sum).by(\"runways\").sack()",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.Inject(3, 4, 5)
			},
			equals: "g.inject(3,4,5)",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.Inject([]interface{}{3, 4, 5})
			},
			equals: "g.inject([3,4,5])",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.Inject(3, 4, 5).Count()
			},
			equals: "g.inject(3,4,5).count()",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Has("runways", P.Gt(5)).Count()
			},
			equals: "g.V().has(\"runways\",gt(5)).count()",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Has("runways", P.Lte(5.3)).Count()
			},
			equals: "g.V().has(\"runways\",lte(5.3D)).count()",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Has("code", P.Within(123, 124))
			},
			equals: "g.V().has(\"code\",within([123,124]))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Has("code", P.Within(123, "abc"))
			},
			equals: "g.V().has(\"code\",within([123,\"abc\"]))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Has("code", P.Within("abc", 123))
			},
			equals: "g.V().has(\"code\",within([\"abc\",123]))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Has("code", P.Within("abc", "xyz"))
			},
			equals: "g.V().has(\"code\",within([\"abc\",\"xyz\"]))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V("1", "2").Has("region", P.Within("US-TX", "US-GA"))
			},
			equals: "g.V(\"1\",\"2\").has(\"region\",within([\"US-TX\",\"US-GA\"]))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().And(T__.Has("runways", P.Gt(5)), T__.Has("region", "US-TX"))
			},
			equals: "g.V().and(__.has(\"runways\",gt(5)),__.has(\"region\",\"US-TX\"))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Union(T__.Has("runways", P.Gt(5)), T__.Has("region", "US-TX"))
			},
			equals: "g.V().union(__.has(\"runways\",gt(5)),__.has(\"region\",\"US-TX\"))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V("3").Choose(T__.Values("runways").Is(3), T__.Constant("three"), T__.Constant("not three"))
			},
			equals: "g.V(\"3\").choose(__.values(\"runways\").is(3),__.constant(\"three\"),__.constant(\"not three\"))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V("3").Choose(T__.Values("runways")).Option(1, T__.Constant("three")).Option(2, T__.Constant("not three"))
			},
			equals: "g.V(\"3\").choose(__.values(\"runways\")).option(1,__.constant(\"three\")).option(2,__.constant(\"not three\"))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V("3").Choose(T__.Values("runways")).Option(1.5, T__.Constant("one and a half")).Option(2, T__.Constant("not three"))
			},
			equals: "g.V(\"3\").choose(__.values(\"runways\")).option(1.5D,__.constant(\"one and a half\")).option(2,__.constant(\"not three\"))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V("3").Repeat(T__.Out().SimplePath()).Until(T__.Loops().Is(1)).Count()
			},
			equals: "g.V(\"3\").repeat(__.out().simplePath()).until(__.loops().is(1)).count()",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().HasLabel("airport").Limit(20).Group().By("region").By("code").Order(Scope.Local).By(Column.Keys)
			},
			equals: "g.V().hasLabel(\"airport\").limit(20).group().by(\"region\").by(\"code\").order(Scope.local).by(Column.keys)",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V("1").As("a").V("2").As("a").Select(Pop.All, "a")
			},
			equals: "g.V(\"1\").as(\"a\").V(\"2\").as(\"a\").select(Pop.all,\"a\")",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.AddV("test").Property(Cardinality.Set, "p1", 10)
			},
			equals: "g.addV(\"test\").property(Cardinality.set,\"p1\",10)",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.AddV("test").Property(Cardinality.List, "p1", 10)
			},
			equals: "g.addV(\"test\").property(Cardinality.list,\"p1\",10)",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.AddV("test").Property(Cardinality.Single, "p1", 10)
			},
			equals: "g.addV(\"test\").property(Cardinality.single,\"p1\",10)",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Limit(5).Order().By(T__.Label())
			},
			equals: "g.V().limit(5).order().by(__.label())",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Range(1, 5)
			},
			equals: "g.V().range(1,5)",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.AddV("test").Property("p1", 123)
			},
			equals: "g.addV(\"test\").property(\"p1\",123)",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Has("date", P.Gt(time.Date(2021, 1, 1, 9, 30, 0, 0, time.UTC)))
			},
			equals: "g.V().has(\"date\",gt(datetime(\"2021-01-01T09:30:00.000Z\")))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.AddE("route").From(T__.V("1")).To(T__.V("2"))
			},
			equals: "g.addE(\"route\").from(__.V(\"1\")).to(__.V(\"2\"))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.WithSideEffect("a", []interface{}{1, 2}).V("3").Select("a")
			},
			equals: "g.withSideEffect(\"a\",[1,2]).V(\"3\").select(\"a\")",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.WithSideEffect("a", 1).V("3").Select("a")
			},
			equals: "g.withSideEffect(\"a\",1).V(\"3\").select(\"a\")",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.WithSideEffect("a", "abc").V("3").Select("a")
			},
			equals: "g.withSideEffect(\"a\",\"abc\").V(\"3\").select(\"a\")",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Has("airport", "region", "US-NM").Limit(3).Values("elev").Fold().Index()
			},
			equals: "g.V().has(\"airport\",\"region\",\"US-NM\").limit(3).values(\"elev\").fold().index()",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V("3").Repeat(T__.TimeLimit(1000).Out().SimplePath()).Until(T__.Has("code", "AGR")).Path()
			},
			equals: "g.V(\"3\").repeat(__.timeLimit(1000).out().simplePath()).until(__.has(\"code\",\"AGR\")).path()",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().HasLabel("airport").Where(T__.Values("elev").Is(P.Gt(14000)))
			},
			equals: "g.V().hasLabel(\"airport\").where(__.values(\"elev\").is(gt(14000)))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().HasLabel("airport").Where(T__.Out().Count().Is(P.Gt(250))).Values("code")
			},
			equals: "g.V().hasLabel(\"airport\").where(__.out().count().is(gt(250))).values(\"code\")",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().HasLabel("airport").Filter(T__.Out().Count().Is(P.Gt(250))).Values("code")
			},
			equals: "g.V().hasLabel(\"airport\").filter(__.out().count().is(gt(250))).values(\"code\")",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.WithSack(0).
					V("3").
					Repeat(T__.OutE("route").Sack(Operator.Sum).By("dist").InV()).
					Until(T__.Has("code", "AGR").Or().Loops().Is(4)).
					Has("code", "AGR").
					Local(T__.Union(T__.Path().By("code").By("dist"), T__.Sack()).Fold()).
					Limit(10)
			},
			equals: "g.withSack(0).V(\"3\").repeat(__.outE(\"route\").sack(Operator.sum).by(\"dist\").inV()).until(__.has(\"code\",\"AGR\").or().loops().is(4)).has(\"code\",\"AGR\").local(__.union(__.path().by(\"code\").by(\"dist\"),__.sack()).fold()).limit(10)",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.AddV().As("a").AddV().As("b").AddE("knows").From("a").To("b")
			},
			equals: "g.addV().as(\"a\").addV().as(\"b\").addE(\"knows\").from(\"a\").to(\"b\")",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.AddV("Person").As("a").AddV("Person").As("b").AddE("knows").From("a").To("b")
			},
			equals: "g.addV(\"Person\").as(\"a\").addV(\"Person\").as(\"b\").addE(\"knows\").from(\"a\").to(\"b\")",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V("3").Project("Out", "In").By(T__.Out().Count()).By(T__.In().Count())
			},
			equals: "g.V(\"3\").project(\"Out\",\"In\").by(__.out().count()).by(__.in().count())",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V("44").Out().Aggregate("a").Out().Where(P.Within("a")).Path()
			},
			equals: "g.V(\"44\").out().aggregate(\"a\").out().where(within(\"a\")).path()",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Has("date", time.Date(2021, 2, 22, 0, 0, 0, 0, time.UTC))
			},
			equals: "g.V().has(\"date\",datetime(\"2021-02-22T00:00:00.000Z\"))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Has("date", P.Within(time.Date(2021, 2, 22, 0, 0, 0, 0, time.UTC), time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC)))
			},
			equals: "g.V().has(\"date\",within([datetime(\"2021-02-22T00:00:00.000Z\"),datetime(\"2021-01-01T00:00:00.000Z\")]))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Has("date", P.Between(time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC), time.Date(2021, 2, 22, 0, 0, 0, 0, time.UTC)))
			},
			equals: "g.V().has(\"date\",between(datetime(\"2021-01-01T00:00:00.000Z\"),datetime(\"2021-02-22T00:00:00.000Z\")))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Has("date", P.Inside(time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC), time.Date(2021, 2, 22, 0, 0, 0, 0, time.UTC)))
			},
			equals: "g.V().has(\"date\",inside(datetime(\"2021-01-01T00:00:00.000Z\"),datetime(\"2021-02-22T00:00:00.000Z\")))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Has("date", P.Gt(time.Date(2021, 1, 1, 9, 30, 0, 0, time.UTC)))
			},
			equals: "g.V().has(\"date\",gt(datetime(\"2021-01-01T09:30:00.000Z\")))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Has("runways", P.Between(3, 5))
			},
			equals: "g.V().has(\"runways\",between(3,5))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Has("runways", P.Outside(3, 5))
			},
			equals: "g.V().has(\"runways\",outside(3,5))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Has("runways", P.Inside(3, 5))
			},
			equals: "g.V().has(\"runways\",inside(3,5))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Has("runways", P.Inside(3, 5))
			},
			equals: "g.V().has(\"runways\",inside(3,5))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V("44").OutE().ElementMap()
			},
			equals: "g.V(\"44\").outE().elementMap()",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V("44").ValueMap().By(T__.Unfold())
			},
			equals: "g.V(\"44\").valueMap().by(__.unfold())",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V("44").ValueMap().With(WithOptions.Tokens, WithOptions.Labels)
			},
			equals: "g.V(\"44\").valueMap().with(\"~tinkerpop.valueMap.tokens\",2)",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V("44").ValueMap().With(WithOptions.All)
			},
			equals: "g.V(\"44\").valueMap().with(15)",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V("44").ValueMap().With(WithOptions.Indexer)
			},
			equals: "g.V(\"44\").valueMap().with(\"~tinkerpop.index.indexer\")",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V("44").ValueMap().With(WithOptions.Tokens)
			},
			equals: "g.V(\"44\").valueMap().with(\"~tinkerpop.valueMap.tokens\")",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.WithStrategies(ReadOnlyStrategy()).AddV("test")
			},
			equals: "g.withStrategies(ReadOnlyStrategy).addV(\"test\")",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.WithStrategies(SubgraphStrategy(SubgraphStrategyConfig{Vertices: T__.Has("region", "US-TX"), Edges: T__.HasLabel("route")})).V().Count()
			},
			containsRandomClassParams: true,
			equals:                    "g.withStrategies(new SubgraphStrategy(vertices:__.has(\"region\",\"US-TX\"),edges:__.hasLabel(\"route\"))).V().count()",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.WithStrategies(SubgraphStrategy(SubgraphStrategyConfig{VertexProperties: T__.HasNot("runways")})).V().Count()
			},
			equals: "g.withStrategies(new SubgraphStrategy(vertexProperties:__.hasNot(\"runways\"))).V().count()",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.WithStrategies(SubgraphStrategy(SubgraphStrategyConfig{Vertices: T__.Has("region", "US-TX"), VertexProperties: T__.HasNot("runways")})).V().Count()
			},
			containsRandomClassParams: true,
			equals:                    "g.withStrategies(new SubgraphStrategy(vertices:__.has(\"region\",\"US-TX\"),vertexProperties:__.hasNot(\"runways\"))).V().count()",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.WithStrategies(ReadOnlyStrategy(), SubgraphStrategy(SubgraphStrategyConfig{Vertices: T__.Has("region", "US-TX"), Edges: T__.HasLabel("route")})).V().Count()
			},
			containsRandomClassParams: true,
			equals:                    "g.withStrategies(ReadOnlyStrategy,new SubgraphStrategy(vertices:__.has(\"region\",\"US-TX\"),edges:__.hasLabel(\"route\"))).V().count()",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.WithStrategies(ReadOnlyStrategy(), SubgraphStrategy(SubgraphStrategyConfig{Vertices: T__.Has("region", "US-TX")})).V().Count()
			},
			equals: "g.withStrategies(ReadOnlyStrategy,new SubgraphStrategy(vertices:__.has(\"region\",\"US-TX\"))).V().count()",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.WithStrategies(OptionsStrategy(map[string]interface{}{"timeoutMillis": 500})).V().Count()
			},
			// OptionsStrategy are now extracted into request message and is no longer sent with the script
			equals: "g.V().count()",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.WithStrategies(PartitionStrategy(PartitionStrategyConfig{PartitionKey: "partition", WritePartition: "a", ReadPartitions: NewSimpleSet("a")})).AddV("test")
			},
			containsRandomClassParams: true,
			equals:                    "g.withStrategies(new PartitionStrategy(includeMetaProperties:false,partitionKey:\"partition\",writePartition:\"a\",readPartitions:[\"a\"])).addV(\"test\")",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.WithStrategies(VertexProgramStrategy(VertexProgramStrategyConfig{})).V().ShortestPath().With("~tinkerpop.shortestPath.target", T__.Has("name", "peter"))
			},
			equals: "g.withStrategies(VertexProgramStrategy).V().shortestPath().with(\"~tinkerpop.shortestPath.target\",__.has(\"name\",\"peter\"))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Has("p1", TextP.StartingWith("foo"))
			},
			equals: "g.V().has(\"p1\",startingWith(\"foo\"))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Has("p1", TextP.EndingWith("foo"))
			},
			equals: "g.V().has(\"p1\",endingWith(\"foo\"))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Has("p1", TextP.Containing("foo"))
			},
			equals: "g.V().has(\"p1\",containing(\"foo\"))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Has("p1", nil)
			},
			equals: "g.V().has(\"p1\",null)",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().HasLabel("person").Values("age").Is(P.Between(26, 30).Or(P.Gt(34)))
			},
			equals: "g.V().hasLabel(\"person\").values(\"age\").is(between(26,30).or(gt(34)))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.Inject(1).AsNumber(GType.Double)
			},
			equals: "g.inject(1).asNumber(GType.DOUBLE)",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.Inject(uuid.MustParse("f47af10b-58cc-4372-a567-0f02b2f3d479"))
			},
			equals: "g.inject(UUID(\"f47af10b-58cc-4372-a567-0f02b2f3d479\"))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Filter(T__.Has("name", TextP.StartingWith("m").Or(TextP.StartingWith("p"))))
			},
			equals: "g.V().filter(__.has(\"name\",startingWith(\"m\").or(startingWith(\"p\"))))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Has("person", "age", P.Within())
			},
			equals: "g.V().has(\"person\",\"age\",within())",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.Inject(math.NaN()).Is(P.Eq(math.NaN()))
			},
			equals: "g.inject(NaN).is(eq(NaN))",
		},
		{
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Has("name", "\"marko\n\r\t\b\f\"")
			},
			equals: "g.V().has(\"name\",\"\\\"marko\\n\\r\\t\\b\\f\\\"\")",
		},
		// Binary - []byte type (distinct from ByteBuffer)
		{
			name: "g_Inject_ByteSlice",
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.Inject([]byte{1, 2, 3})
			},
			equals: `g.inject(Binary("AQID"))`,
		},
		{
			name: "g_Inject_ByteSlice_Empty",
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.Inject([]byte{})
			},
			equals: `g.inject(Binary(""))`,
		},
		{
			name: "g_Inject_ByteSlice_Padding",
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.Inject([]byte{0})
			},
			equals: `g.inject(Binary("AA=="))`,
		},
		{
			name: "GTS Match with query string spawn step",
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.Match("MATCH (p:person)-[e:knows]->(friend:person)")
			},
			equals: "g.match(\"MATCH (p:person)-[e:knows]->(friend:person)\")",
		},
		{
			name: "GTS Match with query string and params spawn step",
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.Match("MATCH (p:person)-[e:knows]->(friend:person)", map[string]interface{}{"limit": 10})
			},
			equals: "g.match(\"MATCH (p:person)-[e:knows]->(friend:person)\",[\"limit\":10])",
		},
		{
			name: "GT Match mid-traversal string step",
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Match("MATCH (p:person)-[e:knows]->(friend:person)")
			},
			equals: "g.V().match(\"MATCH (p:person)-[e:knows]->(friend:person)\")",
		},
		{
			name: "GT Match mid-traversal string+params step",
			assert: func(g *GraphTraversalSource) *GraphTraversal {
				return g.V().Match("MATCH (p:person)", map[string]interface{}{"name": "marko"})
			},
			equals: "g.V().match(\"MATCH (p:person)\",[\"name\":\"marko\"])",
		},
	}

	var testsToRun []test

	onlyTests := make([]test, 0)
	for _, tt := range tests {
		if tt.only {
			onlyTests = append(onlyTests, tt)
		}
	}

	if len(onlyTests) > 0 {
		testsToRun = onlyTests
	} else {
		testsToRun = tests
	}

	for _, tt := range testsToRun {
		if tt.skip {
			continue
		}

		testName := tt.name
		if testName == "" {
			testName = tt.equals
		}

		t.Run(testName, func(t *testing.T) {
			g := NewGraphTraversalSource(nil, nil)
			gremlinLang := tt.assert(g).GremlinLang.GetGremlin()
			if !tt.containsRandomClassParams && gremlinLang != tt.equals {
				t.Errorf("GremlinLang = %v, equals %v", gremlinLang, tt.equals)
			}
			if tt.containsRandomClassParams {
				equalsParams := getArgs(tt.equals)
				gotParams := getArgs(gremlinLang)

				if len(equalsParams) != len(gotParams) {
					t.Errorf("GremlinLang = %v, equals %v", gremlinLang, tt.equals)
				}

				for _, equalsParam := range equalsParams {
					found := false
					for _, gotParam := range gotParams {
						if equalsParam == gotParam {
							found = true
							break
						}
					}
					if !found {
						t.Errorf("translator.Translate() = %v, equals %v", gremlinLang, tt.equals)
					}
				}
			}
		})
	}
}

func getArgs(result string) []string {
	pattern := `(\w+:.*?)`
	re := regexp.MustCompile(pattern)
	matches := re.FindAllStringSubmatch(result, -1)

	params := make([]string, 0)
	for _, match := range matches {
		if len(match) > 0 {
			params = append(params, match[0])
		}
	}

	return params
}

func Test_UnsupportedType(t *testing.T) {
	t.Run("should panic on unsupported type", func(t *testing.T) {
		defer func() {
			r := recover()
			if r == nil {
				t.Error("expected panic for unsupported type")
			}
			msg := fmt.Sprintf("%v", r)
			if !strings.Contains(msg, "cannot be represented as text") {
				t.Errorf("unexpected panic message: %v", msg)
			}
		}()
		gl := NewGremlinLang(nil)
		gl.argAsString(struct{}{})
	})

	t.Run("should panic on unsupported type in slice", func(t *testing.T) {
		defer func() {
			if r := recover(); r == nil {
				t.Error("expected panic for unsupported type in slice")
			}
		}()
		gl := NewGremlinLang(nil)
		gl.argAsString([]interface{}{1, struct{}{}})
	})
}

func Test_ConvertParametersToString(t *testing.T) {
	t.Run("should convert empty map", func(t *testing.T) {
		result := ConvertParametersToString(nil)
		if result != "[:]" {
			t.Errorf("expected [:], got %v", result)
		}
	})

	t.Run("should convert basic parameters", func(t *testing.T) {
		params := map[string]interface{}{"x": int32(1)}
		result := ConvertParametersToString(params)
		if !strings.Contains(result, "\"x\":1") {
			t.Errorf("expected parameter string to contain \"x\":1, got %v", result)
		}
	})

	t.Run("should panic on unsupported type in parameters", func(t *testing.T) {
		defer func() {
			if r := recover(); r == nil {
				t.Error("expected panic for unsupported type in parameters")
			}
		}()
		params := map[string]interface{}{"x": struct{}{}}
		ConvertParametersToString(params)
	})

	t.Run("should convert nested list parameter", func(t *testing.T) {
		params := map[string]interface{}{"ids": []interface{}{int32(1), int32(2), int32(3)}}
		result := ConvertParametersToString(params)
		if !strings.Contains(result, "\"ids\":[1,2,3]") {
			t.Errorf("expected nested list in result, got %v", result)
		}
	})

	t.Run("should panic on unsupported type in map value", func(t *testing.T) {
		defer func() {
			if r := recover(); r == nil {
				t.Error("expected panic for unsupported type in map value")
			}
		}()
		gl := NewGremlinLang(nil)
		m := map[interface{}]interface{}{"key": struct{}{}}
		gl.argAsString(m)
	})

	t.Run("should panic on unsupported type via RequestOptionsBuilder.Create()", func(t *testing.T) {
		defer func() {
			if r := recover(); r == nil {
				t.Error("expected panic for unsupported type in parameters")
			}
		}()
		new(RequestOptionsBuilder).SetParameters(map[string]interface{}{"x": struct{}{}}).Create()
	})
}

func Test_PDT_GremlinLang(t *testing.T) {
	t.Run("basic PDT", func(t *testing.T) {
		g := NewGraphTraversalSource(nil, nil)
		pdt := &CompositePDT{Name: "MyType", Fields: map[string]interface{}{"x": int32(1), "y": "hello"}}
		gremlin := g.Inject(pdt).GremlinLang.GetGremlin()
		expected := `g.inject(PDT("MyType",["x":1,"y":"hello"]))`
		if gremlin != expected {
			t.Errorf("got %v, expected %v", gremlin, expected)
		}
	})

	t.Run("empty PDT", func(t *testing.T) {
		g := NewGraphTraversalSource(nil, nil)
		pdt := &CompositePDT{Name: "Empty", Fields: map[string]interface{}{}}
		gremlin := g.Inject(pdt).GremlinLang.GetGremlin()
		expected := `g.inject(PDT("Empty",[:]))`
		if gremlin != expected {
			t.Errorf("got %v, expected %v", gremlin, expected)
		}
	})

	t.Run("PDT with special characters in name", func(t *testing.T) {
		g := NewGraphTraversalSource(nil, nil)
		pdt := &CompositePDT{Name: `say"hello"`, Fields: map[string]interface{}{"v": int32(1)}}
		gremlin := g.Inject(pdt).GremlinLang.GetGremlin()
		expected := `g.inject(PDT("say\"hello\"",["v":1]))`
		if gremlin != expected {
			t.Errorf("got %v, expected %v", gremlin, expected)
		}
	})

	t.Run("PDT with backslash in name", func(t *testing.T) {
		g := NewGraphTraversalSource(nil, nil)
		pdt := &CompositePDT{Name: `back\slash`, Fields: map[string]interface{}{"v": int32(1)}}
		gremlin := g.Inject(pdt).GremlinLang.GetGremlin()
		expected := `g.inject(PDT("back\\slash",["v":1]))`
		if gremlin != expected {
			t.Errorf("got %v, expected %v", gremlin, expected)
		}
	})

	t.Run("nested PDT", func(t *testing.T) {
		g := NewGraphTraversalSource(nil, nil)
		inner := &CompositePDT{Name: "Inner", Fields: map[string]interface{}{"v": int32(1)}}
		outer := &CompositePDT{Name: "Outer", Fields: map[string]interface{}{"inner": inner}}
		gremlin := g.Inject(outer).GremlinLang.GetGremlin()
		expected := `g.inject(PDT("Outer",["inner":PDT("Inner",["v":1])]))`
		if gremlin != expected {
			t.Errorf("got %v, expected %v", gremlin, expected)
		}
	})

	t.Run("PDT map keys sorted", func(t *testing.T) {
		g := NewGraphTraversalSource(nil, nil)
		pdt := &CompositePDT{Name: "T", Fields: map[string]interface{}{"z": int32(3), "a": int32(1), "m": int32(2)}}
		gremlin := g.Inject(pdt).GremlinLang.GetGremlin()
		expected := `g.inject(PDT("T",["a":1,"m":2,"z":3]))`
		if gremlin != expected {
			t.Errorf("got %v, expected %v", gremlin, expected)
		}
	})
}

// adapterPrecedencePoint is a struct type used to verify that a registered adapter
// takes precedence over the default handling (which would panic for an unknown struct).
type adapterPrecedencePoint struct {
	X int32
	Y int32
}

func Test_PDT_AdapterPrecedenceOverDefault(t *testing.T) {
	registry := NewPDTRegistry()
	registry.RegisterFuncsWithType("test:Point", reflect.TypeOf(adapterPrecedencePoint{}),
		nil,
		func(obj interface{}) (map[string]interface{}, error) {
			p := obj.(adapterPrecedencePoint)
			return map[string]interface{}{"x": p.X, "y": p.Y}, nil
		})

	g := NewGraphTraversalSource(nil, nil)
	g.GetGremlinLang().pdtRegistry = registry

	gremlin := g.Inject(adapterPrecedencePoint{X: 7, Y: 9}).GremlinLang.GetGremlin()
	expected := `g.inject(PDT("test:Point",["x":7,"y":9]))`
	if gremlin != expected {
		t.Errorf("adapter precedence: got %v, expected %v", gremlin, expected)
	}
}

// TestPDT_GremlinLang_NestedRegisteredInUnregisteredOuter asserts that a registered domain
// object nested inside an unregistered outer *CompositePDT is dehydrated to PDT form.
func TestPDT_GremlinLang_NestedRegisteredInUnregisteredOuter(t *testing.T) {
	registry := NewPDTRegistry()
	registry.RegisterFuncsWithType("test:Point", reflect.TypeOf(adapterPrecedencePoint{}),
		nil,
		func(obj interface{}) (map[string]interface{}, error) {
			p := obj.(adapterPrecedencePoint)
			return map[string]interface{}{"x": p.X, "y": p.Y}, nil
		})

	g := NewGraphTraversalSource(nil, nil)
	g.GetGremlinLang().pdtRegistry = registry

	// Outer is an unregistered raw PDT; its "pt" field is a registered Go domain object.
	outer := &CompositePDT{
		Name:   "unregistered:Wrapper",
		Fields: map[string]interface{}{"pt": adapterPrecedencePoint{X: 3, Y: 4}, "tag": "hello"},
	}

	gremlin := g.Inject(outer).GremlinLang.GetGremlin()
	expected := `g.inject(PDT("unregistered:Wrapper",["pt":PDT("test:Point",["x":3,"y":4]),"tag":"hello"]))`
	if gremlin != expected {
		t.Errorf("nested dehydration: got %v, expected %v", gremlin, expected)
	}
}

func Test_PrimitivePDT_GremlinLang(t *testing.T) {
	t.Run("basic primitive PDT", func(t *testing.T) {
		g := NewGraphTraversalSource(nil, nil)
		pdt := &PrimitivePDT{Name: "x:Uint32", Value: "42"}
		gremlin := g.Inject(pdt).GremlinLang.GetGremlin()
		expected := `g.inject(PDT("x:Uint32","42"))`
		if gremlin != expected {
			t.Errorf("got %v, expected %v", gremlin, expected)
		}
	})

	t.Run("primitive PDT with special chars in value", func(t *testing.T) {
		g := NewGraphTraversalSource(nil, nil)
		pdt := &PrimitivePDT{Name: "x:Label", Value: `say"hello"`}
		gremlin := g.Inject(pdt).GremlinLang.GetGremlin()
		expected := `g.inject(PDT("x:Label","say\"hello\""))`
		if gremlin != expected {
			t.Errorf("got %v, expected %v", gremlin, expected)
		}
	})

	t.Run("primitive PDT leading zeros preserved", func(t *testing.T) {
		g := NewGraphTraversalSource(nil, nil)
		pdt := &PrimitivePDT{Name: "x:ZipCode", Value: "00123"}
		gremlin := g.Inject(pdt).GremlinLang.GetGremlin()
		expected := `g.inject(PDT("x:ZipCode","00123"))`
		if gremlin != expected {
			t.Errorf("got %v, expected %v", gremlin, expected)
		}
	})

	t.Run("primitive PDT empty value", func(t *testing.T) {
		g := NewGraphTraversalSource(nil, nil)
		pdt := &PrimitivePDT{Name: "x:Empty", Value: ""}
		gremlin := g.Inject(pdt).GremlinLang.GetGremlin()
		expected := `g.inject(PDT("x:Empty",""))`
		if gremlin != expected {
			t.Errorf("got %v, expected %v", gremlin, expected)
		}
	})
}

// primitiveAdapterUint32 is a test type for primitive PDT dehydration.
type primitiveAdapterUint32 uint32

func Test_PrimitivePDT_AdapterDehydration(t *testing.T) {
	registry := NewPDTRegistry()
	registry.RegisterPrimitiveFuncsWithType("x:Uint32", reflect.TypeOf(primitiveAdapterUint32(0)),
		func(s string) (interface{}, error) {
			return primitiveAdapterUint32(42), nil
		},
		func(obj interface{}) (string, error) {
			return fmt.Sprintf("%d", obj.(primitiveAdapterUint32)), nil
		})

	g := NewGraphTraversalSource(nil, nil)
	g.GetGremlinLang().pdtRegistry = registry

	gremlin := g.Inject(primitiveAdapterUint32(99)).GremlinLang.GetGremlin()
	expected := `g.inject(PDT("x:Uint32","99"))`
	if gremlin != expected {
		t.Errorf("primitive adapter dehydration: got %v, expected %v", gremlin, expected)
	}
}

func Test_PrimitivePDT_PrimitiveAdapterTakesPrecedenceOverComposite(t *testing.T) {
	type dualType struct{ V int }
	registry := NewPDTRegistry()
	// Register both primitive and composite for the same Go type — primitive must win.
	registry.RegisterPrimitiveFuncsWithType("x:Dual", reflect.TypeOf(dualType{}),
		func(s string) (interface{}, error) { return dualType{V: 1}, nil },
		func(obj interface{}) (string, error) { return "prim", nil })
	registry.RegisterFuncsWithType("x:Dual", reflect.TypeOf(dualType{}),
		nil,
		func(obj interface{}) (map[string]interface{}, error) { return map[string]interface{}{"v": 1}, nil })

	g := NewGraphTraversalSource(nil, nil)
	g.GetGremlinLang().pdtRegistry = registry

	gremlin := g.Inject(dualType{V: 1}).GremlinLang.GetGremlin()
	expected := `g.inject(PDT("x:Dual","prim"))`
	if gremlin != expected {
		t.Errorf("primitive adapter should take precedence over composite: got %v, expected %v", gremlin, expected)
	}
}
