package sqls

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/require"
)

func TestAppendUniqueFields(t *testing.T) {
	tests := []struct {
		name     string
		slice    []string
		elems    []string
		expected []string
	}{
		{
			name:     "Empty slice and elems",
			slice:    []string{},
			elems:    []string{},
			expected: []string{},
		},
		{
			name:     "Add unique elements",
			slice:    []string{"a", "b"},
			elems:    []string{"c", "d"},
			expected: []string{"a", "b", "c", "d"},
		},
		{
			name:     "Skip duplicate elements",
			slice:    []string{"a", "b"},
			elems:    []string{"b", "c"},
			expected: []string{"a", "b", "c"},
		},
		{
			name:     "All duplicates",
			slice:    []string{"a", "b"},
			elems:    []string{"a", "b"},
			expected: []string{"a", "b"},
		},
		{
			name:     "Empty slice with elems",
			slice:    []string{},
			elems:    []string{"a", "b"},
			expected: []string{"a", "b"},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := appendUniqueFields(tt.slice, tt.elems)
			require.Equal(t, tt.expected, result)
		})
	}
}

func TestConvertStatementFields(t *testing.T) {
	tests := []struct {
		name     string
		fields   []string
		expected []string
	}{
		{
			name:     "Empty fields",
			fields:   []string{},
			expected: nil,
		},
		{
			name:     "Nil fields",
			fields:   nil,
			expected: nil,
		},
		{
			name:     "Mapped field",
			fields:   []string{"summary_begin_time"},
			expected: []string{"min(epoch(SUMMARY_BEGIN_TIME)) as summary_begin_time"},
		},
		{
			name:     "Unmapped field",
			fields:   []string{"unknown_field"},
			expected: []string{"unknown_field"},
		},
		{
			name:     "Mixed fields",
			fields:   []string{"summary_begin_time", "unknown_field", "digest"},
			expected: []string{"min(epoch(SUMMARY_BEGIN_TIME)) as summary_begin_time", "unknown_field", "digest"},
		},
		{
			name:   "avg_ru_v2 mapped like sum_ru for v2 columns",
			fields: []string{"avg_ru_v2"},
			expected: []string{
				"sum(COALESCE(AVG_REQUEST_UNIT_V2,0)*exec_count)/sum(exec_count) as avg_ru_v2",
			},
		},
		{
			name:   "sum_ru_v2 aggregates AVG_REQUEST_UNIT_V2 * exec_count",
			fields: []string{"sum_ru_v2"},
			expected: []string{
				"sum(COALESCE(AVG_REQUEST_UNIT_V2,0)*exec_count) as sum_ru_v2",
			},
		},
		{
			name:   "user maps to sample_user for v1",
			fields: []string{"user"},
			expected: []string{
				"any_value(sample_user) as user",
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := convertStatementFields(tt.fields, "")
			require.Equal(t, tt.expected, result)
		})
	}
}

func TestConvertStatementFields_UserV2(t *testing.T) {
	result := convertStatementFields([]string{"user"}, "v2")
	require.Equal(t, []string{
		"any_value(list_element(coalesce(json_keys(auth_users), []), 1)) as user",
	}, result)
}

func TestParsePlan(t *testing.T) {
	tests := []struct {
		name       string
		plan       string
		binaryPlan string
		expected   string
	}{
		{
			name:       "Empty plan and binary plan",
			plan:       "",
			binaryPlan: "",
			expected:   "",
		},
		{
			name:       "Plain plan",
			plan:       "SELECT * FROM table",
			binaryPlan: "",
			expected:   "SELECT * FROM table",
		},
		{
			name:       "Plan with tidb_decode_plan prefix",
			plan:       "tidb_decode_plan('test')",
			binaryPlan: "",
			expected:   "test", // Will be decoded, but we can't test actual decoding without plancodec
		},
		{
			name:       "Binary plan with prefix",
			plan:       "",
			binaryPlan: "tidb_decode_binary_plan('test')",
			expected:   "test", // Will be decoded, but we can't test actual decoding without plancodec
		},
		{
			name:       "Binary plan takes precedence",
			plan:       "tidb_decode_plan('plan')",
			binaryPlan: "tidb_decode_binary_plan('binary')",
			expected:   "binary",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := parsePlan(tt.plan, tt.binaryPlan)
			// For plans with decode prefixes, we can only check that they're processed
			// Actual decoding requires plancodec which may fail, so we just verify the function runs
			require.NotNil(t, result)
		})
	}
}

func TestStatementsAvailableFieldsHandler(t *testing.T) {
	gin.SetMode(gin.TestMode)

	mockAdapter := &mockDuckDBAdapter{}
	service := &Service{
		SQLsDuckDBAdapter: mockAdapter,
		SQLsQuery:         mockAdapter,
	}

	router := gin.New()
	router.GET("/statements/available_fields", service.StatementsAvailableFieldsHandler)

	tests := []struct {
		name           string
		headers        map[string]string
		expectedStatus int
	}{
		{
			name: "Valid headers",
			headers: map[string]string{
				"X-Provider":    "aws",
				"X-Region":      "us-west-2",
				"X-Org-Id":      "tenant-1",
				"X-Project-Id":  "project-1",
				"X-Cluster-Id":  "cluster-1",
				"X-Deploy-Type": "dedicated",
			},
			expectedStatus: http.StatusOK,
		},
		{
			name:           "Missing headers",
			headers:        map[string]string{},
			expectedStatus: http.StatusBadRequest,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			req := httptest.NewRequest("GET", "/statements/available_fields", nil)
			for k, v := range tt.headers {
				req.Header.Set(k, v)
			}

			w := httptest.NewRecorder()
			router.ServeHTTP(w, req)

			require.Equal(t, tt.expectedStatus, w.Code)

			if tt.expectedStatus == http.StatusOK {
				var fields []string
				err := json.Unmarshal(w.Body.Bytes(), &fields)
				require.NoError(t, err)
				require.NotEmpty(t, fields)
			}
		})
	}
}

func TestStatementsStmtTypesHandler(t *testing.T) {
	gin.SetMode(gin.TestMode)

	mockAdapter := &mockDuckDBAdapter{}
	service := &Service{
		SQLsDuckDBAdapter: mockAdapter,
		SQLsQuery:         mockAdapter,
	}

	router := gin.New()
	router.GET("/statements/stmt_types", service.StatementsStmtTypesHandler)

	req := httptest.NewRequest("GET", "/statements/stmt_types", nil)
	req.Header.Set("X-Provider", "aws")
	req.Header.Set("X-Region", "us-west-2")
	req.Header.Set("X-Org-Id", "tenant-1")
	req.Header.Set("X-Project-Id", "project-1")
	req.Header.Set("X-Cluster-Id", "cluster-1")
	req.Header.Set("X-Deploy-Type", "dedicated")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	require.Equal(t, http.StatusOK, w.Code)

	var stmtTypes []string
	err := json.Unmarshal(w.Body.Bytes(), &stmtTypes)
	require.NoError(t, err)
	require.NotEmpty(t, stmtTypes)
	require.Contains(t, stmtTypes, "Select")
	require.Contains(t, stmtTypes, "Insert")
	require.Contains(t, stmtTypes, "Update")
}

