package orm

import "testing"

func TestParseDomainStringSimple(t *testing.T) {
	domain, err := ParseDomainString("[('name', '=', 'test')]", nil)
	if err != nil {
		t.Fatal(err)
	}
	if len(domain) != 1 {
		t.Fatalf("expected 1 node, got %d", len(domain))
	}
	cond, ok := domain[0].(Condition)
	if !ok {
		t.Fatal("expected Condition")
	}
	if cond.Field != "name" {
		t.Errorf("field: %s", cond.Field)
	}
	if cond.Operator != "=" {
		t.Errorf("op: %s", cond.Operator)
	}
	if cond.Value != "test" {
		t.Errorf("value: %v", cond.Value)
	}
}

func TestParseDomainStringNumeric(t *testing.T) {
	domain, err := ParseDomainString("[('age', '>', 18)]", nil)
	if err != nil {
		t.Fatal(err)
	}
	cond := domain[0].(Condition)
	if cond.Value != int64(18) {
		t.Errorf("expected int64(18), got %T %v", cond.Value, cond.Value)
	}
}

func TestParseDomainStringFloat(t *testing.T) {
	domain, err := ParseDomainString("[('amount', '>', 99.5)]", nil)
	if err != nil {
		t.Fatal(err)
	}
	cond := domain[0].(Condition)
	if cond.Value != float64(99.5) {
		t.Errorf("expected float64(99.5), got %T %v", cond.Value, cond.Value)
	}
}

func TestParseDomainStringNegativeNumber(t *testing.T) {
	domain, err := ParseDomainString("[('balance', '<', -100)]", nil)
	if err != nil {
		t.Fatal(err)
	}
	cond := domain[0].(Condition)
	if cond.Value != int64(-100) {
		t.Errorf("expected int64(-100), got %T %v", cond.Value, cond.Value)
	}
}

func TestParseDomainStringBoolean(t *testing.T) {
	domain, err := ParseDomainString("[('active', '=', True)]", nil)
	if err != nil {
		t.Fatal(err)
	}
	cond := domain[0].(Condition)
	if cond.Value != true {
		t.Errorf("expected true, got %v", cond.Value)
	}
}

func TestParseDomainStringBooleanFalse(t *testing.T) {
	domain, err := ParseDomainString("[('active', '=', False)]", nil)
	if err != nil {
		t.Fatal(err)
	}
	cond := domain[0].(Condition)
	if cond.Value != false {
		t.Errorf("expected false, got %v", cond.Value)
	}
}

func TestParseDomainStringList(t *testing.T) {
	domain, err := ParseDomainString("[('id', 'in', [1, 2, 3])]", nil)
	if err != nil {
		t.Fatal(err)
	}
	cond := domain[0].(Condition)
	vals, ok := cond.Value.([]int64)
	if !ok {
		t.Fatalf("expected []int64, got %T", cond.Value)
	}
	if len(vals) != 3 {
		t.Errorf("expected 3, got %d", len(vals))
	}
	if vals[0] != 1 || vals[1] != 2 || vals[2] != 3 {
		t.Errorf("values: %v", vals)
	}
}

func TestParseDomainStringStringList(t *testing.T) {
	domain, err := ParseDomainString("[('state', 'in', ['draft', 'sent'])]", nil)
	if err != nil {
		t.Fatal(err)
	}
	cond := domain[0].(Condition)
	vals, ok := cond.Value.([]string)
	if !ok {
		t.Fatalf("expected []string, got %T", cond.Value)
	}
	if len(vals) != 2 {
		t.Errorf("expected 2, got %d", len(vals))
	}
}

func TestParseDomainStringEmptyList(t *testing.T) {
	domain, err := ParseDomainString("[('id', 'in', [])]", nil)
	if err != nil {
		t.Fatal(err)
	}
	cond := domain[0].(Condition)
	vals, ok := cond.Value.([]int64)
	if !ok {
		t.Fatalf("expected []int64, got %T", cond.Value)
	}
	if len(vals) != 0 {
		t.Errorf("expected 0, got %d", len(vals))
	}
}

func TestParseDomainStringOperators(t *testing.T) {
	domain, err := ParseDomainString("['&', ('a', '=', 1), ('b', '=', 2)]", nil)
	if err != nil {
		t.Fatal(err)
	}
	if len(domain) != 3 {
		t.Fatalf("expected 3 nodes, got %d", len(domain))
	}
	if domain[0] != OpAnd {
		t.Error("expected & operator")
	}
}

