package cassandra

import (
	"testing"

	"github.com/scylladb/gocqlx/v2/qb"
	"github.com/stretchr/testify/assert"
)

func TestEq(t *testing.T) {
	tests := []struct {
		name     string
		column   string
		value    any
		validate func(*testing.T, Condition)
	}{
		{
			name:   "string value",
			column: "name",
			value:  "Alice",
			validate: func(t *testing.T, cond Condition) {
				cmp, binds := cond.Build()
				assert.NotNil(t, cmp)
				assert.Equal(t, "Alice", binds["name"])
			},
		},
		{
			name:   "int value",
			column: "age",
			value:  25,
			validate: func(t *testing.T, cond Condition) {
				cmp, binds := cond.Build()
				assert.NotNil(t, cmp)
				assert.Equal(t, 25, binds["age"])
			},
		},
		{
			name:   "nil value",
			column: "description",
			value:  nil,
			validate: func(t *testing.T, cond Condition) {
				cmp, binds := cond.Build()
				assert.NotNil(t, cmp)
				assert.Nil(t, binds["description"])
			},
		},
		{
			name:   "empty string",
			column: "email",
			value:  "",
			validate: func(t *testing.T, cond Condition) {
				cmp, binds := cond.Build()
				assert.NotNil(t, cmp)
				assert.Equal(t, "", binds["email"])
			},
		},
		{
			name:   "boolean value",
			column: "active",
			value:  true,
			validate: func(t *testing.T, cond Condition) {
				cmp, binds := cond.Build()
				assert.NotNil(t, cmp)
				assert.Equal(t, true, binds["active"])
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cond := Eq(tt.column, tt.value)
			assert.NotNil(t, cond)
			tt.validate(t, cond)
		})
	}
}

func TestIn(t *testing.T) {
	tests := []struct {
		name     string
		column   string
		values   []any
		validate func(*testing.T, Condition)
	}{
		{
			name:   "string values",
			column: "status",
			values: []any{"active", "pending", "completed"},
			validate: func(t *testing.T, cond Condition) {
				cmp, binds := cond.Build()
				assert.NotNil(t, cmp)
				assert.Equal(t, []any{"active", "pending", "completed"}, binds["status"])
			},
		},
		{
			name:   "int values",
			column: "ids",
			values: []any{1, 2, 3, 4, 5},
			validate: func(t *testing.T, cond Condition) {
				cmp, binds := cond.Build()
				assert.NotNil(t, cmp)
				assert.Equal(t, []any{1, 2, 3, 4, 5}, binds["ids"])
			},
		},
		{
			name:   "empty slice",
			column: "tags",
			values: []any{},
			validate: func(t *testing.T, cond Condition) {
				cmp, binds := cond.Build()
				assert.NotNil(t, cmp)
				assert.Equal(t, []any{}, binds["tags"])
			},
		},
		{
			name:   "single value",
			column: "id",
			values: []any{1},
			validate: func(t *testing.T, cond Condition) {
				cmp, binds := cond.Build()
				assert.NotNil(t, cmp)
				assert.Equal(t, []any{1}, binds["id"])
			},
		},
		{
			name:   "mixed types",
			column: "values",
			values: []any{"string", 123, true},
			validate: func(t *testing.T, cond Condition) {
				cmp, binds := cond.Build()
				assert.NotNil(t, cmp)
				assert.Equal(t, []any{"string", 123, true}, binds["values"])
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cond := In(tt.column, tt.values)
			assert.NotNil(t, cond)
			tt.validate(t, cond)
		})
	}
}