func TestStatementsTimeRangesHandler(t *testing.T) {
	gin.SetMode(gin.TestMode)

	mockAdapter := &mockDuckDBAdapter{}
	service := &Service{
		SQLsDuckDBAdapter: mockAdapter,
		SQLsQuery:         mockAdapter,
	}

	router := gin.New()
	router.GET("/statements/time_ranges", service.StatementsTimeRangesHandler)

	req := httptest.NewRequest("GET", "/statements/time_ranges", nil)
	req.Header.Set("X-Provider", "aws")
	req.Header.Set("X-Region", "us-west-2")
	req.Header.Set("X-Org-Id", "tenant-1")
	req.Header.Set("X-Project-Id", "project-1")
	req.Header.Set("X-Cluster-Id", "cluster-1")
	req.Header.Set("X-Deploy-Type", "dedicated")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	require.Equal(t, http.StatusOK, w.Code)
	require.Equal(t, "", w.Body.String())
}

func TestHandleTableList(t *testing.T) {
	gin.SetMode(gin.TestMode)

	mockAdapter := &mockDuckDBAdapter{
		listTablesFunc: func(ctx context.Context, params *ListTableParams) ([]string, error) {
			return []string{"table1", "table2"}, nil
		},
	}
	service := &Service{
		SQLsDuckDBAdapter: mockAdapter,
		SQLsQuery:         mockAdapter,
	}

	router := gin.New()
	router.GET("/SQLs/", service.handleTableList)

	req := httptest.NewRequest("GET", "/SQLs/?type=statements", nil)
	req.Header.Set("X-Provider", "aws")
	req.Header.Set("X-Region", "us-west-2")
	req.Header.Set("X-Org-Id", "tenant-1")
	req.Header.Set("X-Project-Id", "project-1")
	req.Header.Set("X-Cluster-Id", "cluster-1")
	req.Header.Set("X-Deploy-Type", "dedicated")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	require.Equal(t, http.StatusOK, w.Code)

	var tables []string
	err := json.Unmarshal(w.Body.Bytes(), &tables)
	require.NoError(t, err)
	require.Equal(t, []string{"table1", "table2"}, tables)
}

func TestHandleTableFields(t *testing.T) {
	gin.SetMode(gin.TestMode)

	mockAdapter := &mockDuckDBAdapter{
		listTableFieldsFunc: func(ctx context.Context, params *ListTableFieldsParams) ([]FieldInfo, error) {
			return []FieldInfo{
				{Name: "field1", Type: "string"},
				{Name: "field2", Type: "int"},
			}, nil
		},
	}
	service := &Service{
		SQLsDuckDBAdapter: mockAdapter,
		SQLsQuery:         mockAdapter,
	}

	router := gin.New()
	router.GET("/SQLs/:name/fields", service.handleTableFields)

	req := httptest.NewRequest("GET", "/SQLs/test_table/fields", nil)
	req.Header.Set("X-Provider", "aws")
	req.Header.Set("X-Region", "us-west-2")
	req.Header.Set("X-Org-Id", "tenant-1")
	req.Header.Set("X-Project-Id", "project-1")
	req.Header.Set("X-Cluster-Id", "cluster-1")
	req.Header.Set("X-Deploy-Type", "dedicated")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	require.Equal(t, http.StatusOK, w.Code)

	var response TableFieldsResponse
	err := json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)
	require.Len(t, response.Fields, 2)
	require.Equal(t, "field1", response.Fields[0].Name)
	require.Equal(t, "string", response.Fields[0].Type)
}

func TestHandleTableData(t *testing.T) {
	gin.SetMode(gin.TestMode)

	mockAdapter := &mockDuckDBAdapter{
		listTableDataFunc: func(ctx context.Context, params *ListTableDataParams) (*ListTableDataResponse, error) {
			return &ListTableDataResponse{
				Data: []map[string]interface{}{
					{"id": 1, "name": "test"},
				},
				Pagination: struct {
					Total  int64 `json:"total"`
					Limit  int   `json:"limit"`
					Offset int   `json:"offset"`
				}{
					Total:  1,
					Limit:  10,
					Offset: 0,
				},
			}, nil
		},
	}
	service := &Service{
		SQLsDuckDBAdapter: mockAdapter,
		SQLsQuery:         mockAdapter,
	}

	router := gin.New()
	router.GET("/SQLs/:name/data", service.handleTableData)

	req := httptest.NewRequest("GET", "/SQLs/test_table/data?StartTime=2024-01-01T00:00:00Z&EndTime=2024-01-02T00:00:00Z&Fields=id,name", nil)
	req.Header.Set("X-Provider", "aws")
	req.Header.Set("X-Region", "us-west-2")
	req.Header.Set("X-Org-Id", "tenant-1")
	req.Header.Set("X-Project-Id", "project-1")
	req.Header.Set("X-Cluster-Id", "cluster-1")
	req.Header.Set("X-Deploy-Type", "dedicated")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	require.Equal(t, http.StatusOK, w.Code)

	var response ListTableDataResponse
	err := json.Unmarshal(w.Body.Bytes(), &response)
	require.NoError(t, err)
	require.Len(t, response.Data, 1)
	require.Equal(t, int64(1), response.Pagination.Total)
}

func TestHandleTableDataByID(t *testing.T) {
	gin.SetMode(gin.TestMode)

	mockAdapter := &mockDuckDBAdapter{
		getTableDataByIDFunc: func(ctx context.Context, params *GetTableDataByIDParams) (map[string]interface{}, error) {
			return map[string]interface{}{
				"id":   123,
				"name": "test",
			}, nil
		},
	}
	service := &Service{
		SQLsDuckDBAdapter: mockAdapter,
		SQLsQuery:         mockAdapter,
	}

	router := gin.New()
	router.GET("/SQLs/:name/data/:id", service.handleTableDataByID)

	req := httptest.NewRequest("GET", "/SQLs/test_table/data/123", nil)
	req.Header.Set("X-Provider", "aws")
	req.Header.Set("X-Region", "us-west-2")
	req.Header.Set("X-Org-Id", "tenant-1")
	req.Header.Set("X-Project-Id", "project-1")
	req.Header.Set("X-Cluster-Id", "cluster-1")
	req.Header.Set("X-Deploy-Type", "dedicated")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	require.Equal(t, http.StatusOK, w.Code)

	var record map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &record)
	require.NoError(t, err)
	require.Equal(t, float64(123), record["id"])
	require.Equal(t, "test", record["name"])
}

func TestHandleTableDataByID_InvalidID(t *testing.T) {
	gin.SetMode(gin.TestMode)

	mockAdapter := &mockDuckDBAdapter{}
	service := &Service{
		SQLsDuckDBAdapter: mockAdapter,
		SQLsQuery:         mockAdapter,
	}

	router := gin.New()
	router.GET("/SQLs/:name/data/:id", service.handleTableDataByID)

	req := httptest.NewRequest("GET", "/SQLs/test_table/data/invalid", nil)
	req.Header.Set("X-Provider", "aws")
	req.Header.Set("X-Region", "us-west-2")
	req.Header.Set("X-Org-Id", "tenant-1")
	req.Header.Set("X-Project-Id", "project-1")
	req.Header.Set("X-Cluster-Id", "cluster-1")
	req.Header.Set("X-Deploy-Type", "dedicated")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	require.Equal(t, http.StatusBadRequest, w.Code)
}

