package cassandra

import (
	"reflect"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestContains(t *testing.T) {
	tests := []struct {
		name   string
		list   []string
		target string
		want   bool
	}{
		{
			name:   "target exists in list",
			list:   []string{"a", "b", "c"},
			target: "b",
			want:   true,
		},
		{
			name:   "target at beginning",
			list:   []string{"a", "b", "c"},
			target: "a",
			want:   true,
		},
		{
			name:   "target at end",
			list:   []string{"a", "b", "c"},
			target: "c",
			want:   true,
		},
		{
			name:   "target not in list",
			list:   []string{"a", "b", "c"},
			target: "d",
			want:   false,
		},
		{
			name:   "empty list",
			list:   []string{},
			target: "a",
			want:   false,
		},
		{
			name:   "empty target",
			list:   []string{"a", "b", "c"},
			target: "",
			want:   false,
		},
		{
			name:   "target in single element list",
			list:   []string{"a"},
			target: "a",
			want:   true,
		},
		{
			name:   "case sensitive",
			list:   []string{"A", "B", "C"},
			target: "a",
			want:   false,
		},
		{
			name:   "duplicate values",
			list:   []string{"a", "b", "a", "c"},
			target: "a",
			want:   true,
		},
		{
			name:   "long list",
			list:   []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"},
			target: "j",
			want:   true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := contains(tt.list, tt.target)
			assert.Equal(t, tt.want, result)
		})
	}
}

func TestIsZero(t *testing.T) {
	tests := []struct {
		name     string
		value    any
		expected bool
		skip     bool
	}{
		{
			name:     "nil pointer",
			value:    (*string)(nil),
			expected: true,
			skip:     false,
		},
		{
			name:     "non-nil pointer",
			value:    stringPtr("test"),
			expected: false,
			skip:     false,
		},
		{
			name:     "nil slice",
			value:    []string(nil),
			expected: true,
			skip:     false,
		},
		{
			name:     "empty slice",
			value:    []string{},
			expected: false, // 空 slice 不是 nil
			skip:     false,
		},
		{
			name:     "nil map",
			value:    map[string]int(nil),
			expected: true,
			skip:     false,
		},
		{
			name:     "empty map",
			value:    map[string]int{},
			expected: false, // 空 map 不是 nil
			skip:     false,
		},
		{
			name:     "zero int",
			value:    0,
			expected: true,
			skip:     false,
		},
		{
			name:     "non-zero int",
			value:    42,
			expected: false,
			skip:     false,
		},
		{
			name:     "zero int64",
			value:    int64(0),
			expected: true,
			skip:     false,
		},
		{
			name:     "non-zero int64",
			value:    int64(42),
			expected: false,
			skip:     false,
		},
		{
			name:     "zero float64",
			value:    0.0,
			expected: true,
			skip:     false,
		},
		{
			name:     "non-zero float64",
			value:    3.14,
			expected: false,
			skip:     false,
		},
		{
			name:     "empty string",
			value:    "",
			expected: true,
			skip:     false,
		},
		{
			name:     "non-empty string",
			value:    "test",
			expected: false,
			skip:     false,
		},
		{
			name:     "false bool",
			value:    false,
			expected: true,
			skip:     false,
		},
		{
			name:     "true bool",
			value:    true,
			expected: false,
			skip:     false,
		},
		{
			name:     "struct with zero values",
			value:    testUser{},
			expected: true, // 所有欄位都是零值，應該返回 true
			skip:     false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			if tt.skip {
				t.Skip("Skipping test")
				return
			}
			// 使用 reflect.ValueOf 來獲取 reflect.Value
			v := reflect.ValueOf(tt.value)
			// 檢查是否為零值（nil interface 會導致 zero Value）
			if !v.IsValid() {
				// 對於 nil interface，直接返回 true
				assert.True(t, tt.expected)
				return
			}
			result := isZero(v)
			assert.Equal(t, tt.expected, result)
		})
	}
}

func TestNewRepository(t *testing.T) {
	tests := []struct {
		name     string
		keyspace string
		wantErr  bool
		validate func(*testing.T, Repository[testUser], *DB)
	}{
		{
			name:     "valid keyspace",
			keyspace: "test_keyspace",
			wantErr:  false,
			validate: func(t *testing.T, repo Repository[testUser], db *DB) {
				assert.NotNil(t, repo)
			},
		},
		{
			name:     "empty keyspace uses default",
			keyspace: "",
			wantErr:  false,
			validate: func(t *testing.T, repo Repository[testUser], db *DB) {
				assert.NotNil(t, repo)
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// 注意：這需要一個有效的 DB 實例
			// 在實際測試中，需要使用 mock 或 testcontainers
			_ = tt
		})
	}
}

