package handlers

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

	"veza-backend-api/internal/services"

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
)

// MockSearchService implements SearchService interface for testing
type MockSearchService struct {
	mock.Mock
}

func (m *MockSearchService) Search(query string, types []string) (*services.SearchResult, error) {
	args := m.Called(query, types)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).(*services.SearchResult), args.Error(1)
}

func setupTestSearchRouter(mockService *MockSearchService) *gin.Engine {
	gin.SetMode(gin.TestMode)
	router := gin.New()

	handler := NewSearchHandlersWithInterface(mockService)

	api := router.Group("/api/v1")
	{
		api.GET("/search", handler.Search)
	}

	return router
}

func TestSearchHandlers_Search_Success(t *testing.T) {
	// Setup
	mockService := new(MockSearchService)
	router := setupTestSearchRouter(mockService)

	expectedResult := &services.SearchResult{
		Tracks: []services.TrackResult{
			{ID: "track-1", Title: "Test Track", Artist: "Test Artist", URL: "http://example.com/track-1"},
		},
		Users: []services.UserResult{
			{ID: "user-1", Username: "testuser", Avatar: "http://example.com/avatar.jpg"},
		},
		Playlists: []services.PlaylistResult{
			{ID: "playlist-1", Name: "Test Playlist", Cover: "http://example.com/cover.jpg"},
		},
	}

	mockService.On("Search", "test", []string(nil)).Return(expectedResult, nil)

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

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

func TestSearchHandlers_Search_WithTypes(t *testing.T) {
	// Setup
	mockService := new(MockSearchService)
	router := setupTestSearchRouter(mockService)

	expectedResult := &services.SearchResult{
		Tracks: []services.TrackResult{
			{ID: "track-1", Title: "Test Track", Artist: "Test Artist", URL: "http://example.com/track-1"},
		},
		Users:     []services.UserResult{},
		Playlists: []services.PlaylistResult{},
	}

	mockService.On("Search", "test", []string{"track"}).Return(expectedResult, nil)

	// Execute
	req, _ := http.NewRequest("GET", "/api/v1/search?q=test&type=track", nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

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

func TestSearchHandlers_Search_MissingQuery(t *testing.T) {
	// Setup
	mockService := new(MockSearchService)
	router := setupTestSearchRouter(mockService)

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

	// Assert
	assert.Equal(t, http.StatusBadRequest, w.Code)
	mockService.AssertNotCalled(t, "Search")
}

func TestSearchHandlers_Search_EmptyQuery(t *testing.T) {
	// Setup
	mockService := new(MockSearchService)
	router := setupTestSearchRouter(mockService)

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

	// Assert
	assert.Equal(t, http.StatusBadRequest, w.Code)
	mockService.AssertNotCalled(t, "Search")
}

func TestSearchHandlers_Search_ServiceError(t *testing.T) {
	// Setup
	mockService := new(MockSearchService)
	router := setupTestSearchRouter(mockService)

	mockService.On("Search", "test", []string(nil)).Return(nil, assert.AnError)

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

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

func TestSearchHandlers_Search_MultipleTypes(t *testing.T) {
	// Setup
	mockService := new(MockSearchService)
	router := setupTestSearchRouter(mockService)

	expectedResult := &services.SearchResult{
		Tracks: []services.TrackResult{
			{ID: "track-1", Title: "Test Track", Artist: "Test Artist", URL: "http://example.com/track-1"},
		},
		Users: []services.UserResult{
			{ID: "user-1", Username: "testuser", Avatar: "http://example.com/avatar.jpg"},
		},
		Playlists: []services.PlaylistResult{},
	}

	mockService.On("Search", "test", []string{"track", "user"}).Return(expectedResult, nil)

	// Execute
	req, _ := http.NewRequest("GET", "/api/v1/search?q=test&type=track&type=user", nil)
	w := httptest.NewRecorder()
	router.ServeHTTP(w, req)

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

func TestNewSearchHandlers(t *testing.T) {
	// Setup
	mockService := &services.SearchService{}

	// Execute
	NewSearchHandlers(mockService)

	// Assert
	assert.NotNil(t, SearchHandlersInstance)
	assert.NotNil(t, SearchHandlersInstance.searchService)
}