func TestHandleTableList_InvalidHeader(t *testing.T) {
	gin.SetMode(gin.TestMode)

	mockAdapter := &mockDuckDBAdapter{}
	service := &Service{
		SQLsDuckDBAdapter: mockAdapter,
		SQLsQuery:         mockAdapter,
	}

	router := gin.New()
	router.GET("/SQLs/", service.handleTableList)

	req := httptest.NewRequest("GET", "/SQLs/", nil)
	// Missing required headers

	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	require.Equal(t, http.StatusBadRequest, w.Code)
}

func TestHandleTableFields_MissingTableName(t *testing.T) {
	gin.SetMode(gin.TestMode)

	mockAdapter := &mockDuckDBAdapter{}
	service := &Service{
		SQLsDuckDBAdapter: mockAdapter,
		SQLsQuery:         mockAdapter,
	}

	router := gin.New()
	router.GET("/SQLs/:name/fields", service.handleTableFields)

	req := httptest.NewRequest("GET", "/SQLs//fields", nil)
	req.Header.Set("X-Provider", "aws")
	req.Header.Set("X-Region", "us-west-2")
	req.Header.Set("X-Org-Id", "tenant-1")
	req.Header.Set("X-Project-Id", "project-1")
	req.Header.Set("X-Cluster-Id", "cluster-1")
	req.Header.Set("X-Deploy-Type", "dedicated")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	require.Equal(t, http.StatusBadRequest, w.Code)
}

func TestHandleTableData_MissingFields(t *testing.T) {
	gin.SetMode(gin.TestMode)

	mockAdapter := &mockDuckDBAdapter{}
	service := &Service{
		SQLsDuckDBAdapter: mockAdapter,
		SQLsQuery:         mockAdapter,
	}

	router := gin.New()
	router.GET("/SQLs/:name/data", service.handleTableData)

	req := httptest.NewRequest("GET", "/SQLs/test_table/data?StartTime=2024-01-01T00:00:00Z&EndTime=2024-01-02T00:00:00Z", nil)
	req.Header.Set("X-Provider", "aws")
	req.Header.Set("X-Region", "us-west-2")
	req.Header.Set("X-Org-Id", "tenant-1")
	req.Header.Set("X-Project-Id", "project-1")
	req.Header.Set("X-Cluster-Id", "cluster-1")
	req.Header.Set("X-Deploy-Type", "dedicated")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	require.Equal(t, http.StatusBadRequest, w.Code)
}

func TestHandleTableData_WithFilter(t *testing.T) {
	gin.SetMode(gin.TestMode)

	mockAdapter := &mockDuckDBAdapter{
		listTableDataFunc: func(ctx context.Context, params *ListTableDataParams) (*ListTableDataResponse, error) {
			return &ListTableDataResponse{
				Data: []map[string]interface{}{
					{"id": 1, "name": "test"},
				},
			}, nil
		},
	}
	service := &Service{
		SQLsDuckDBAdapter: mockAdapter,
		SQLsQuery:         mockAdapter,
	}

	router := gin.New()
	router.GET("/SQLs/:name/data", service.handleTableData)

	req := httptest.NewRequest("GET", "/SQLs/test_table/data?Filter=id>0&Fields=id,name", nil)
	req.Header.Set("X-Provider", "aws")
	req.Header.Set("X-Region", "us-west-2")
	req.Header.Set("X-Org-Id", "tenant-1")
	req.Header.Set("X-Project-Id", "project-1")
	req.Header.Set("X-Cluster-Id", "cluster-1")
	req.Header.Set("X-Deploy-Type", "dedicated")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	require.Equal(t, http.StatusOK, w.Code)
}

func TestHandleTableData_BYOC(t *testing.T) {
	gin.SetMode(gin.TestMode)

	mockAdapter := &mockDuckDBAdapter{
		listTableDataFunc: func(ctx context.Context, params *ListTableDataParams) (*ListTableDataResponse, error) {
			return &ListTableDataResponse{
				Data: []map[string]interface{}{},
			}, nil
		},
	}
	service := &Service{
		SQLsDuckDBAdapter: mockAdapter,
		SQLsQuery:         mockAdapter,
	}

	router := gin.New()
	router.GET("/SQLs/:name/data", service.handleTableData)

	req := httptest.NewRequest("GET", "/SQLs/test_table/data?StartTime=2024-01-01T00:00:00Z&EndTime=2024-01-02T00:00:00Z&Fields=id", nil)
	req.Header.Set("X-Provider", "aws")
	req.Header.Set("X-Region", "us-west-2")
	req.Header.Set("X-Org-Id", "tenant-1")
	req.Header.Set("X-Project-Id", "project-1")
	req.Header.Set("X-Cluster-Id", "cluster-1")
	req.Header.Set("X-Deploy-Type", "byoc")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	require.Equal(t, http.StatusOK, w.Code)
}

func TestHandleTableData_SharedTier(t *testing.T) {
	gin.SetMode(gin.TestMode)

	mockAdapter := &mockDuckDBAdapter{
		listTableDataFunc: func(ctx context.Context, params *ListTableDataParams) (*ListTableDataResponse, error) {
			return &ListTableDataResponse{
				Data: []map[string]interface{}{},
			}, nil
		},
	}
	service := &Service{
		SQLsDuckDBAdapter: mockAdapter,
		SQLsQuery:         mockAdapter,
	}

	router := gin.New()
	router.GET("/SQLs/:name/data", service.handleTableData)

	req := httptest.NewRequest("GET", "/SQLs/test_table/data?StartTime=2024-01-01T00:00:00Z&EndTime=2024-01-02T00:00:00Z&Fields=id", nil)
	req.Header.Set("X-Provider", "aws")
	req.Header.Set("X-Region", "us-west-2")
	req.Header.Set("X-Org-Id", "tenant-1")
	req.Header.Set("X-Project-Id", "project-1")
	req.Header.Set("X-Cluster-Id", "cluster-1")
	req.Header.Set("X-Deploy-Type", "shared")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	require.Equal(t, http.StatusOK, w.Code)
}

func TestHandleTableData_NextGen(t *testing.T) {
	gin.SetMode(gin.TestMode)

	mockAdapter := &mockDuckDBAdapter{
		listTableDataFunc: func(ctx context.Context, params *ListTableDataParams) (*ListTableDataResponse, error) {
			return &ListTableDataResponse{
				Data: []map[string]interface{}{},
			}, nil
		},
	}
	service := &Service{
		SQLsDuckDBAdapter: mockAdapter,
		SQLsQuery:         mockAdapter,
	}

	router := gin.New()
	router.GET("/SQLs/:name/data", service.handleTableData)

	req := httptest.NewRequest("GET", "/SQLs/test_table/data?StartTime=2024-01-01T00:00:00Z&EndTime=2024-01-02T00:00:00Z&Fields=id", nil)
	req.Header.Set("X-Provider", "aws")
	req.Header.Set("X-Region", "us-west-2")
	req.Header.Set("X-Org-Id", "tenant-1")
	req.Header.Set("X-Project-Id", "project-1")
	req.Header.Set("X-Cluster-Id", "cluster-1")
	req.Header.Set("X-Deploy-Type", "nextgen")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	require.Equal(t, http.StatusOK, w.Code)
}

