package pagination

import (
	"testing"
	"time"

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

// TestCursor_Encode 测试 Cursor 编码
func TestCursor_Encode(t *testing.T) {
	tests := []struct {
		name     string
		cursor   *Cursor
		expected string
	}{
		{
			name:     "nil cursor",
			cursor:   nil,
			expected: "",
		},
		{
			name:     "zero LastID",
			cursor:   &Cursor{LastID: 0, LastValue: time.Now()},
			expected: "",
		},
		{
			name:     "valid cursor",
			cursor:   &Cursor{LastID: 123, LastValue: time.Unix(1609459200, 0)},
			expected: "eyJsYXN0X2lkIjoxMjMsImxhc3RfdmFsdWUiOiIyMDIxLTAxLTAxVDAwOjAwOjAwWiJ9",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := tt.cursor.Encode()
			if tt.expected == "" {
				assert.Empty(t, got)
			} else {
				assert.NotEmpty(t, got)
			}
		})
	}
}

// TestDecode 测试 Cursor 解码
func TestDecode(t *testing.T) {
	tests := []struct {
		name        string
		encoded     string
		wantNil     bool
		wantErr     bool
		expectedID  int64
		expectedTime time.Time
	}{
		{
			name:    "empty string",
			encoded: "",
			wantNil: true,
			wantErr: false,
		},
		{
			name:    "invalid base64",
			encoded: "!!!invalid!!!",
			wantNil: true,
			wantErr: true,
		},
		{
			name:    "invalid json",
			encoded: "aW52YWxpZCBqc29u", // "invalid json" base64
			wantNil: true,
			wantErr: true,
		},
		{
			name:         "valid cursor",
			encoded:      "eyJsYXN0X2lkIjoxMjMsImxhc3RfdmFsdWUiOiIyMDIxLTAxLTAxVDAwOjAwOjAwWiJ9",
			wantNil:      false,
			wantErr:      false,
			expectedID:   123,
			expectedTime: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := Decode(tt.encoded)
			if tt.wantErr {
				require.Error(t, err)
				return
			}
			require.NoError(t, err)
			if tt.wantNil {
				assert.Nil(t, got)
			} else {
				require.NotNil(t, got)
				assert.Equal(t, tt.expectedID, got.LastID)
				assert.Equal(t, tt.expectedTime, got.LastValue)
			}
		})
	}
}

// TestClampPageSize 测试分页大小限制
func TestClampPageSize(t *testing.T) {
	tests := []struct {
		name     string
		input    int
		expected int
	}{
		{"zero", 0, DefaultPageSize},
		{"negative", -1, DefaultPageSize},
		{"negative large", -100, DefaultPageSize},
		{"one", 1, 1},
		{"ten", 10, 10},
		{"default", 20, 20},
		{"max", 100, 100},
		{"over max", 101, MaxPageSize},
		{"large", 1000, MaxPageSize},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := ClampPageSize(tt.input)
			assert.Equal(t, tt.expected, got)
		})
	}
}

// TestBuildNextCursor 测试构建下一页游标
func TestBuildNextCursor(t *testing.T) {
	tests := []struct {
		name     string
		lastID   int64
		lastTime time.Time
		expected string
	}{
		{
			name:     "zero ID",
			lastID:   0,
			lastTime: time.Now(),
			expected: "",
		},
		{
			name:     "valid cursor",
			lastID:   456,
			lastTime: time.Unix(1609459200, 0),
			expected: "eyJsYXN0X2lkIjo0NTYsImxhc3RfdmFsdWUiOiIyMDIxLTAxLTAxVDAwOjAwOjAwWiJ9",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got := BuildNextCursor(tt.lastID, tt.lastTime)
			if tt.expected == "" {
				assert.Empty(t, got)
			} else {
				assert.NotEmpty(t, got)
				// 验证可以解码
				decoded, err := Decode(got)
				require.NoError(t, err)
				assert.Equal(t, tt.lastID, decoded.LastID)
			}
		})
	}
}

// TestPageResult 测试 PageResult 结构
func TestPageResult(t *testing.T) {
	// 测试泛型 PageResult
	type Item struct {
		ID   int
		Name string
	}

	items := []Item{
		{ID: 1, Name: "item1"},
		{ID: 2, Name: "item2"},
	}

	result := PageResult[Item]{
		Items:      items,
		Total:      100,
		NextCursor: "cursor123",
		HasMore:    true,
		PageSize:   20,
	}

	assert.Len(t, result.Items, 2)
	assert.Equal(t, int64(100), result.Total)
	assert.Equal(t, "cursor123", result.NextCursor)
	assert.True(t, result.HasMore)
	assert.Equal(t, 20, result.PageSize)
}

// TestCursor_RoundTrip 测试编码解码往返
func TestCursor_RoundTrip(t *testing.T) {
	original := &Cursor{
		LastID:    999,
		LastValue: time.Date(2023, 6, 15, 10, 30, 0, 0, time.UTC),
	}

	encoded := original.Encode()
	require.NotEmpty(t, encoded)

	decoded, err := Decode(encoded)
	require.NoError(t, err)
	require.NotNil(t, decoded)

	assert.Equal(t, original.LastID, decoded.LastID)
	assert.Equal(t, original.LastValue, decoded.LastValue)
}

// TestConstants 测试常量值
func TestConstants(t *testing.T) {
	assert.Equal(t, 20, DefaultPageSize)
	assert.Equal(t, 100, MaxPageSize)
}
