package rest_test

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

	"github.com/gin-gonic/gin"
	"github.com/nexuscrm/backend/internal/interfaces/rest"
	"github.com/stretchr/testify/assert"
)

// ---------------------------------------------------------------------------
// HandleCollectionsCreate — input validation
// ---------------------------------------------------------------------------

func TestCompositeCollectionsHandler_HandleCollectionsCreate_InvalidJSON(t *testing.T) {
	handler := rest.NewCompositeCollectionsHandler(nil, nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest("POST", "/composite/sobjects", bytes.NewReader([]byte(`{bad`)))
	c.Request.Header.Set("Content-Type", "application/json")

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

func TestCompositeCollectionsHandler_HandleCollectionsCreate_TooManyRecords(t *testing.T) {
	handler := rest.NewCompositeCollectionsHandler(nil, nil)

	// Build 201 records
	records := make([]map[string]interface{}, 201)
	for i := range records {
		records[i] = map[string]interface{}{"Name": "test"}
	}
	body, _ := json.Marshal(map[string]interface{}{
		"allOrNone": false,
		"records":   records,
	})

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest("POST", "/composite/sobjects", bytes.NewReader(body))
	c.Request.Header.Set("Content-Type", "application/json")

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

func TestCompositeCollectionsHandler_HandleCollectionsCreate_Unauthenticated(t *testing.T) {
	handler := rest.NewCompositeCollectionsHandler(nil, nil)

	body, _ := json.Marshal(map[string]interface{}{
		"allOrNone": false,
		"records":   []map[string]interface{}{{"Name": "test"}},
	})

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest("POST", "/composite/sobjects", bytes.NewReader(body))
	c.Request.Header.Set("Content-Type", "application/json")

	assert.Panics(t, func() { handler.HandleCollectionsCreate(c) })
}

// ---------------------------------------------------------------------------
// HandleCollectionsUpdate — input validation
// ---------------------------------------------------------------------------

func TestCompositeCollectionsHandler_HandleCollectionsUpdate_InvalidJSON(t *testing.T) {
	handler := rest.NewCompositeCollectionsHandler(nil, nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest("PATCH", "/composite/sobjects", bytes.NewReader([]byte(`invalid`)))
	c.Request.Header.Set("Content-Type", "application/json")

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

func TestCompositeCollectionsHandler_HandleCollectionsUpdate_TooManyRecords(t *testing.T) {
	handler := rest.NewCompositeCollectionsHandler(nil, nil)

	records := make([]map[string]interface{}, 201)
	for i := range records {
		records[i] = map[string]interface{}{"Name": "test"}
	}
	body, _ := json.Marshal(map[string]interface{}{
		"allOrNone": false,
		"records":   records,
	})

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest("PATCH", "/composite/sobjects", bytes.NewReader(body))
	c.Request.Header.Set("Content-Type", "application/json")

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

func TestCompositeCollectionsHandler_HandleCollectionsUpdate_Unauthenticated(t *testing.T) {
	handler := rest.NewCompositeCollectionsHandler(nil, nil)

	body, _ := json.Marshal(map[string]interface{}{
		"allOrNone": false,
		"records":   []map[string]interface{}{{"Name": "test"}},
	})

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest("PATCH", "/composite/sobjects", bytes.NewReader(body))
	c.Request.Header.Set("Content-Type", "application/json")

	assert.Panics(t, func() { handler.HandleCollectionsUpdate(c) })
}

// ---------------------------------------------------------------------------
// HandleCollectionsDelete — input validation
// ---------------------------------------------------------------------------

func TestCompositeCollectionsHandler_HandleCollectionsDelete_MissingIds(t *testing.T) {
	handler := rest.NewCompositeCollectionsHandler(nil, nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest("DELETE", "/composite/sobjects", nil)

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

func TestCompositeCollectionsHandler_HandleCollectionsDelete_TooManyIds(t *testing.T) {
	handler := rest.NewCompositeCollectionsHandler(nil, nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)

	// Generate 201 IDs
	ids := ""
	for i := 0; i < 201; i++ {
		if i > 0 {
			ids += ","
		}
		ids += "id" + string(rune('0'+i%10))
	}
	c.Request = httptest.NewRequest("DELETE", "/composite/sobjects?ids="+ids, nil)

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

func TestCompositeCollectionsHandler_HandleCollectionsDelete_Unauthenticated(t *testing.T) {
	handler := rest.NewCompositeCollectionsHandler(nil, nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest("DELETE", "/composite/sobjects?ids=001,002,003", nil)

	assert.Panics(t, func() { handler.HandleCollectionsDelete(c) })
}

// ---------------------------------------------------------------------------
// HandleCollectionsRetrieve — input validation
// ---------------------------------------------------------------------------

func TestCompositeCollectionsHandler_HandleCollectionsRetrieve_MissingIds(t *testing.T) {
	handler := rest.NewCompositeCollectionsHandler(nil, nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest("GET", "/composite/sobjects", nil)

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

func TestCompositeCollectionsHandler_HandleCollectionsRetrieve_TooManyIds(t *testing.T) {
	handler := rest.NewCompositeCollectionsHandler(nil, nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)

	ids := ""
	for i := 0; i < 201; i++ {
		if i > 0 {
			ids += ","
		}
		ids += "id"
	}
	c.Request = httptest.NewRequest("GET", "/composite/sobjects?ids="+ids, nil)

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

func TestCompositeCollectionsHandler_HandleCollectionsRetrieve_Unauthenticated(t *testing.T) {
	handler := rest.NewCompositeCollectionsHandler(nil, nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest("GET", "/composite/sobjects?ids=001,002", nil)

	assert.Panics(t, func() { handler.HandleCollectionsRetrieve(c) })
}

// ---------------------------------------------------------------------------
// HandleCollectionsUpsert — input validation
// ---------------------------------------------------------------------------

func TestCompositeCollectionsHandler_HandleCollectionsUpsert_MissingParams(t *testing.T) {
	handler := rest.NewCompositeCollectionsHandler(nil, nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest("PATCH", "/composite/sobjects//", nil)
	c.Params = gin.Params{
		{Key: "sObjectName", Value: ""},
		{Key: "externalIdFieldName", Value: ""},
	}

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

func TestCompositeCollectionsHandler_HandleCollectionsUpsert_InvalidJSON(t *testing.T) {
	handler := rest.NewCompositeCollectionsHandler(nil, nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest("PATCH", "/composite/sobjects/Account/ExtId__c", bytes.NewReader([]byte(`{bad`)))
	c.Request.Header.Set("Content-Type", "application/json")
	c.Params = gin.Params{
		{Key: "sObjectName", Value: "Account"},
		{Key: "externalIdFieldName", Value: "ExtId__c"},
	}

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

func TestCompositeCollectionsHandler_HandleCollectionsUpsert_TooManyRecords(t *testing.T) {
	handler := rest.NewCompositeCollectionsHandler(nil, nil)

	records := make([]map[string]interface{}, 201)
	for i := range records {
		records[i] = map[string]interface{}{"Name": "test"}
	}
	body, _ := json.Marshal(map[string]interface{}{
		"allOrNone": false,
		"records":   records,
	})

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest("PATCH", "/composite/sobjects/Account/ExtId__c", bytes.NewReader(body))
	c.Request.Header.Set("Content-Type", "application/json")
	c.Params = gin.Params{
		{Key: "sObjectName", Value: "Account"},
		{Key: "externalIdFieldName", Value: "ExtId__c"},
	}

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

func TestCompositeCollectionsHandler_HandleCollectionsUpsert_Unauthenticated(t *testing.T) {
	handler := rest.NewCompositeCollectionsHandler(nil, nil)

	body, _ := json.Marshal(map[string]interface{}{
		"allOrNone": false,
		"records":   []map[string]interface{}{{"Name": "test"}},
	})

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = httptest.NewRequest("PATCH", "/composite/sobjects/Account/ExtId__c", bytes.NewReader(body))
	c.Request.Header.Set("Content-Type", "application/json")
	c.Params = gin.Params{
		{Key: "sObjectName", Value: "Account"},
		{Key: "externalIdFieldName", Value: "ExtId__c"},
	}

	assert.Panics(t, func() { handler.HandleCollectionsUpsert(c) })
}