func TestHandleTableData_Premium(t *testing.T) {
	gin.SetMode(gin.TestMode)

	mockAdapter := &mockDuckDBAdapter{
		listTableDataFunc: func(ctx context.Context, params *ListTableDataParams) (*ListTableDataResponse, error) {
			return &ListTableDataResponse{
				Data: []map[string]interface{}{},
			}, nil
		},
	}
	service := &Service{
		SQLsDuckDBAdapter: mockAdapter,
		SQLsQuery:         mockAdapter,
	}

	router := gin.New()
	router.GET("/SQLs/:name/data", service.handleTableData)

	req := httptest.NewRequest("GET", "/SQLs/test_table/data?StartTime=2024-01-01T00:00:00Z&EndTime=2024-01-02T00:00:00Z&Fields=id", nil)
	req.Header.Set("X-Provider", "aws")
	req.Header.Set("X-Region", "us-west-2")
	req.Header.Set("X-Org-Id", "tenant-1")
	req.Header.Set("X-Project-Id", "project-1")
	req.Header.Set("X-Cluster-Id", "cluster-1")
	req.Header.Set("X-Deploy-Type", "premium")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	require.Equal(t, http.StatusOK, w.Code)
}

func TestHandleTableData_AdapterError(t *testing.T) {
	gin.SetMode(gin.TestMode)

	mockAdapter := &mockDuckDBAdapter{
		listTableDataFunc: func(ctx context.Context, params *ListTableDataParams) (*ListTableDataResponse, error) {
			return nil, errors.New("adapter error")
		},
	}
	service := &Service{
		SQLsDuckDBAdapter: mockAdapter,
		SQLsQuery:         mockAdapter,
	}

	router := gin.New()
	router.GET("/SQLs/:name/data", service.handleTableData)

	req := httptest.NewRequest("GET", "/SQLs/test_table/data?StartTime=2024-01-01T00:00:00Z&EndTime=2024-01-02T00:00:00Z&Fields=id", nil)
	req.Header.Set("X-Provider", "aws")
	req.Header.Set("X-Region", "us-west-2")
	req.Header.Set("X-Org-Id", "tenant-1")
	req.Header.Set("X-Project-Id", "project-1")
	req.Header.Set("X-Cluster-Id", "cluster-1")
	req.Header.Set("X-Deploy-Type", "dedicated")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	require.Equal(t, http.StatusInternalServerError, w.Code)
}

func TestHandleTableDataByID_BYOC(t *testing.T) {
	gin.SetMode(gin.TestMode)

	mockAdapter := &mockDuckDBAdapter{
		getTableDataByIDFunc: func(ctx context.Context, params *GetTableDataByIDParams) (map[string]interface{}, error) {
			return map[string]interface{}{"id": params.ID}, nil
		},
	}
	service := &Service{
		SQLsDuckDBAdapter: mockAdapter,
		SQLsQuery:         mockAdapter,
	}

	router := gin.New()
	router.GET("/SQLs/:name/data/:id", service.handleTableDataByID)

	req := httptest.NewRequest("GET", "/SQLs/test_table/data/123", nil)
	req.Header.Set("X-Provider", "aws")
	req.Header.Set("X-Region", "us-west-2")
	req.Header.Set("X-Org-Id", "tenant-1")
	req.Header.Set("X-Project-Id", "project-1")
	req.Header.Set("X-Cluster-Id", "cluster-1")
	req.Header.Set("X-Deploy-Type", "byoc")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	require.Equal(t, http.StatusOK, w.Code)
}

func TestHandleTableDataByID_AdapterError(t *testing.T) {
	gin.SetMode(gin.TestMode)

	mockAdapter := &mockDuckDBAdapter{
		getTableDataByIDFunc: func(ctx context.Context, params *GetTableDataByIDParams) (map[string]interface{}, error) {
			return nil, errors.New("adapter error")
		},
	}
	service := &Service{
		SQLsDuckDBAdapter: mockAdapter,
		SQLsQuery:         mockAdapter,
	}

	router := gin.New()
	router.GET("/SQLs/:name/data/:id", service.handleTableDataByID)

	req := httptest.NewRequest("GET", "/SQLs/test_table/data/123", nil)
	req.Header.Set("X-Provider", "aws")
	req.Header.Set("X-Region", "us-west-2")
	req.Header.Set("X-Org-Id", "tenant-1")
	req.Header.Set("X-Project-Id", "project-1")
	req.Header.Set("X-Cluster-Id", "cluster-1")
	req.Header.Set("X-Deploy-Type", "dedicated")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	require.Equal(t, http.StatusInternalServerError, w.Code)
}

func TestHandleTableFields_AdapterError(t *testing.T) {
	gin.SetMode(gin.TestMode)

	mockAdapter := &mockDuckDBAdapter{
		listTableFieldsFunc: func(ctx context.Context, params *ListTableFieldsParams) ([]FieldInfo, error) {
			return nil, errors.New("adapter error")
		},
	}
	service := &Service{
		SQLsDuckDBAdapter: mockAdapter,
		SQLsQuery:         mockAdapter,
	}

	router := gin.New()
	router.GET("/SQLs/:name/fields", service.handleTableFields)

	req := httptest.NewRequest("GET", "/SQLs/test_table/fields", nil)
	req.Header.Set("X-Provider", "aws")
	req.Header.Set("X-Region", "us-west-2")
	req.Header.Set("X-Org-Id", "tenant-1")
	req.Header.Set("X-Project-Id", "project-1")
	req.Header.Set("X-Cluster-Id", "cluster-1")
	req.Header.Set("X-Deploy-Type", "dedicated")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	require.Equal(t, http.StatusInternalServerError, w.Code)
}

func TestHandleTableList_AdapterError(t *testing.T) {
	gin.SetMode(gin.TestMode)

	mockAdapter := &mockDuckDBAdapter{
		listTablesFunc: func(ctx context.Context, params *ListTableParams) ([]string, error) {
			return nil, errors.New("adapter error")
		},
	}
	service := &Service{
		SQLsDuckDBAdapter: mockAdapter,
		SQLsQuery:         mockAdapter,
	}

	router := gin.New()
	router.GET("/SQLs/", service.handleTableList)

	req := httptest.NewRequest("GET", "/SQLs/?type=statements", nil)
	req.Header.Set("X-Provider", "aws")
	req.Header.Set("X-Region", "us-west-2")
	req.Header.Set("X-Org-Id", "tenant-1")
	req.Header.Set("X-Project-Id", "project-1")
	req.Header.Set("X-Cluster-Id", "cluster-1")
	req.Header.Set("X-Deploy-Type", "dedicated")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	// Note: handleTableList doesn't return error, it just logs
	// So we check that it doesn't crash
	require.True(t, w.Code == http.StatusOK || w.Code == http.StatusBadRequest)
}