func TestGt(t *testing.T) {
	tests := []struct {
		name     string
		column   string
		value    any
		validate func(*testing.T, Condition)
	}{
		{
			name:   "int value",
			column: "age",
			value:  18,
			validate: func(t *testing.T, cond Condition) {
				cmp, binds := cond.Build()
				assert.NotNil(t, cmp)
				assert.Equal(t, 18, binds["age"])
			},
		},
		{
			name:   "float value",
			column: "price",
			value:  99.99,
			validate: func(t *testing.T, cond Condition) {
				cmp, binds := cond.Build()
				assert.NotNil(t, cmp)
				assert.Equal(t, 99.99, binds["price"])
			},
		},
		{
			name:   "zero value",
			column: "count",
			value:  0,
			validate: func(t *testing.T, cond Condition) {
				cmp, binds := cond.Build()
				assert.NotNil(t, cmp)
				assert.Equal(t, 0, binds["count"])
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cond := Gt(tt.column, tt.value)
			assert.NotNil(t, cond)
			tt.validate(t, cond)
		})
	}
}

func TestLt(t *testing.T) {
	tests := []struct {
		name     string
		column   string
		value    any
		validate func(*testing.T, Condition)
	}{
		{
			name:   "int value",
			column: "age",
			value:  65,
			validate: func(t *testing.T, cond Condition) {
				cmp, binds := cond.Build()
				assert.NotNil(t, cmp)
				assert.Equal(t, 65, binds["age"])
			},
		},
		{
			name:   "float value",
			column: "price",
			value:  199.99,
			validate: func(t *testing.T, cond Condition) {
				cmp, binds := cond.Build()
				assert.NotNil(t, cmp)
				assert.Equal(t, 199.99, binds["price"])
			},
		},
		{
			name:   "negative value",
			column: "balance",
			value:  -100,
			validate: func(t *testing.T, cond Condition) {
				cmp, binds := cond.Build()
				assert.NotNil(t, cmp)
				assert.Equal(t, -100, binds["balance"])
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cond := Lt(tt.column, tt.value)
			assert.NotNil(t, cond)
			tt.validate(t, cond)
		})
	}
}

func TestCondition_Build(t *testing.T) {
	tests := []struct {
		name     string
		cond     Condition
		validate func(*testing.T, qb.Cmp, map[string]any)
	}{
		{
			name: "Eq condition",
			cond: Eq("name", "test"),
			validate: func(t *testing.T, cmp qb.Cmp, binds map[string]any) {
				assert.NotNil(t, cmp)
				assert.Equal(t, "test", binds["name"])
			},
		},
		{
			name: "In condition",
			cond: In("ids", []any{1, 2, 3}),
			validate: func(t *testing.T, cmp qb.Cmp, binds map[string]any) {
				assert.NotNil(t, cmp)
				assert.Equal(t, []any{1, 2, 3}, binds["ids"])
			},
		},
		{
			name: "Gt condition",
			cond: Gt("age", 18),
			validate: func(t *testing.T, cmp qb.Cmp, binds map[string]any) {
				assert.NotNil(t, cmp)
				assert.Equal(t, 18, binds["age"])
			},
		},
		{
			name: "Lt condition",
			cond: Lt("price", 100),
			validate: func(t *testing.T, cmp qb.Cmp, binds map[string]any) {
				assert.NotNil(t, cmp)
				assert.Equal(t, 100, binds["price"])
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			cmp, binds := tt.cond.Build()
			tt.validate(t, cmp, binds)
		})
	}
}

func TestQueryBuilder_Where(t *testing.T) {
	tests := []struct {
		name      string
		condition Condition
		validate  func(*testing.T, *queryBuilder[testUser])
	}{
		{
			name:      "single condition",
			condition: Eq("name", "Alice"),
			validate: func(t *testing.T, qb *queryBuilder[testUser]) {
				assert.Len(t, qb.conditions, 1)
			},
		},
		{
			name:      "multiple conditions",
			condition: In("status", []any{"active", "pending"}),
			validate: func(t *testing.T, qb *queryBuilder[testUser]) {
				// 添加多個條件
				cond := In("status", []any{"active", "pending"})
				qb.Where(Eq("name", "test"))
				qb.Where(cond)
				assert.Len(t, qb.conditions, 2)
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// 注意：這需要一個有效的 repository，但我們可以測試鏈式調用
			// 實際的執行需要資料庫連接
			_ = tt
		})
	}
}

