package middleware

import (
	"context"
	"errors"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/gin-gonic/gin"
	"github.com/nexuscrm/backend/pkg/apierr"
	"github.com/nexuscrm/backend/pkg/logging"
	"github.com/stretchr/testify/assert"
)

func TestGlobalErrorHandler(t *testing.T) {
	gin.SetMode(gin.TestMode)
	logging.InitLogger()
	logger := logging.New(context.Background())

	t.Run("Handle API Error", func(t *testing.T) {
		r := gin.New()
		r.Use(GlobalErrorHandler(logger))

		r.GET("/err", func(c *gin.Context) {
			c.Error(apierr.NotFound("Item not found"))
		})

		w := httptest.NewRecorder()
		req, _ := http.NewRequest("GET", "/err", nil)
		r.ServeHTTP(w, req)

		assert.Equal(t, http.StatusNotFound, w.Code)
		assert.Contains(t, w.Body.String(), "ResourceNotFound")
		assert.Contains(t, w.Body.String(), "Item not found")
	})

	t.Run("Handle Generic Error", func(t *testing.T) {
		r := gin.New()
		r.Use(GlobalErrorHandler(logger))

		r.GET("/panic", func(c *gin.Context) {
			c.Error(errors.New("database failure"))
		})

		w := httptest.NewRecorder()
		req, _ := http.NewRequest("GET", "/panic", nil)
		r.ServeHTTP(w, req)

		assert.Equal(t, http.StatusInternalServerError, w.Code)
		assert.Contains(t, w.Body.String(), "InternalServerError")
		assert.Contains(t, w.Body.String(), "An unexpected error occurred")
	})

	t.Run("Handle API Error With Cause", func(t *testing.T) {
		r := gin.New()
		r.Use(GlobalErrorHandler(logger))

		r.GET("/err-cause", func(c *gin.Context) {
			apiErr := apierr.Internal(errors.New("underlying db error"))
			c.Error(apiErr)
		})

		w := httptest.NewRecorder()
		req, _ := http.NewRequest("GET", "/err-cause", nil)
		r.ServeHTTP(w, req)

		assert.Equal(t, http.StatusInternalServerError, w.Code)
		assert.Contains(t, w.Body.String(), "InternalServerError")
	})

	t.Run("Handle Salesforce API Error", func(t *testing.T) {
		r := gin.New()
		r.Use(GlobalErrorHandler(logger))

		r.GET("/sf-err", func(c *gin.Context) {
			c.Error(apierr.NewSalesforce(http.StatusBadRequest, apierr.SFCodeInvalidCrossReferenceKey, "layout mismatch"))
		})

		w := httptest.NewRecorder()
		req, _ := http.NewRequest("GET", "/sf-err", nil)
		r.ServeHTTP(w, req)

		assert.Equal(t, http.StatusBadRequest, w.Code)
		assert.Contains(t, w.Body.String(), apierr.SFCodeInvalidCrossReferenceKey)
		assert.Contains(t, w.Body.String(), "layout mismatch")
	})

	t.Run("No Errors Passes Through", func(t *testing.T) {
		r := gin.New()
		r.Use(GlobalErrorHandler(logger))

		r.GET("/ok", func(c *gin.Context) {
			c.String(http.StatusOK, "success")
		})

		w := httptest.NewRecorder()
		req, _ := http.NewRequest("GET", "/ok", nil)
		r.ServeHTTP(w, req)

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