func TestStatementsTimeRangesHandler_InvalidHeader(t *testing.T) {
	gin.SetMode(gin.TestMode)

	mockAdapter := &mockDuckDBAdapter{}
	service := &Service{
		SQLsDuckDBAdapter: mockAdapter,
		SQLsQuery:         mockAdapter,
	}

	router := gin.New()
	router.GET("/statements/time_ranges", service.StatementsTimeRangesHandler)

	req := httptest.NewRequest("GET", "/statements/time_ranges", nil)
	// Missing headers

	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	require.Equal(t, http.StatusBadRequest, w.Code)
}

func TestStatementsStmtTypesHandler_InvalidHeader(t *testing.T) {
	gin.SetMode(gin.TestMode)

	mockAdapter := &mockDuckDBAdapter{}
	service := &Service{
		SQLsDuckDBAdapter: mockAdapter,
		SQLsQuery:         mockAdapter,
	}

	router := gin.New()
	router.GET("/statements/stmt_types", service.StatementsStmtTypesHandler)

	req := httptest.NewRequest("GET", "/statements/stmt_types", nil)
	// Missing headers

	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	require.Equal(t, http.StatusBadRequest, w.Code)
}

func TestNewSQLsService(t *testing.T) {
	// This test verifies that NewSQLsService initializes metrics
	// Note: NewSQLsService requires actual DuckDB and Kubernetes client dependencies,
	// so we can't easily test it without setting up complex mocks.
	// The function is simple enough (just initializes metrics and creates adapters)
	// that integration tests would be more appropriate.
	// For unit test coverage, we skip this test as it requires actual infrastructure.
	t.Skip("NewSQLsService requires actual DuckDB and Kubernetes client, skipping unit test")
}

func TestInitSQLsApi(t *testing.T) {
	gin.SetMode(gin.TestMode)

	mockAdapter := &mockDuckDBAdapter{}
	service := &Service{
		SQLsDuckDBAdapter: mockAdapter,
		SQLsQuery:         mockAdapter,
	}

	router := gin.New()
	group := router.Group("/")
	InitSQLsApi(group, service)

	// Test that routes are registered by making requests
	// We'll test one route to verify InitSQLsApi works
	req := httptest.NewRequest("GET", "/statements/available_fields", nil)
	req.Header.Set("X-Provider", "aws")
	req.Header.Set("X-Region", "us-west-2")
	req.Header.Set("X-Org-Id", "tenant-1")
	req.Header.Set("X-Project-Id", "project-1")
	req.Header.Set("X-Cluster-Id", "cluster-1")
	req.Header.Set("X-Deploy-Type", "dedicated")

	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	// Should get a response (either OK or BadRequest depending on validation)
	require.True(t, w.Code == http.StatusOK || w.Code == http.StatusBadRequest)
}

func TestStatementsListHandler(t *testing.T) {
	gin.SetMode(gin.TestMode)

	mockAdapter := &mockDuckDBAdapter{
		listTableDataFunc: func(ctx context.Context, params *ListTableDataParams) (*ListTableDataResponse, error) {
			return &ListTableDataResponse{
				Data: []map[string]interface{}{
					{"digest": "digest1", "exec_count": float64(10)},
				},
			}, nil
		},
	}
	service := &Service{
		SQLsDuckDBAdapter: mockAdapter,
		SQLsQuery:         mockAdapter,
	}

	router := gin.New()
	router.GET("/statements/list", service.StatementsListHandler)

	tests := []struct {
		name           string
		query          string
		headers        map[string]string
		expectedStatus int
		setupMock      func(*mockDuckDBAdapter)
	}{
		{
			name:  "Successful request",
			query: "begin_time=1678886400&end_time=1678886400&fields=digest,exec_count",
			headers: map[string]string{
				"X-Provider":    "aws",
				"X-Region":      "us-west-2",
				"X-Org-Id":      "tenant-1",
				"X-Project-Id":  "project-1",
				"X-Cluster-Id":  "cluster-1",
				"X-Deploy-Type": "dedicated",
			},
			expectedStatus: http.StatusOK,
		},
		{
			name:           "Missing headers",
			query:          "begin_time=1678886400&end_time=1678886400",
			headers:        map[string]string{},
			expectedStatus: http.StatusBadRequest,
		},
		{
			name:  "With schemas and stmt_types",
			query: "begin_time=1678886400&end_time=1678886400&fields=digest&schemas=schema1&stmt_types=Select",
			headers: map[string]string{
				"X-Provider":    "aws",
				"X-Region":      "us-west-2",
				"X-Org-Id":      "tenant-1",
				"X-Project-Id":  "project-1",
				"X-Cluster-Id":  "cluster-1",
				"X-Deploy-Type": "dedicated",
			},
			expectedStatus: http.StatusOK,
		},
		{
			name:  "With text filter",
			query: "begin_time=1678886400&end_time=1678886400&fields=digest&text=SELECT",
			headers: map[string]string{
				"X-Provider":    "aws",
				"X-Region":      "us-west-2",
				"X-Org-Id":      "tenant-1",
				"X-Project-Id":  "project-1",
				"X-Cluster-Id":  "cluster-1",
				"X-Deploy-Type": "dedicated",
			},
			expectedStatus: http.StatusOK,
		},
		{
			name:  "BYOC deploy type",
			query: "begin_time=1678886400&end_time=1678886400&fields=digest",
			headers: map[string]string{
				"X-Provider":    "aws",
				"X-Region":      "us-west-2",
				"X-Org-Id":      "tenant-1",
				"X-Project-Id":  "project-1",
				"X-Cluster-Id":  "cluster-1",
				"X-Deploy-Type": "byoc",
			},
			expectedStatus: http.StatusOK,
		},
		{
			name:  "Adapter error",
			query: "begin_time=1678886400&end_time=1678886400&fields=digest",
			headers: map[string]string{
				"X-Provider":    "aws",
				"X-Region":      "us-west-2",
				"X-Org-Id":      "tenant-1",
				"X-Project-Id":  "project-1",
				"X-Cluster-Id":  "cluster-1",
				"X-Deploy-Type": "dedicated",
			},
			expectedStatus: http.StatusInternalServerError,
			setupMock: func(m *mockDuckDBAdapter) {
				m.listTableDataFunc = func(ctx context.Context, params *ListTableDataParams) (*ListTableDataResponse, error) {
					return nil, errors.New("adapter error")
				}
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			testAdapter := &mockDuckDBAdapter{
				listTableDataFunc: func(ctx context.Context, params *ListTableDataParams) (*ListTableDataResponse, error) {
					return &ListTableDataResponse{
						Data: []map[string]interface{}{
							{"digest": "digest1"},
						},
					}, nil
				},
			}
			if tt.setupMock != nil {
				tt.setupMock(testAdapter)
			}
			testService := &Service{
				SQLsDuckDBAdapter: testAdapter,
				SQLsQuery:         testAdapter,
			}

			testRouter := gin.New()
			testRouter.GET("/statements/list", testService.StatementsListHandler)

			req := httptest.NewRequest("GET", "/statements/list?"+tt.query, nil)
			for k, v := range tt.headers {
				req.Header.Set(k, v)
			}

			w := httptest.NewRecorder()
			testRouter.ServeHTTP(w, req)

			require.Equal(t, tt.expectedStatus, w.Code)

			if tt.expectedStatus == http.StatusOK {
				var data []map[string]interface{}
				err := json.Unmarshal(w.Body.Bytes(), &data)
				require.NoError(t, err)
			}
		})
	}
}

