package handlers

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

	"veza-backend-api/internal/config"

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	"go.uber.org/zap"
)

// MockConfigReloader mocks ConfigReloader
type MockConfigReloader struct {
	mock.Mock
}

func (m *MockConfigReloader) ReloadAll() error {
	args := m.Called()
	return args.Error(0)
}

func (m *MockConfigReloader) ReloadLogLevel() error {
	args := m.Called()
	return args.Error(0)
}

func (m *MockConfigReloader) ReloadRateLimits() error {
	args := m.Called()
	return args.Error(0)
}

func (m *MockConfigReloader) GetCurrentConfig() *config.ReloadableConfig {
	args := m.Called()
	if args.Get(0) == nil {
		return nil
	}
	return args.Get(0).(*config.ReloadableConfig)
}

func setupTestConfigReloadRouter(mockReloader *MockConfigReloader) *gin.Engine {
	gin.SetMode(gin.TestMode)
	router := gin.New()

	logger := zap.NewNop()
	handler := NewConfigReloadHandlerWithInterface(mockReloader, logger)

	api := router.Group("/api/v1/config")
	{
		api.POST("/reload", handler.ReloadConfig())
		api.GET("/", handler.GetConfig())
	}

	return router
}

func TestConfigReloadHandler_ReloadConfig_All(t *testing.T) {
	// Setup
	mockReloader := new(MockConfigReloader)
	router := setupTestConfigReloadRouter(mockReloader)

	reqBody := map[string]string{"type": "all"}
	expectedConfig := &config.ReloadableConfig{
		LogLevel:        "info",
		RateLimitLimit:  100,
		RateLimitWindow: 60,
	}

	mockReloader.On("ReloadAll").Return(nil)
	mockReloader.On("GetCurrentConfig").Return(expectedConfig)

	body, _ := json.Marshal(reqBody)

	// Execute
	req, _ := http.NewRequest("POST", "/api/v1/config/reload", bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	// Assert
	assert.Equal(t, http.StatusOK, w.Code)
	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(t, err)
	assert.True(t, response["success"].(bool))
	mockReloader.AssertExpectations(t)
}

func TestConfigReloadHandler_ReloadConfig_LogLevel(t *testing.T) {
	// Setup
	mockReloader := new(MockConfigReloader)
	router := setupTestConfigReloadRouter(mockReloader)

	reqBody := map[string]string{"type": "log_level"}
	expectedConfig := &config.ReloadableConfig{
		LogLevel:        "debug",
		RateLimitLimit:  100,
		RateLimitWindow: 60,
	}

	mockReloader.On("ReloadLogLevel").Return(nil)
	mockReloader.On("GetCurrentConfig").Return(expectedConfig)

	body, _ := json.Marshal(reqBody)

	// Execute
	req, _ := http.NewRequest("POST", "/api/v1/config/reload", bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	// Assert
	assert.Equal(t, http.StatusOK, w.Code)
	mockReloader.AssertExpectations(t)
}

func TestConfigReloadHandler_ReloadConfig_RateLimits(t *testing.T) {
	// Setup
	mockReloader := new(MockConfigReloader)
	router := setupTestConfigReloadRouter(mockReloader)

	reqBody := map[string]string{"type": "rate_limits"}
	expectedConfig := &config.ReloadableConfig{
		LogLevel:        "info",
		RateLimitLimit:  200,
		RateLimitWindow: 120,
	}

	mockReloader.On("ReloadRateLimits").Return(nil)
	mockReloader.On("GetCurrentConfig").Return(expectedConfig)

	body, _ := json.Marshal(reqBody)

	// Execute
	req, _ := http.NewRequest("POST", "/api/v1/config/reload", bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	// Assert
	assert.Equal(t, http.StatusOK, w.Code)
	mockReloader.AssertExpectations(t)
}

func TestConfigReloadHandler_ReloadConfig_InvalidType(t *testing.T) {
	// Setup
	mockReloader := new(MockConfigReloader)
	router := setupTestConfigReloadRouter(mockReloader)

	reqBody := map[string]string{"type": "invalid"}

	body, _ := json.Marshal(reqBody)

	// Execute
	req, _ := http.NewRequest("POST", "/api/v1/config/reload", bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	// Assert
	assert.Equal(t, http.StatusBadRequest, w.Code)
	mockReloader.AssertNotCalled(t, "ReloadAll")
	mockReloader.AssertNotCalled(t, "ReloadLogLevel")
	mockReloader.AssertNotCalled(t, "ReloadRateLimits")
}

func TestConfigReloadHandler_ReloadConfig_ServiceError(t *testing.T) {
	// Setup
	mockReloader := new(MockConfigReloader)
	router := setupTestConfigReloadRouter(mockReloader)

	reqBody := map[string]string{"type": "all"}

	mockReloader.On("ReloadAll").Return(assert.AnError)

	body, _ := json.Marshal(reqBody)

	// Execute
	req, _ := http.NewRequest("POST", "/api/v1/config/reload", bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	// Assert
	assert.Equal(t, http.StatusInternalServerError, w.Code)
	mockReloader.AssertExpectations(t)
}

func TestConfigReloadHandler_GetConfig_Success(t *testing.T) {
	// Setup
	mockReloader := new(MockConfigReloader)
	router := setupTestConfigReloadRouter(mockReloader)

	expectedConfig := &config.ReloadableConfig{
		LogLevel:        "info",
		RateLimitLimit:  100,
		RateLimitWindow: 60,
	}

	mockReloader.On("GetCurrentConfig").Return(expectedConfig)

	// Execute
	req, _ := http.NewRequest("GET", "/api/v1/config/", nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	// Assert
	assert.Equal(t, http.StatusOK, w.Code)
	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(t, err)
	assert.True(t, response["success"].(bool))
	mockReloader.AssertExpectations(t)
}
