package handlers

import (
	"bytes"
	"encoding/json"
	"github.com/google/uuid"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"go.uber.org/zap"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
	"veza-backend-api/internal/models"
	"veza-backend-api/internal/services"
)

func setupTestPlaylistHandlers(t *testing.T) (*services.PlaylistService, *gorm.DB, func()) {
	gin.SetMode(gin.TestMode)

	// Setup in-memory SQLite database
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	assert.NoError(t, err)

	// Enable foreign keys for SQLite
	db.Exec("PRAGMA foreign_keys = ON")

	// Auto-migrate
	err = db.AutoMigrate(&models.User{}, &models.Track{}, &models.Playlist{}, &models.PlaylistTrack{}, &models.PlaylistCollaborator{})
	assert.NoError(t, err)

	// Create test user
	user := &models.User{
		Username:     "testuser",
		Email:        "test@example.com",
		PasswordHash: "hash",
		Slug:         "testuser",
		IsActive:     true,
		CreatedAt:    time.Now(),
	}
	err = db.Create(user).Error
	assert.NoError(t, err)

	// Setup logger
	logger := zap.NewNop()

	// Setup service
	playlistService := services.NewPlaylistServiceWithDB(db, logger)

	// Cleanup function
	cleanup := func() {
		// Database will be closed automatically
	}

	return playlistService, db, cleanup
}

func TestHandlers_CreatePlaylist_Success(t *testing.T) {
	service, _, cleanup := setupTestPlaylistHandlers(t)
	defer cleanup()

	// Use local struct matching the handler implementation
	type CreatePlaylistRequest struct {
		Title       string `json:"title"`
		Description string `json:"description,omitempty"`
		IsPublic    bool   `json:"is_public"`
	}

	reqBody := CreatePlaylistRequest{
		Title:       "My Playlist",
		Description: "A test playlist",
		IsPublic:    true,
	}
	body, _ := json.Marshal(reqBody)

	req := httptest.NewRequest("POST", "/api/v1/playlists", bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	c, _ := gin.CreateTestContext(httptest.NewRecorder())
	c.Request = req
	c.Set("user_id", 1) // Set user_id as int

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("user_id", 1) // Set user_id as int

	CreatePlaylist(service)(c)

	assert.Equal(t, http.StatusCreated, w.Code)
	var response map[string]interface{}
	json.Unmarshal(w.Body.Bytes(), &response)
	assert.NotNil(t, response["playlist"])
}

func TestHandlers_GetPlaylists_Success(t *testing.T) {
	service, db, cleanup := setupTestPlaylistHandlers(t)
	defer cleanup()

	// Create test playlists
	playlist1 := &models.Playlist{
		UserID:    1,
		Title:     "Public Playlist",
		IsPublic:  true,
		CreatedAt: time.Now(),
	}
	db.Create(playlist1)

	req := httptest.NewRequest("GET", "/api/v1/playlists", nil)
	c, _ := gin.CreateTestContext(httptest.NewRecorder())
	c.Request = req
	c.Set("user_id", 1)

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("user_id", 1)

	GetPlaylists(service)(c)

	assert.Equal(t, http.StatusOK, w.Code)
	var response map[string]interface{}
	json.Unmarshal(w.Body.Bytes(), &response)
	assert.NotNil(t, response["playlists"])
}

func TestHandlers_GetPlaylist_Success(t *testing.T) {
	service, db, cleanup := setupTestPlaylistHandlers(t)
	defer cleanup()

	// Create test playlist
	playlist := &models.Playlist{
		UserID:    1,
		Title:     "My Playlist",
		IsPublic:  true,
		CreatedAt: time.Now(),
	}
	db.Create(playlist)

	req := httptest.NewRequest("GET", "/api/v1/playlists/1", nil)
	c, _ := gin.CreateTestContext(httptest.NewRecorder())
	c.Request = req
	c.Set("user_id", 1)
	c.Params = gin.Params{gin.Param{Key: "id", Value: "1"}}

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("user_id", 1)
	c.Params = gin.Params{gin.Param{Key: "id", Value: "1"}}

	GetPlaylist(service)(c)

	assert.Equal(t, http.StatusOK, w.Code)
	var response map[string]interface{}
	json.Unmarshal(w.Body.Bytes(), &response)
	assert.NotNil(t, response["playlist"])
}

func TestHandlers_AddTrack_Success(t *testing.T) {
	service, db, cleanup := setupTestPlaylistHandlers(t)
	defer cleanup()

	// Create test track
	track := &models.Track{
		UserID:    1,
		Title:     "Test Track",
		FilePath:  "/test/track.mp3",
		Format:    "mp3",
		IsPublic:  true,
		CreatedAt: time.Now(),
	}
	db.Create(track)

	// Create test playlist
	playlist := &models.Playlist{
		UserID:    1,
		Title:     "My Playlist",
		IsPublic:  true,
		CreatedAt: time.Now(),
	}
	db.Create(playlist)

	// Handler uses AddTrackToPlaylistRequest
	type AddTrackToPlaylistRequest struct {
		TrackID  int64 `json:"track_id"`
		Position int   `json:"position,omitempty"`
	}
	reqBody := AddTrackToPlaylistRequest{
		TrackID: track.ID,
	}
	body, _ := json.Marshal(reqBody)

	req := httptest.NewRequest("POST", "/api/v1/playlists/1/tracks", bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	c, _ := gin.CreateTestContext(httptest.NewRecorder())
	c.Request = req
	c.Set("user_id", 1)
	c.Params = gin.Params{gin.Param{Key: "id", Value: "1"}}

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("user_id", 1)
	c.Params = gin.Params{gin.Param{Key: "id", Value: "1"}}

	AddTrackToPlaylist(service)(c)

	assert.Equal(t, http.StatusOK, w.Code)
}

func TestHandlers_RemoveTrack_Success(t *testing.T) {
	service, db, cleanup := setupTestPlaylistHandlers(t)
	defer cleanup()

	// Create test track
	track := &models.Track{
		UserID:    1,
		Title:     "Test Track",
		FilePath:  "/test/track.mp3",
		Format:    "mp3",
		IsPublic:  true,
		CreatedAt: time.Now(),
	}
	db.Create(track)

	// Create test playlist
	playlist := &models.Playlist{
		UserID:    1,
		Title:     "My Playlist",
		IsPublic:  true,
		CreatedAt: time.Now(),
	}
	db.Create(playlist)

	// Add track to playlist using repository directly to setup state
	err := db.Create(&models.PlaylistTrack{
		PlaylistID: playlist.ID,
		TrackID:    track.ID,
		Position:   1,
	}).Error
	require.NoError(t, err)

	req := httptest.NewRequest("DELETE", "/api/v1/playlists/1/tracks/1", nil)
	c, _ := gin.CreateTestContext(httptest.NewRecorder())
	c.Request = req
	c.Set("user_id", 1)
	c.Params = gin.Params{
		gin.Param{Key: "id", Value: "1"},
		gin.Param{Key: "track_id", Value: "1"},
	}

	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("user_id", 1)
	c.Params = gin.Params{
		gin.Param{Key: "id", Value: "1"},
		gin.Param{Key: "track_id", Value: "1"},
	}

	RemoveTrackFromPlaylist(service)(c)

	assert.Equal(t, http.StatusOK, w.Code)
}