func statementsPlansTestFieldInfos() []FieldInfo {
	names := []string{
		"SUMMARY_BEGIN_TIME", "SUMMARY_END_TIME", "digest_text", "exec_count", "stmt_type",
		"sum_latency", "max_latency", "min_latency", "avg_mem", "max_mem", "last_seen",
		"SCHEMA_NAME", "plan_digest", "plan_hint", "plan_in_binding",
		"AVG_REQUEST_UNIT_READ", "AVG_REQUEST_UNIT_WRITE", "MAX_REQUEST_UNIT_READ", "MAX_REQUEST_UNIT_WRITE",
		"AVG_REQUEST_UNIT_V2", "MAX_REQUEST_UNIT_V2",
		"sum_tidb_cpu", "sum_tikv_cpu", "plan_cache_hits", "plan_in_cache",
		"plan_cache_unqualified_count", "plan_cache_unqualified_last_reason",
		"sum_ia_read_segment_count", "max_ia_read_segment_count",
		"sum_ia_remote_read_segment_size", "max_ia_remote_read_segment_size",
		"sum_ia_remote_read_segment_wait_time", "max_ia_remote_read_segment_wait_time",
		"digest",
		"begin", "end", "normalized_sql", "sum_rru", "sum_wru", "sum_ruv2", "max_ruv2", "schema_name",
	}
	fields := make([]FieldInfo, len(names))
	for i, name := range names {
		fields[i] = FieldInfo{Name: name}
	}
	return fields
}

func TestStatementsPlansHandler(t *testing.T) {
	gin.SetMode(gin.TestMode)

	mockAdapter := &mockDuckDBAdapter{
		listTableFieldsFunc: func(ctx context.Context, params *ListTableFieldsParams) ([]FieldInfo, error) {
			return statementsPlansTestFieldInfos(), nil
		},
		listTableDataFunc: func(ctx context.Context, params *ListTableDataParams) (*ListTableDataResponse, error) {
			return &ListTableDataResponse{
				Data: []map[string]interface{}{
					{"plan_digest": "plan1", "exec_count": float64(5)},
				},
			}, nil
		},
	}
	service := &Service{
		SQLsDuckDBAdapter: mockAdapter,
		SQLsQuery:         mockAdapter,
	}

	router := gin.New()
	router.GET("/statements/plans", service.StatementsPlansHandler)

	tests := []struct {
		name           string
		query          string
		headers        map[string]string
		expectedStatus int
		setupMock      func(*mockDuckDBAdapter)
	}{
		{
			name:  "Successful request",
			query: "begin_time=1678886400&end_time=1678886400&digest=test_digest",
			headers: map[string]string{
				"X-Provider":    "aws",
				"X-Region":      "us-west-2",
				"X-Org-Id":      "tenant-1",
				"X-Project-Id":  "project-1",
				"X-Cluster-Id":  "cluster-1",
				"X-Deploy-Type": "dedicated",
			},
			expectedStatus: http.StatusOK,
		},
		{
			name:           "Missing headers",
			query:          "begin_time=1678886400&end_time=1678886400&digest=test_digest",
			headers:        map[string]string{},
			expectedStatus: http.StatusBadRequest,
		},
		{
			name:  "With schema_name",
			query: "begin_time=1678886400&end_time=1678886400&digest=test_digest&schema_name=test_schema",
			headers: map[string]string{
				"X-Provider":    "aws",
				"X-Region":      "us-west-2",
				"X-Org-Id":      "tenant-1",
				"X-Project-Id":  "project-1",
				"X-Cluster-Id":  "cluster-1",
				"X-Deploy-Type": "dedicated",
			},
			expectedStatus: http.StatusOK,
		},
		{
			name:  "With schema_name N/A",
			query: "begin_time=1678886400&end_time=1678886400&digest=test_digest&schema_name=N/A&data_source=v2",
			headers: map[string]string{
				"X-Provider":    "aws",
				"X-Region":      "us-west-2",
				"X-Org-Id":      "tenant-1",
				"X-Project-Id":  "project-1",
				"X-Cluster-Id":  "cluster-1",
				"X-Deploy-Type": "dedicated",
			},
			expectedStatus: http.StatusOK,
			setupMock: func(m *mockDuckDBAdapter) {
				m.listTableDataFunc = func(ctx context.Context, params *ListTableDataParams) (*ListTableDataResponse, error) {
					require.Contains(t, params.Filter, "coalesce(schema_name, '') = ''")
					return &ListTableDataResponse{
						Data: []map[string]interface{}{
							{"schema_name": "", "plan_digest": ""},
						},
					}, nil
				}
			},
		},
		{
			name:  "BYOC deploy type",
			query: "begin_time=1678886400&end_time=1678886400&digest=test_digest",
			headers: map[string]string{
				"X-Provider":    "aws",
				"X-Region":      "us-west-2",
				"X-Org-Id":      "tenant-1",
				"X-Project-Id":  "project-1",
				"X-Cluster-Id":  "cluster-1",
				"X-Deploy-Type": "byoc",
			},
			expectedStatus: http.StatusOK,
		},
		{
			name:  "Uses coalesced plan_digest group by",
			query: "begin_time=1678886400&end_time=1678886400&digest=test_digest",
			headers: map[string]string{
				"X-Provider":    "aws",
				"X-Region":      "us-west-2",
				"X-Org-Id":      "tenant-1",
				"X-Project-Id":  "project-1",
				"X-Cluster-Id":  "cluster-1",
				"X-Deploy-Type": "dedicated",
			},
			expectedStatus: http.StatusOK,
			setupMock: func(m *mockDuckDBAdapter) {
				m.listTableDataFunc = func(ctx context.Context, params *ListTableDataParams) (*ListTableDataResponse, error) {
					require.Equal(t, statementsPlanDigestGroupByClause(), params.GroupBy)
					require.Contains(t, params.Fields, statementsPlanDigestSelectExpr())
					return &ListTableDataResponse{
						Data: []map[string]interface{}{
							{"plan_digest": ""},
						},
					}, nil
				}
			},
		},
		{
			name:  "Includes RU fields for v2",
			query: "begin_time=1678886400&end_time=1678886400&digest=test_digest&data_source=v2",
			headers: map[string]string{
				"X-Provider":    "aws",
				"X-Region":      "us-west-2",
				"X-Org-Id":      "tenant-1",
				"X-Project-Id":  "project-1",
				"X-Cluster-Id":  "cluster-1",
				"X-Deploy-Type": "dedicated",
			},
			expectedStatus: http.StatusOK,
			setupMock: func(m *mockDuckDBAdapter) {
				m.listTableDataFunc = func(ctx context.Context, params *ListTableDataParams) (*ListTableDataResponse, error) {
					require.Contains(t, params.Fields, "sum(sum_rru) + sum(sum_wru) as sum_ru")
					require.Contains(t, params.Fields, "(sum(sum_rru) + sum(sum_wru))/sum(exec_count) as avg_ru")
					require.Contains(t, params.Fields, "sum(sum_ruv2) as sum_ru_v2")
					return &ListTableDataResponse{
						Data: []map[string]interface{}{
							{"plan_digest": "plan1", "sum_ru": 12.5},
						},
					}, nil
				}
			},
		},
		{
			name:  "schema without RU v2 columns excludes ru_v2 fields",
			query: "begin_time=1678886400&end_time=1678886400&digest=test_digest",
			headers: map[string]string{
				"X-Provider":    "aws",
				"X-Region":      "us-west-2",
				"X-Org-Id":      "tenant-1",
				"X-Project-Id":  "project-1",
				"X-Cluster-Id":  "cluster-1",
				"X-Deploy-Type": "dedicated",
			},
			expectedStatus: http.StatusOK,
			setupMock: func(m *mockDuckDBAdapter) {
				m.listTableFieldsFunc = func(ctx context.Context, params *ListTableFieldsParams) ([]FieldInfo, error) {
					return []FieldInfo{
						{Name: "digest"},
						{Name: "exec_count"},
						{Name: "SUMMARY_BEGIN_TIME"},
						{Name: "SUMMARY_END_TIME"},
						{Name: "digest_text"},
						{Name: "AVG_REQUEST_UNIT_READ"},
						{Name: "AVG_REQUEST_UNIT_WRITE"},
						{Name: "MAX_REQUEST_UNIT_READ"},
						{Name: "MAX_REQUEST_UNIT_WRITE"},
						{Name: "plan_digest"},
					}, nil
				}
				m.listTableDataFunc = func(ctx context.Context, params *ListTableDataParams) (*ListTableDataResponse, error) {
					require.Contains(t, params.Fields, "sum(AVG_REQUEST_UNIT_READ*exec_count) + sum(AVG_REQUEST_UNIT_WRITE*exec_count) as sum_ru")
					require.NotContains(t, params.Fields, "sum(COALESCE(AVG_REQUEST_UNIT_V2,0)*exec_count) as sum_ru_v2")
					require.NotContains(t, params.Fields, "sum(COALESCE(AVG_REQUEST_UNIT_V2,0)*exec_count)/sum(exec_count) as avg_ru_v2")
					require.NotContains(t, params.Fields, "max(COALESCE(MAX_REQUEST_UNIT_V2,0)) as max_ru_v2")
					return &ListTableDataResponse{
						Data: []map[string]interface{}{
							{"plan_digest": "plan1", "sum_ru": 12.5},
						},
					}, nil
				}
			},
		},
		{
			name:  "Adapter error",
			query: "begin_time=1678886400&end_time=1678886400&digest=test_digest",
			headers: map[string]string{
				"X-Provider":    "aws",
				"X-Region":      "us-west-2",
				"X-Org-Id":      "tenant-1",
				"X-Project-Id":  "project-1",
				"X-Cluster-Id":  "cluster-1",
				"X-Deploy-Type": "dedicated",
			},
			expectedStatus: http.StatusInternalServerError,
			setupMock: func(m *mockDuckDBAdapter) {
				m.listTableDataFunc = func(ctx context.Context, params *ListTableDataParams) (*ListTableDataResponse, error) {
					return nil, errors.New("adapter error")
				}
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			testAdapter := &mockDuckDBAdapter{
				listTableFieldsFunc: func(ctx context.Context, params *ListTableFieldsParams) ([]FieldInfo, error) {
					return statementsPlansTestFieldInfos(), nil
				},
				listTableDataFunc: func(ctx context.Context, params *ListTableDataParams) (*ListTableDataResponse, error) {
					return &ListTableDataResponse{
						Data: []map[string]interface{}{
							{"plan_digest": "plan1"},
						},
					}, nil
				},
			}
			if tt.setupMock != nil {
				tt.setupMock(testAdapter)
			}
			testService := &Service{
				SQLsDuckDBAdapter: testAdapter,
				SQLsQuery:         testAdapter,
			}

			testRouter := gin.New()
			testRouter.GET("/statements/plans", testService.StatementsPlansHandler)

			req := httptest.NewRequest("GET", "/statements/plans?"+tt.query, nil)
			for k, v := range tt.headers {
				req.Header.Set(k, v)
			}

			w := httptest.NewRecorder()
			testRouter.ServeHTTP(w, req)

			require.Equal(t, tt.expectedStatus, w.Code)

			if tt.expectedStatus == http.StatusOK {
				var data []map[string]interface{}
				err := json.Unmarshal(w.Body.Bytes(), &data)
				require.NoError(t, err)
				if tt.name == "Uses coalesced plan_digest group by" {
					// v1 (no data_source=v2): empty plan_digest stays ""
					require.Equal(t, "", data[0]["plan_digest"])
				}
			}
		})
	}
}

