package rest

import (
	"testing"

	"github.com/nexuscrm/shared/pkg/models"
	"github.com/stretchr/testify/assert"
)

// ---------------------------------------------------------------------------
// normalizeEnum — generic enum normalizer
// ---------------------------------------------------------------------------

func TestNormalizeEnum(t *testing.T) {
	mapping := map[string]string{
		"alpha": "Alpha",
		"beta":  "Beta",
	}

	tests := []struct {
		name     string
		input    string
		fallback string
		expected string
	}{
		{"exact lowercase match", "alpha", "Default", "Alpha"},
		{"uppercase match", "ALPHA", "Default", "Alpha"},
		{"mixed case match", "AlPhA", "Default", "Alpha"},
		{"second value", "beta", "Default", "Beta"},
		{"unknown returns fallback", "gamma", "Default", "Default"},
		{"empty string returns fallback", "", "Default", "Default"},
	}

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

// ---------------------------------------------------------------------------
// toFolderResponse — SObject → FolderResponse mapping
// ---------------------------------------------------------------------------

func TestToFolderResponse_FullRecord(t *testing.T) {
	record := models.SObject{
		"Id":               "folder-001",
		"Name":             "Sales Reports",
		"DeveloperName":    "Sales_Reports",
		"Description":      "Folder for sales team",
		"Type":             "Report",
		"AccessType":       "Shared",
		"ParentId":         "parent-001",
		"CreatedDate":      "2024-01-01T00:00:00Z",
		"LastModifiedDate": "2024-01-15T00:00:00Z",
	}

	resp := toFolderResponse(record)

	assert.Equal(t, "folder-001", resp.ID)
	assert.Equal(t, "Sales Reports", resp.Name)
	assert.Equal(t, "Sales Reports", resp.Label, "Label should equal Name")
	assert.Equal(t, "Sales_Reports", resp.DeveloperName)
	assert.Equal(t, "Folder for sales team", resp.Description)
	assert.Equal(t, "Report", resp.Type)
	assert.Equal(t, "Shared", resp.AccessType)
	assert.Equal(t, "parent-001", resp.ParentFolderID)
	assert.Equal(t, "2024-01-01T00:00:00Z", resp.CreatedDate)
	assert.Equal(t, "2024-01-15T00:00:00Z", resp.LastModifiedDate)
}

func TestToFolderResponse_EmptyRecord(t *testing.T) {
	record := models.SObject{}

	resp := toFolderResponse(record)

	assert.Equal(t, "", resp.ID)
	assert.Equal(t, "", resp.Name)
	assert.Equal(t, "Report", resp.Type, "Default type should be Report")
	assert.Equal(t, "Hidden", resp.AccessType, "Default access type should be Hidden")
}

func TestToFolderResponse_DefaultType(t *testing.T) {
	record := models.SObject{
		"Id":   "folder-002",
		"Name": "No Type Folder",
		// Type is missing
	}

	resp := toFolderResponse(record)
	assert.Equal(t, "Report", resp.Type, "Missing type should default to Report")
}

func TestToFolderResponse_DefaultAccessType(t *testing.T) {
	record := models.SObject{
		"Id":   "folder-003",
		"Name": "No Access Folder",
		// AccessType is missing
	}

	resp := toFolderResponse(record)
	assert.Equal(t, "Hidden", resp.AccessType, "Missing access type should default to Hidden")
}

func TestToFolderResponse_NilValues(t *testing.T) {
	record := models.SObject{
		"Id":          nil,
		"Name":        nil,
		"Type":        nil,
		"AccessType":  nil,
		"Description": nil,
	}

	resp := toFolderResponse(record)

	assert.Equal(t, "", resp.ID)
	assert.Equal(t, "", resp.Name)
	assert.Equal(t, "Report", resp.Type, "Nil type should default to Report")
	assert.Equal(t, "Hidden", resp.AccessType, "Nil access type should default to Hidden")
}

func TestToFolderResponse_NonStringValues(t *testing.T) {
	// Edge case: numeric values instead of strings
	record := models.SObject{
		"Id":   123,
		"Name": true,
	}

	resp := toFolderResponse(record)
	assert.Equal(t, "", resp.ID, "Non-string values should result in empty string")
	assert.Equal(t, "", resp.Name)
}

// ---------------------------------------------------------------------------
// paginateRecords — pagination helper
// ---------------------------------------------------------------------------

func TestPaginateRecords_FirstPage(t *testing.T) {
	records := make([]models.SObject, 10)
	for i := range records {
		records[i] = models.SObject{"Id": i}
	}

	result, hasNext := paginateRecords(records, 0, 5)
	assert.Len(t, result, 5)
	assert.True(t, hasNext)
}

func TestPaginateRecords_LastPage(t *testing.T) {
	records := make([]models.SObject, 10)
	for i := range records {
		records[i] = models.SObject{"Id": i}
	}

	// When end exactly equals len(records), the implementation considers
	// there might be a next page (conservative approach).
	result, hasNext := paginateRecords(records, 1, 5)
	assert.Len(t, result, 5)
	assert.True(t, hasNext, "end == len triggers hasNextPage=true in implementation")
}

func TestPaginateRecords_PartialLastPage(t *testing.T) {
	records := make([]models.SObject, 7)
	for i := range records {
		records[i] = models.SObject{"Id": i}
	}

	result, hasNext := paginateRecords(records, 1, 5)
	assert.Len(t, result, 2)
	assert.False(t, hasNext)
}

func TestPaginateRecords_BeyondRange(t *testing.T) {
	records := make([]models.SObject, 5)
	for i := range records {
		records[i] = models.SObject{"Id": i}
	}

	result, hasNext := paginateRecords(records, 5, 5) // page=5, way beyond
	assert.Empty(t, result)
	assert.False(t, hasNext)
}

func TestPaginateRecords_EmptyInput(t *testing.T) {
	result, hasNext := paginateRecords(nil, 0, 10)
	assert.Empty(t, result)
	assert.False(t, hasNext)
}

func TestPaginateRecords_ExactFit(t *testing.T) {
	records := make([]models.SObject, 10)
	for i := range records {
		records[i] = models.SObject{"Id": i}
	}

	// When end exactly equals len(records), implementation returns hasNextPage=true.
	result, hasNext := paginateRecords(records, 0, 10)
	assert.Len(t, result, 10)
	assert.True(t, hasNext, "end == len triggers hasNextPage=true in implementation")
}

func TestPaginateRecords_SingleItem(t *testing.T) {
	records := []models.SObject{{"Id": "abc"}}

	result, hasNext := paginateRecords(records, 0, 10)
	assert.Len(t, result, 1)
	assert.False(t, hasNext)
}
