package pagination

import (
	"reflect"
	"testing"

	"go.einride.tech/aip/pagination"
	freightv1 "go.einride.tech/aip/proto/gen/einride/example/freight/v1"
)

// TestLocalPagination tests the LocalPagination function.
func TestLocalPagination(t *testing.T) {
	type args[T any] struct {
		input           []T
		request         pagination.Request
		defaultPageSize int32
		maxPageSize     int32
	}
	type testCase[T any] struct {
		name              string
		args              args[T]
		wantOutput        []T
		wantNextPageToken string
		wantErr           bool
	}
	tests := []testCase[int]{
		{
			name: "Page token and page size",
			args: args[int]{
				input: []int{1, 2, 3, 4, 5},
				request: &freightv1.ListSitesRequest{
					PageToken: "NX8DAQEJUGFnZVRva2VuAf-AAAECAQZPZmZzZXQBBAABD1JlcXVlc3RDaGVja3N1bQEGAAAAC_-AAQQB_JrLBEIA",
					PageSize:  2,
				},
				defaultPageSize: 1,
				maxPageSize:     5,
			},
			wantOutput:        []int{3, 4},
			wantNextPageToken: "NX8DAQEJUGFnZVRva2VuAf-AAAECAQZPZmZzZXQBBAABD1JlcXVlc3RDaGVja3N1bQEGAAAAC_-AAQgB_JrLBEIA",
			wantErr:           false,
		},
		{
			name: "Default page size",
			args: args[int]{
				input:           []int{1, 2, 3, 4, 5},
				request:         &freightv1.ListSitesRequest{PageToken: "", PageSize: 0},
				defaultPageSize: 2,
				maxPageSize:     5,
			},
			wantOutput:        []int{1, 2},
			wantNextPageToken: "NX8DAQEJUGFnZVRva2VuAf-AAAECAQZPZmZzZXQBBAABD1JlcXVlc3RDaGVja3N1bQEGAAAAC_-AAQQB_JrLBEIA",
			wantErr:           false,
		},
		{
			name: "Exceed max page size",
			args: args[int]{
				input:           []int{1, 2, 3, 4, 5},
				request:         &freightv1.ListSitesRequest{PageToken: "", PageSize: 10},
				defaultPageSize: 2,
				maxPageSize:     5,
			},
			wantOutput:        []int{1, 2, 3, 4, 5},
			wantNextPageToken: "",
			wantErr:           false,
		},
		{
			name: "Negative page size",
			args: args[int]{
				input:           []int{1, 2, 3, 4, 5},
				request:         &freightv1.ListSitesRequest{PageToken: "", PageSize: -1},
				defaultPageSize: 2,
				maxPageSize:     5,
			},
			wantOutput:        nil,
			wantNextPageToken: "",
			wantErr:           true,
		},
		{
			name: "Page size larger than input",
			args: args[int]{
				input:           []int{1, 2, 3},
				request:         &freightv1.ListSitesRequest{PageToken: "", PageSize: 5},
				defaultPageSize: 2,
				maxPageSize:     5,
			},
			wantOutput:        []int{1, 2, 3},
			wantNextPageToken: "",
			wantErr:           false,
		},
		{
			name: "End of input",
			args: args[int]{
				input: []int{1, 2, 3, 4, 5},
				request: &freightv1.ListSitesRequest{
					PageToken: "NX8DAQEJUGFnZVRva2VuAf-AAAECAQZPZmZzZXQBBAABD1JlcXVlc3RDaGVja3N1bQEGAAAAC_-AAQQB_JrLBEIA",
					PageSize:  3,
				},
				defaultPageSize: 2,
				maxPageSize:     5,
			},
			wantOutput:        []int{3, 4, 5},
			wantNextPageToken: "",
			wantErr:           false,
		},
		{
			name: "Skip results",
			args: args[int]{
				input: []int{1, 2, 3, 4, 5},
				request: &freightv1.ListSitesRequest{
					PageToken: "NX8DAQEJUGFnZVRva2VuAf-AAAECAQZPZmZzZXQBBAABD1JlcXVlc3RDaGVja3N1bQEGAAAAC_-AAQQB_JrLBEIA",
					PageSize:  2,
					Skip:      1,
				},
				defaultPageSize: 2,
				maxPageSize:     5,
			},
			wantOutput:        []int{4, 5},
			wantNextPageToken: "",
			wantErr:           false,
		},
		{
			name: "Skip results to the end of input",
			args: args[int]{
				input: []int{1, 2, 3, 4, 5, 6},
				request: &freightv1.ListSitesRequest{
					PageToken: "NX8DAQEJUGFnZVRva2VuAf-AAAECAQZPZmZzZXQBBAABD1JlcXVlc3RDaGVja3N1bQEGAAAAC_-AAQQB_JrLBEIA",
					PageSize:  2,
					Skip:      1,
				},
				defaultPageSize: 2,
				maxPageSize:     5,
			},
			wantOutput:        []int{4, 5},
			wantNextPageToken: "NX8DAQEJUGFnZVRva2VuAf-AAAECAQZPZmZzZXQBBAABD1JlcXVlc3RDaGVja3N1bQEGAAAAC_-AAQoB_JrLBEIA",
			wantErr:           false,
		},
		{
			name: "Skip results exceeding input",
			args: args[int]{
				input: []int{1, 2, 3, 4, 5},
				request: &freightv1.ListSitesRequest{
					PageToken: "NX8DAQEJUGFnZVRva2VuAf-AAAECAQZPZmZzZXQBBAABD1JlcXVlc3RDaGVja3N1bQEGAAAAC_-AAQQB_JrLBEIA",
					PageSize:  2,
					Skip:      2,
				},
				defaultPageSize: 2,
				maxPageSize:     5,
			},
			wantOutput:        []int{5},
			wantNextPageToken: "",
			wantErr:           false,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			gotOutput, gotNextPageToken, err := LocalPagination(
				tt.args.input,
				tt.args.request,
				tt.args.defaultPageSize,
				tt.args.maxPageSize,
			)
			if (err != nil) != tt.wantErr {
				t.Errorf("LocalPagination() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
			if !reflect.DeepEqual(gotOutput, tt.wantOutput) {
				t.Errorf("LocalPagination() gotOutput = %v, want %v", gotOutput, tt.wantOutput)
			}
			if gotNextPageToken != tt.wantNextPageToken {
				t.Errorf("LocalPagination() gotNextPageToken = %v, want %v", gotNextPageToken, tt.wantNextPageToken)
			}
		})
	}
}