func TestStatementsPlanDetailHandler(t *testing.T) {
	gin.SetMode(gin.TestMode)

	mockAdapter := &mockDuckDBAdapter{
		listTableDataFunc: func(ctx context.Context, params *ListTableDataParams) (*ListTableDataResponse, error) {
			return &ListTableDataResponse{
				Data: []map[string]interface{}{
					{"plan_digest": "plan1", "query_sample_text": "SELECT 1"},
				},
			}, nil
		},
	}
	service := &Service{
		SQLsDuckDBAdapter: mockAdapter,
		SQLsQuery:         mockAdapter,
	}

	router := gin.New()
	router.GET("/statements/plan/detail", service.StatementsPlanDetailHandler)

	tests := []struct {
		name           string
		query          string
		headers        map[string]string
		expectedStatus int
		setupMock      func(*mockDuckDBAdapter)
	}{
		{
			name:  "Successful request",
			query: "begin_time=1678886400&end_time=1678886400&digest=test_digest",
			headers: map[string]string{
				"X-Provider":    "aws",
				"X-Region":      "us-west-2",
				"X-Org-Id":      "tenant-1",
				"X-Project-Id":  "project-1",
				"X-Cluster-Id":  "cluster-1",
				"X-Deploy-Type": "dedicated",
			},
			expectedStatus: http.StatusOK,
		},
		{
			name:           "Missing headers",
			query:          "begin_time=1678886400&end_time=1678886400&digest=test_digest",
			headers:        map[string]string{},
			expectedStatus: http.StatusBadRequest,
		},
		{
			name:  "With plans and schema_name",
			query: "begin_time=1678886400&end_time=1678886400&digest=test_digest&plans=plan1,plan2&schema_name=test_schema",
			headers: map[string]string{
				"X-Provider":    "aws",
				"X-Region":      "us-west-2",
				"X-Org-Id":      "tenant-1",
				"X-Project-Id":  "project-1",
				"X-Cluster-Id":  "cluster-1",
				"X-Deploy-Type": "dedicated",
			},
			expectedStatus: http.StatusOK,
		},
		{
			name:  "With plans[] bracket suffix",
			query: "begin_time=1678886400&end_time=1678886400&digest=test_digest&plans[]=plan1&plans[]=plan2&data_source=v2",
			headers: map[string]string{
				"X-Provider":    "aws",
				"X-Region":      "us-west-2",
				"X-Org-Id":      "tenant-1",
				"X-Project-Id":  "project-1",
				"X-Cluster-Id":  "cluster-1",
				"X-Deploy-Type": "dedicated",
			},
			expectedStatus: http.StatusOK,
			setupMock: func(m *mockDuckDBAdapter) {
				m.listTableDataFunc = func(ctx context.Context, params *ListTableDataParams) (*ListTableDataResponse, error) {
					require.Contains(t, params.Filter, "coalesce(plan_digest, '') in ('plan1','plan2')")
					return &ListTableDataResponse{
						Data: []map[string]interface{}{
							{"plan_digest": "plan1", "query_sample_text": "SELECT 1"},
						},
					}, nil
				}
			},
		},
		{
			name:  "With empty plan digest via N/A",
			query: "begin_time=1678886400&end_time=1678886400&digest=test_digest&plans=N/A",
			headers: map[string]string{
				"X-Provider":    "aws",
				"X-Region":      "us-west-2",
				"X-Org-Id":      "tenant-1",
				"X-Project-Id":  "project-1",
				"X-Cluster-Id":  "cluster-1",
				"X-Deploy-Type": "dedicated",
			},
			expectedStatus: http.StatusOK,
			setupMock: func(m *mockDuckDBAdapter) {
				m.listTableDataFunc = func(ctx context.Context, params *ListTableDataParams) (*ListTableDataResponse, error) {
					require.Contains(t, params.Filter, "coalesce(plan_digest, '') = ''")
					return &ListTableDataResponse{
						Data: []map[string]interface{}{
							{"plan_digest": "", "query_sample_text": "SELECT 1"},
						},
					}, nil
				}
			},
		},
		{
			name:  "BYOC deploy type",
			query: "begin_time=1678886400&end_time=1678886400&digest=test_digest",
			headers: map[string]string{
				"X-Provider":    "aws",
				"X-Region":      "us-west-2",
				"X-Org-Id":      "tenant-1",
				"X-Project-Id":  "project-1",
				"X-Cluster-Id":  "cluster-1",
				"X-Deploy-Type": "byoc",
			},
			expectedStatus: http.StatusOK,
		},
		{
			name:  "v2 schema without avg_rocksdb columns uses sum-based exprs",
			query: "begin_time=1678886400&end_time=1678886400&digest=test_digest&data_source=v2",
			headers: map[string]string{
				"X-Provider":    "aws",
				"X-Region":      "us-west-2",
				"X-Org-Id":      "tenant-1",
				"X-Project-Id":  "project-1",
				"X-Cluster-Id":  "cluster-1",
				"X-Deploy-Type": "dedicated",
			},
			expectedStatus: http.StatusOK,
			setupMock: func(m *mockDuckDBAdapter) {
				m.listTableFieldsFunc = func(ctx context.Context, params *ListTableFieldsParams) ([]FieldInfo, error) {
					return []FieldInfo{
						{Name: "digest"},
						{Name: "exec_count"},
						{Name: "sum_rocksdb_block_read_count"},
						{Name: "max_rocksdb_block_read_count"},
						{Name: "sum_rocksdb_delete_skipped_count"},
						{Name: "max_rocksdb_delete_skipped_count"},
						{Name: "auth_users"},
						{Name: "begin"},
						{Name: "end"},
					}, nil
				}
				m.listTableDataFunc = func(ctx context.Context, params *ListTableDataParams) (*ListTableDataResponse, error) {
					require.Contains(t, params.Fields, "avg_rocksdb_block_read_count")
					require.NotContains(t, params.Fields, "sum(avg_rocksdb_block_read_count*exec_count)/sum(exec_count) as avg_rocksdb_block_read_count")
					require.Equal(t, "sum(sum_rocksdb_block_read_count)/sum(exec_count) as avg_rocksdb_block_read_count", sqlsFieldMap("v2")["avg_rocksdb_block_read_count"])
					return &ListTableDataResponse{
						Data: []map[string]interface{}{
							{"digest": "test_digest", "avg_rocksdb_block_read_count": 1.5},
						},
					}, nil
				}
			},
		},
		{
			name:  "Adapter error",
			query: "begin_time=1678886400&end_time=1678886400&digest=test_digest",
			headers: map[string]string{
				"X-Provider":    "aws",
				"X-Region":      "us-west-2",
				"X-Org-Id":      "tenant-1",
				"X-Project-Id":  "project-1",
				"X-Cluster-Id":  "cluster-1",
				"X-Deploy-Type": "dedicated",
			},
			expectedStatus: http.StatusInternalServerError,
			setupMock: func(m *mockDuckDBAdapter) {
				m.listTableDataFunc = func(ctx context.Context, params *ListTableDataParams) (*ListTableDataResponse, error) {
					return nil, errors.New("adapter error")
				}
			},
		},
		{
			name:  "Empty data array returns empty object",
			query: "begin_time=1678886400&end_time=1678886400&digest=test_digest",
			headers: map[string]string{
				"X-Provider":    "aws",
				"X-Region":      "us-west-2",
				"X-Org-Id":      "tenant-1",
				"X-Project-Id":  "project-1",
				"X-Cluster-Id":  "cluster-1",
				"X-Deploy-Type": "dedicated",
			},
			expectedStatus: http.StatusOK,
			setupMock: func(m *mockDuckDBAdapter) {
				m.listTableDataFunc = func(ctx context.Context, params *ListTableDataParams) (*ListTableDataResponse, error) {
					return &ListTableDataResponse{Data: []map[string]interface{}{}}, nil
				}
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			testAdapter := &mockDuckDBAdapter{
				listTableDataFunc: func(ctx context.Context, params *ListTableDataParams) (*ListTableDataResponse, error) {
					return &ListTableDataResponse{
						Data: []map[string]interface{}{
							{"plan_digest": "plan1"},
						},
					}, nil
				},
			}
			if tt.setupMock != nil {
				tt.setupMock(testAdapter)
			}
			testService := &Service{
				SQLsDuckDBAdapter: testAdapter,
				SQLsQuery:         testAdapter,
			}

			testRouter := gin.New()
			testRouter.GET("/statements/plan/detail", testService.StatementsPlanDetailHandler)

			req := httptest.NewRequest("GET", "/statements/plan/detail?"+tt.query, nil)
			for k, v := range tt.headers {
				req.Header.Set(k, v)
			}

			w := httptest.NewRecorder()
			testRouter.ServeHTTP(w, req)

			require.Equal(t, tt.expectedStatus, w.Code)

			if tt.expectedStatus == http.StatusOK && w.Body.Len() > 0 {
				var data map[string]interface{}
				err := json.Unmarshal(w.Body.Bytes(), &data)
				// If data is empty, unmarshal might fail, which is OK
				if err == nil {
					require.NotNil(t, data)
				}
			}
		})
	}
}

// Helper function to create a test request body
//
//nolint:unused // Test helper, kept for potential future use
func createJSONBody(t *testing.T, v interface{}) *bytes.Buffer {
	body, err := json.Marshal(v)
	require.NoError(t, err)
	return bytes.NewBuffer(body)
}