func TestRepository_Insert(t *testing.T) {
	tests := []struct {
		name        string
		description string
	}{
		{
			name:        "successful insert",
			description: "should insert document successfully",
		},
		{
			name:        "duplicate key",
			description: "should return error on duplicate key",
		},
		{
			name:        "invalid document",
			description: "should return error for invalid document",
		},
	}

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

func TestRepository_Get(t *testing.T) {
	tests := []struct {
		name        string
		pk          any
		description string
		wantErr     bool
	}{
		{
			name:        "found with string key",
			pk:          "test-id",
			description: "should return document when found",
			wantErr:     false,
		},
		{
			name:        "not found",
			pk:          "non-existent",
			description: "should return ErrNotFound when not found",
			wantErr:     true,
		},
		{
			name:        "invalid primary key structure",
			pk:          "single-key",
			description: "should return error for invalid key structure",
			wantErr:     true,
		},
		{
			name:        "struct primary key",
			pk:          testUser{ID: "test-id"},
			description: "should work with struct primary key",
			wantErr:     false,
		},
	}

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

func TestRepository_Update(t *testing.T) {
	tests := []struct {
		name        string
		description string
		wantErr     bool
	}{
		{
			name:        "successful update",
			description: "should update document successfully",
			wantErr:     false,
		},
		{
			name:        "not found",
			description: "should return error when document not found",
			wantErr:     true,
		},
		{
			name:        "no fields to update",
			description: "should return error when no fields to update",
			wantErr:     true,
		},
	}

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

func TestRepository_Delete(t *testing.T) {
	tests := []struct {
		name        string
		pk          any
		description string
		wantErr     bool
	}{
		{
			name:        "successful delete",
			pk:          "test-id",
			description: "should delete document successfully",
			wantErr:     false,
		},
		{
			name:        "not found",
			pk:          "non-existent",
			description: "should not return error when not found (idempotent)",
			wantErr:     false,
		},
	}

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

func TestRepository_InsertMany(t *testing.T) {
	tests := []struct {
		name        string
		docs        []testUser
		description string
		wantErr     bool
	}{
		{
			name:        "empty slice",
			docs:        []testUser{},
			description: "should return nil for empty slice",
			wantErr:     false,
		},
		{
			name:        "single document",
			docs:        []testUser{{ID: "1", Name: "Alice"}},
			description: "should insert single document",
			wantErr:     false,
		},
		{
			name:        "multiple documents",
			docs:        []testUser{{ID: "1", Name: "Alice"}, {ID: "2", Name: "Bob"}},
			description: "should insert multiple documents",
			wantErr:     false,
		},
		{
			name:        "large batch",
			docs:        make([]testUser, 100),
			description: "should handle large batch",
			wantErr:     false,
		},
	}

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

func TestRepository_Query(t *testing.T) {
	t.Run("should return QueryBuilder", func(t *testing.T) {
		// 注意：這需要一個有效的 repository
		// 實際的執行需要資料庫連接
	})
}

func TestBuildUpdateStatement(t *testing.T) {
	tests := []struct {
		name      string
		setCols   []string
		whereCols []string
		table     string
		validate  func(*testing.T, string, []string)
	}{
		{
			name:      "single set column, single where column",
			setCols:   []string{"name"},
			whereCols: []string{"id"},
			table:     "users",
			validate: func(t *testing.T, stmt string, names []string) {
				assert.Contains(t, stmt, "UPDATE")
				assert.Contains(t, stmt, "users")
				assert.Contains(t, stmt, "SET")
				assert.Contains(t, stmt, "WHERE")
				assert.Len(t, names, 2) // name, id
			},
		},
		{
			name:      "multiple set columns, single where column",
			setCols:   []string{"name", "email", "age"},
			whereCols: []string{"id"},
			table:     "users",
			validate: func(t *testing.T, stmt string, names []string) {
				assert.Contains(t, stmt, "UPDATE")
				assert.Contains(t, stmt, "users")
				assert.Len(t, names, 4) // name, email, age, id
			},
		},
		{
			name:      "single set column, multiple where columns",
			setCols:   []string{"status"},
			whereCols: []string{"user_id", "account_id"},
			table:     "accounts",
			validate: func(t *testing.T, stmt string, names []string) {
				assert.Contains(t, stmt, "UPDATE")
				assert.Contains(t, stmt, "accounts")
				assert.Len(t, names, 3) // status, user_id, account_id
			},
		},
		{
			name:      "multiple set and where columns",
			setCols:   []string{"name", "email"},
			whereCols: []string{"id", "version"},
			table:     "users",
			validate: func(t *testing.T, stmt string, names []string) {
				assert.Contains(t, stmt, "UPDATE")
				assert.Len(t, names, 4) // name, email, id, version
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// 創建一個臨時的 repository 來測試 buildUpdateStatement
			// 注意：這需要一個有效的 metadata
			// 使用 testUser 的 metadata
			var zero testUser
			metadata, err := generateMetadata(zero, "test_keyspace")
			require.NoError(t, err)

			repo := &repository[testUser]{
				table:    tt.table,
				metadata: metadata,
			}
			stmt, names := repo.buildUpdateStatement(tt.setCols, tt.whereCols)
			tt.validate(t, stmt, names)
		})
	}
}

func TestBuildUpdateFields(t *testing.T) {
	tests := []struct {
		name        string
		doc         testUser
		includeZero bool
		wantErr     bool
		validate    func(*testing.T, *updateFields)
	}{
		{
			name:        "update with includeZero false",
			doc:         testUser{ID: "1", Name: "Alice", Email: "alice@example.com"},
			includeZero: false,
			wantErr:     false,
			validate: func(t *testing.T, fields *updateFields) {
				assert.NotEmpty(t, fields.setCols)
				assert.Contains(t, fields.whereCols, "id")
			},
		},
		{
			name:        "update with includeZero true",
			doc:         testUser{ID: "1", Name: "", Email: ""},
			includeZero: true,
			wantErr:     false,
			validate: func(t *testing.T, fields *updateFields) {
				assert.NotEmpty(t, fields.setCols)
			},
		},
		{
			name:        "no fields to update",
			doc:         testUser{ID: "1"},
			includeZero: false,
			wantErr:     true,
			validate:    nil,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// 注意：這需要一個有效的 repository 和 metadata
			// 在實際測試中，需要使用 mock 或 testcontainers
			_ = tt
		})
	}
}

