package handlers

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

	"veza-backend-api/internal/models"
	"veza-backend-api/internal/services"

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

// MockChatServiceForChatHandler mocks ChatService
type MockChatServiceForChatHandler struct {
	mock.Mock
}

func (m *MockChatServiceForChatHandler) GenerateToken(userID uuid.UUID, username string) (*services.ChatTokenResponse, error) {
	args := m.Called(userID, username)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).(*services.ChatTokenResponse), args.Error(1)
}

func (m *MockChatServiceForChatHandler) GetStats(ctx context.Context) (*services.ChatStats, error) {
	args := m.Called(ctx)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).(*services.ChatStats), args.Error(1)
}

// MockUserServiceForChatHandler mocks UserService
type MockUserServiceForChatHandler struct {
	mock.Mock
}

func (m *MockUserServiceForChatHandler) GetByID(userID uuid.UUID) (*models.User, error) {
	args := m.Called(userID)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).(*models.User), args.Error(1)
}

func setupTestChatRouter(mockChatService *MockChatServiceForChatHandler, mockUserService *MockUserServiceForChatHandler) *gin.Engine {
	gin.SetMode(gin.TestMode)
	router := gin.New()

	logger := zap.NewNop()
	handler := NewChatHandlerWithInterface(mockChatService, mockUserService, logger)

	api := router.Group("/api/v1/chat")
	api.Use(func(c *gin.Context) {
		userIDStr := c.GetHeader("X-User-ID")
		if userIDStr != "" {
			uid, err := uuid.Parse(userIDStr)
			if err == nil {
				c.Set("user_id", uid)
			}
		}
		c.Next()
	})
	{
		api.GET("/token", handler.GetToken)
		api.GET("/stats", handler.GetStats)
	}

	return router
}

func TestChatHandler_GetToken_Success(t *testing.T) {
	// Setup
	mockChatService := new(MockChatServiceForChatHandler)
	mockUserService := new(MockUserServiceForChatHandler)
	router := setupTestChatRouter(mockChatService, mockUserService)

	userID := uuid.New()
	username := "testuser"
	expectedToken := &services.ChatTokenResponse{
		Token: "test-token-123",
	}

	user := &models.User{
		ID:       userID,
		Username: username,
	}

	mockUserService.On("GetByID", userID).Return(user, nil)
	mockChatService.On("GenerateToken", userID, username).Return(expectedToken, nil)

	// Execute
	req, _ := http.NewRequest("GET", "/api/v1/chat/token", nil)
	req.Header.Set("X-User-ID", userID.String())
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

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

func TestChatHandler_GetToken_Unauthorized(t *testing.T) {
	// Setup
	mockChatService := new(MockChatServiceForChatHandler)
	mockUserService := new(MockUserServiceForChatHandler)
	router := setupTestChatRouter(mockChatService, mockUserService)

	// Execute - No X-User-ID header
	req, _ := http.NewRequest("GET", "/api/v1/chat/token", nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

	// Assert
	assert.Equal(t, http.StatusUnauthorized, w.Code)
	mockChatService.AssertNotCalled(t, "GenerateToken")
	mockUserService.AssertNotCalled(t, "GetByID")
}

func TestChatHandler_GetToken_UserServiceError(t *testing.T) {
	// Setup
	mockChatService := new(MockChatServiceForChatHandler)
	mockUserService := new(MockUserServiceForChatHandler)
	router := setupTestChatRouter(mockChatService, mockUserService)

	userID := uuid.New()
	expectedToken := &services.ChatTokenResponse{
		Token: "test-token-123",
	}

	// UserService returns error, handler should use fallback username
	mockUserService.On("GetByID", userID).Return(nil, assert.AnError)
	mockChatService.On("GenerateToken", userID, mock.MatchedBy(func(username string) bool {
		return username == "user_"+userID.String()
	})).Return(expectedToken, nil)

	// Execute
	req, _ := http.NewRequest("GET", "/api/v1/chat/token", nil)
	req.Header.Set("X-User-ID", userID.String())
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

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

func TestChatHandler_GetToken_ChatServiceError(t *testing.T) {
	// Setup
	mockChatService := new(MockChatServiceForChatHandler)
	mockUserService := new(MockUserServiceForChatHandler)
	router := setupTestChatRouter(mockChatService, mockUserService)

	userID := uuid.New()
	username := "testuser"

	user := &models.User{
		ID:       userID,
		Username: username,
	}

	mockUserService.On("GetByID", userID).Return(user, nil)
	mockChatService.On("GenerateToken", userID, username).Return(nil, assert.AnError)

	// Execute
	req, _ := http.NewRequest("GET", "/api/v1/chat/token", nil)
	req.Header.Set("X-User-ID", userID.String())
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

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

func TestChatHandler_GetStats_Success(t *testing.T) {
	// Setup
	mockChatService := new(MockChatServiceForChatHandler)
	mockUserService := new(MockUserServiceForChatHandler)
	router := setupTestChatRouter(mockChatService, mockUserService)

	expectedStats := &services.ChatStats{
		TotalMessages: 100,
		ActiveUsers:   10,
	}

	mockChatService.On("GetStats", mock.Anything).Return(expectedStats, nil)

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

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

func TestChatHandler_GetStats_ServiceError(t *testing.T) {
	// Setup
	mockChatService := new(MockChatServiceForChatHandler)
	mockUserService := new(MockUserServiceForChatHandler)
	router := setupTestChatRouter(mockChatService, mockUserService)

	mockChatService.On("GetStats", mock.Anything).Return(nil, assert.AnError)

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

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