func TestParseDomainStringOrOperator(t *testing.T) {
	domain, err := ParseDomainString("['|', ('a', '=', 1), ('b', '=', 2)]", nil)
	if err != nil {
		t.Fatal(err)
	}
	if len(domain) != 3 {
		t.Fatalf("expected 3 nodes, got %d", len(domain))
	}
	if domain[0] != OpOr {
		t.Error("expected | operator")
	}
}

func TestParseDomainStringNotOperator(t *testing.T) {
	domain, err := ParseDomainString("['!', ('active', '=', True)]", nil)
	if err != nil {
		t.Fatal(err)
	}
	if len(domain) != 2 {
		t.Fatalf("expected 2 nodes, got %d", len(domain))
	}
	if domain[0] != OpNot {
		t.Error("expected ! operator")
	}
}

func TestParseDomainStringEmpty(t *testing.T) {
	domain, err := ParseDomainString("[]", nil)
	if err != nil {
		t.Fatal(err)
	}
	if len(domain) != 0 {
		t.Errorf("expected 0 nodes, got %d", len(domain))
	}
}

func TestParseDomainStringEmptyString(t *testing.T) {
	domain, err := ParseDomainString("", nil)
	if err != nil {
		t.Fatal(err)
	}
	if len(domain) != 0 {
		t.Errorf("expected 0 nodes, got %d", len(domain))
	}
}

func TestParseDomainStringNone(t *testing.T) {
	domain, err := ParseDomainString("[('field', '=', None)]", nil)
	if err != nil {
		t.Fatal(err)
	}
	cond := domain[0].(Condition)
	if cond.Value != nil {
		t.Errorf("expected nil, got %v", cond.Value)
	}
}

func TestParseDomainStringImplicitAnd(t *testing.T) {
	// Multiple leaves without explicit operator should be implicitly ANDed
	domain, err := ParseDomainString("[('a', '=', 1), ('b', '=', 2)]", nil)
	if err != nil {
		t.Fatal(err)
	}
	// normalizeDomainNodes wraps with And() → [&, leaf, leaf] = 3 nodes
	if len(domain) != 3 {
		t.Fatalf("expected 3 nodes (implicit AND), got %d", len(domain))
	}
	if domain[0] != OpAnd {
		t.Error("expected implicit & operator")
	}
}

func TestParseDomainStringDoubleQuotes(t *testing.T) {
	domain, err := ParseDomainString(`[("name", "=", "test")]`, nil)
	if err != nil {
		t.Fatal(err)
	}
	if len(domain) != 1 {
		t.Fatalf("expected 1 node, got %d", len(domain))
	}
	cond := domain[0].(Condition)
	if cond.Field != "name" {
		t.Errorf("field: %s", cond.Field)
	}
	if cond.Value != "test" {
		t.Errorf("value: %v", cond.Value)
	}
}

func TestParseDomainStringContextVar(t *testing.T) {
	// Without env, context vars should resolve to int64(0)
	domain, err := ParseDomainString("[('user_id', '=', user.id)]", nil)
	if err != nil {
		t.Fatal(err)
	}
	cond := domain[0].(Condition)
	if cond.Value != int64(0) {
		t.Errorf("expected int64(0), got %T %v", cond.Value, cond.Value)
	}
}

func TestParseDomainStringInvalidSyntax(t *testing.T) {
	_, err := ParseDomainString("not a domain", nil)
	if err == nil {
		t.Error("expected error for invalid syntax")
	}
}

func TestParseDomainStringTupleAsList(t *testing.T) {
	// Some domain_force uses tuple syntax for list values
	domain, err := ParseDomainString("[('id', 'in', (1, 2, 3))]", nil)
	if err != nil {
		t.Fatal(err)
	}
	cond := domain[0].(Condition)
	vals, ok := cond.Value.([]int64)
	if !ok {
		t.Fatalf("expected []int64, got %T", cond.Value)
	}
	if len(vals) != 3 {
		t.Errorf("expected 3, got %d", len(vals))
	}
}

func TestParseDomainStringEscapedQuote(t *testing.T) {
	domain, err := ParseDomainString(`[('name', '=', 'it\'s')]`, nil)
	if err != nil {
		t.Fatal(err)
	}
	cond := domain[0].(Condition)
	if cond.Value != "it's" {
		t.Errorf("expected it's, got %v", cond.Value)
	}
}
