//go:build integration

package rest_test

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"

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

	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
)

func TestCompositeHandler_RetrieveRecords_Strict(t *testing.T) {
	// Setup
	sm, handlers, r := SetupHandlerHelper(t)
	recordSvc := sm.Persistence

	// r (gin.Engine) from SetupHandlerHelper is fresh and doesn't have routes?
	// But handlers are created.
	// We need to register the route we are testing.
	r.GET("/services/data/v63.0/composite/sobjects", handlers.CompositeCollection.HandleCollectionsRetrieve)

	// Use System objects which are guaranteed to exist in metadata
	ctx := context.Background()
	timestamp := fmt.Sprintf("%d", time.Now().UnixNano())

	// 1. Create Profile
	profRec := models.SObject{
		"Name":        "Test Profile " + timestamp,
		"Description": "Test Generic Profile",
	}
	// Use Setup user session
	setupUser := &models.UserSession{
		ID:            "setup-user",
		Name:          "Setup User",
		ProfileID:     constants.ProfileSystemAdmin,
		IsSystemAdmin: true,
	}

	profRes, err := recordSvc.Insert(ctx, constants.TableProfile, profRec, setupUser)
	require.NoError(t, err)
	profId := profRes["Id"].(string)

	// 2. Create User
	// Minimal User fields
	userRec := models.SObject{
		"Username":  "testuser" + timestamp + "@example.com",
		"Email":     "testuser" + timestamp + "@example.com",
		"FirstName": "Strict",
		"LastName":  "MacTest",
		"Password":  "StrictPass123!",
		"ProfileId": profId, // Use the profile we just created
		"IsActive":  true,
	}
	userRes, err := recordSvc.Insert(ctx, constants.TableUser, userRec, setupUser)
	require.NoError(t, err)
	userId := userRes["Id"].(string)

	// IDs to retrieve
	idsToRetrieve := []string{profId, userId}

	// 2. Test Mixed Retrieve NO TYPE param
	t.Run("Mixed Types No Param", func(t *testing.T) {
		req, _ := http.NewRequest("GET", "/services/data/v63.0/composite/sobjects?ids="+strings.Join(idsToRetrieve, ","), nil)

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

		if w.Code != http.StatusOK {
			t.Logf("Response: %s", w.Body.String())
		}
		require.Equal(t, http.StatusOK, w.Code)

		var res []models.SObject
		err := json.Unmarshal(w.Body.Bytes(), &res)
		require.NoError(t, err)
		require.Len(t, res, 2)

		// Map results by ID
		resMap := make(map[string]models.SObject)
		for _, r := range res {
			if r != nil {
				var id string
				if val, ok := r["Id"]; ok {
					id = val.(string)
				} else if val, ok := r["id"]; ok {
					id = val.(string)
				}
				resMap[id] = r
			}
		}

		assert.Contains(t, resMap, profId)
		assert.Contains(t, resMap, userId)

		// Verify attributes.type
		getAttrType := func(r models.SObject) string {
			if attrs, ok := r["attributes"].(map[string]interface{}); ok {
				return attrs["type"].(string)
			}
			return ""
		}

		assert.Equal(t, constants.TableProfile, getAttrType(resMap[profId]))
		assert.Equal(t, constants.TableUser, getAttrType(resMap[userId]))
	})

	// 3. Test Invalid ID
	t.Run("Invalid ID ignored", func(t *testing.T) {
		req, _ := http.NewRequest("GET", "/services/data/v63.0/composite/sobjects?ids="+profId+",INVALID_ID", nil)
		w := httptest.NewRecorder()
		r.ServeHTTP(w, req)

		assert.Equal(t, http.StatusOK, w.Code)
		var res []interface{}
		json.Unmarshal(w.Body.Bytes(), &res)
		require.Len(t, res, 2)

		// Order matches: profId, INVALID_ID
		assert.NotNil(t, res[0])
		assert.Nil(t, res[1])
	})

	// 4. Test Error (Too many IDs)
	t.Run("Too Many IDs", func(t *testing.T) {
		manyIds := make([]string, 201)
		for i := 0; i < 201; i++ {
			manyIds[i] = "id"
		}

		req, _ := http.NewRequest("GET", "/services/data/v63.0/composite/sobjects?ids="+strings.Join(manyIds, ","), nil)
		w := httptest.NewRecorder()
		r.ServeHTTP(w, req)

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