func TestQueryBuilder_OrderBy(t *testing.T) {
	tests := []struct {
		name     string
		column   string
		order    Order
		validate func(*testing.T, *queryBuilder[testUser])
	}{
		{
			name:   "ASC order",
			column: "created_at",
			order:  ASC,
			validate: func(t *testing.T, qb *queryBuilder[testUser]) {
				assert.Len(t, qb.orders, 1)
				assert.Equal(t, "created_at", qb.orders[0].column)
				assert.Equal(t, ASC, qb.orders[0].order)
			},
		},
		{
			name:   "DESC order",
			column: "updated_at",
			order:  DESC,
			validate: func(t *testing.T, qb *queryBuilder[testUser]) {
				assert.Len(t, qb.orders, 1)
				assert.Equal(t, "updated_at", qb.orders[0].column)
				assert.Equal(t, DESC, qb.orders[0].order)
			},
		},
		{
			name:   "multiple orders",
			column: "name",
			order:  ASC,
			validate: func(t *testing.T, qb *queryBuilder[testUser]) {
				qb.OrderBy("created_at", DESC)
				qb.OrderBy("name", ASC)
				assert.Len(t, qb.orders, 2)
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// 注意：這需要一個有效的 repository
			_ = tt
		})
	}
}

func TestQueryBuilder_Limit(t *testing.T) {
	tests := []struct {
		name     string
		limit    int
		expected int
	}{
		{
			name:     "positive limit",
			limit:    10,
			expected: 10,
		},
		{
			name:     "zero limit",
			limit:    0,
			expected: 0,
		},
		{
			name:     "large limit",
			limit:    1000,
			expected: 1000,
		},
		{
			name:     "negative limit",
			limit:    -1,
			expected: -1,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// 注意：這需要一個有效的 repository
			_ = tt
		})
	}
}

func TestQueryBuilder_Select(t *testing.T) {
	tests := []struct {
		name     string
		columns  []string
		expected int
	}{
		{
			name:     "single column",
			columns:  []string{"name"},
			expected: 1,
		},
		{
			name:     "multiple columns",
			columns:  []string{"name", "email", "age"},
			expected: 3,
		},
		{
			name:     "empty columns",
			columns:  []string{},
			expected: 0,
		},
		{
			name:     "duplicate columns",
			columns:  []string{"name", "name"},
			expected: 2,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// 注意：這需要一個有效的 repository
			_ = tt
		})
	}
}

func TestQueryBuilder_Chaining(t *testing.T) {
	t.Run("chain multiple methods", func(t *testing.T) {
		// 注意：這需要一個有效的 repository
		// 實際的執行需要資料庫連接
		// 這裡只是展示測試結構
	})
}

func TestQueryBuilder_Scan_ErrorCases(t *testing.T) {
	tests := []struct {
		name        string
		description string
	}{
		{
			name:        "nil destination",
			description: "should return error when destination is nil",
		},
		{
			name:        "invalid query",
			description: "should return error when query is invalid",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// 注意：這需要 mock session 或實際的資料庫連接
			_ = tt
		})
	}
}

func TestQueryBuilder_One_ErrorCases(t *testing.T) {
	tests := []struct {
		name        string
		description string
	}{
		{
			name:        "no results",
			description: "should return ErrNotFound when no results found",
		},
		{
			name:        "query error",
			description: "should return error when query fails",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// 注意：這需要 mock session 或實際的資料庫連接
			_ = tt
		})
	}
}

func TestQueryBuilder_Count_ErrorCases(t *testing.T) {
	tests := []struct {
		name        string
		description string
	}{
		{
			name:        "query error",
			description: "should return error when query fails",
		},
		{
			name:        "ErrNotFound should return 0",
			description: "should return 0 when ErrNotFound",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// 注意：這需要 mock session 或實際的資料庫連接
			_ = tt
		})
	}
}

