package handlers

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/google/uuid"
	"mime/multipart"
	"net/http"
	"net/http/httptest"
	"os"
	"testing"

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

// createTestMP3 creates a minimal valid MP3 file header for testing
func createTestMP3() ([]byte, error) {
	// MP3 file header (ID3v2 tag)
	header := []byte{
		'I', 'D', '3', // ID3v2 marker
		0x03, 0x00, // Version
		0x00,                   // Flags
		0x00, 0x00, 0x00, 0x00, // Size (0 for test)
	}
	return header, nil
}

// createTestAudioFile creates a test audio file with specified extension
func createTestAudioFile(ext string) ([]byte, error) {
	switch ext {
	case ".mp3":
		return createTestMP3()
	case ".flac":
		// FLAC file header
		return []byte{'f', 'L', 'a', 'C', 0x00, 0x00, 0x00, 0x22}, nil
	case ".wav":
		// WAV file header
		return []byte{'R', 'I', 'F', 'F', 0x00, 0x00, 0x00, 0x00, 'W', 'A', 'V', 'E'}, nil
	case ".ogg":
		// OGG file header
		return []byte{'O', 'g', 'g', 'S', 0x00, 0x02, 0x00, 0x00}, nil
	default:
		return createTestMP3()
	}
}

func setupTestTrackHandler(t *testing.T) (*TrackHandler, *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)

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

	// Create test user
	user := &models.User{
		ID:       123,
		Username: "testuser",
		Email:    "test@example.com",
		IsActive: true,
	}
	err = db.Create(user).Error
	assert.NoError(t, err)

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

	// Setup test upload directory
	testUploadDir := "test_uploads/tracks"
	trackService := services.NewTrackService(db, logger, testUploadDir)
	trackUploadService := services.NewTrackUploadService(db, logger)
	chunkService := services.NewTrackChunkService("test_uploads/tracks/chunks", logger)
	trackLikeService := services.NewTrackLikeService(db, logger)
	// Pass nil for streamService in tests
	trackHandler := NewTrackHandler(trackService, trackUploadService, chunkService, trackLikeService, nil)

	// Cleanup function
	cleanup := func() {
		os.RemoveAll("test_uploads")
	}

	return trackHandler, db, cleanup
}

func TestTrackHandler_UploadTrack_Success(t *testing.T) {
	handler, db, cleanup := setupTestTrackHandler(t)
	defer cleanup()

	// Create test MP3 file
	mp3Data, err := createTestAudioFile(".mp3")
	assert.NoError(t, err)

	// Create multipart form
	body := new(bytes.Buffer)
	writer := multipart.NewWriter(body)
	part, err := writer.CreateFormFile("file", "test.mp3")
	assert.NoError(t, err)
	_, err = part.Write(mp3Data)
	assert.NoError(t, err)
	writer.Close()

	// Create request
	req := httptest.NewRequest("POST", "/api/v1/tracks", body)
	req.Header.Set("Content-Type", writer.FormDataContentType())

	// Set user_id in context (simulating auth middleware)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("user_id", int64(123))

	// Execute
	handler.UploadTrack(c)

	// Assert
	assert.Equal(t, http.StatusCreated, w.Code)

	var response map[string]interface{}
	err = json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(t, err)
	assert.Contains(t, response, "track")

	track := response["track"].(map[string]interface{})
	assert.Equal(t, float64(123), track["user_id"])
	assert.Equal(t, "test", track["title"])
	assert.Equal(t, "MP3", track["format"])

	// Verify track was created in DB
	var dbTrack models.Track
	err = db.First(&dbTrack, track["id"]).Error
	assert.NoError(t, err)
	assert.Equal(t, int64(123), dbTrack.UserID)
	assert.Equal(t, "test", dbTrack.Title)
}

func TestTrackHandler_UploadTrack_Unauthorized(t *testing.T) {
	handler, _, cleanup := setupTestTrackHandler(t)
	defer cleanup()

	// Create request without user_id in context
	req := httptest.NewRequest("POST", "/api/v1/tracks/upload", nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	// No user_id set

	// Execute
	handler.UploadTrack(c)

	// Assert
	assert.Equal(t, http.StatusUnauthorized, w.Code)

	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(t, err)
	assert.Equal(t, "unauthorized", response["error"])
}

func TestTrackHandler_UploadTrack_NoFile(t *testing.T) {
	handler, _, cleanup := setupTestTrackHandler(t)
	defer cleanup()

	// Create request without file
	req := httptest.NewRequest("POST", "/api/v1/tracks/upload", nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("user_id", int64(123))

	// Execute
	handler.UploadTrack(c)

	// Assert
	assert.Equal(t, http.StatusBadRequest, w.Code)

	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(t, err)
	assert.Contains(t, response["error"], "no file provided")
}

func TestTrackHandler_UploadTrack_InvalidFormat(t *testing.T) {
	handler, _, cleanup := setupTestTrackHandler(t)
	defer cleanup()

	// Create test file with invalid format
	invalidData := []byte("not an audio file")

	// Create multipart form
	body := new(bytes.Buffer)
	writer := multipart.NewWriter(body)
	part, err := writer.CreateFormFile("file", "test.txt")
	assert.NoError(t, err)
	_, err = part.Write(invalidData)
	assert.NoError(t, err)
	writer.Close()

	// Create request
	req := httptest.NewRequest("POST", "/api/v1/tracks", body)
	req.Header.Set("Content-Type", writer.FormDataContentType())

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

	// Execute
	handler.UploadTrack(c)

	// Assert
	assert.Equal(t, http.StatusBadRequest, w.Code)

	var response map[string]interface{}
	err = json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(t, err)
	assert.Contains(t, response["error"], "Invalid file format")
}

func TestTrackHandler_UploadTrack_FileTooLarge(t *testing.T) {
	handler, _, cleanup := setupTestTrackHandler(t)
	defer cleanup()

	// Create a large file (over 100MB)
	largeData := make([]byte, 101*1024*1024) // 101MB

	// Create multipart form
	body := new(bytes.Buffer)
	writer := multipart.NewWriter(body)
	part, err := writer.CreateFormFile("file", "large.mp3")
	assert.NoError(t, err)
	_, err = part.Write(largeData)
	assert.NoError(t, err)
	writer.Close()

	// Create request
	req := httptest.NewRequest("POST", "/api/v1/tracks", body)
	req.Header.Set("Content-Type", writer.FormDataContentType())

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

	// Execute
	handler.UploadTrack(c)

	// Assert
	assert.Equal(t, http.StatusBadRequest, w.Code)

	var response map[string]interface{}
	err = json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(t, err)
	assert.Contains(t, response["error"], "File size exceeds maximum")
}

func TestTrackHandler_UploadTrack_ValidFormats(t *testing.T) {
	handler, _, cleanup := setupTestTrackHandler(t)
	defer cleanup()

	formats := []struct {
		ext      string
		expected string
	}{
		{".mp3", "MP3"},
		{".flac", "FLAC"},
		{".wav", "WAV"},
		{".ogg", "OGG"},
	}

	for _, format := range formats {
		t.Run(format.ext, func(t *testing.T) {
			// Create test audio file
			audioData, err := createTestAudioFile(format.ext)
			assert.NoError(t, err)

			// Create multipart form
			body := new(bytes.Buffer)
			writer := multipart.NewWriter(body)
			part, err := writer.CreateFormFile("file", "test"+format.ext)
			assert.NoError(t, err)
			_, err = part.Write(audioData)
			assert.NoError(t, err)
			writer.Close()

			// Create request
			req := httptest.NewRequest("POST", "/api/v1/tracks", body)
			req.Header.Set("Content-Type", writer.FormDataContentType())

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

			// Execute
			handler.UploadTrack(c)

			// Assert
			if w.Code != http.StatusCreated {
				t.Logf("Response body: %s", w.Body.String())
			}
			assert.Equal(t, http.StatusCreated, w.Code, "Format %s should be accepted", format.ext)
		})
	}
}

func TestTrackHandler_ListTracks_Success(t *testing.T) {
	handler, db, cleanup := setupTestTrackHandler(t)
	defer cleanup()

	// Créer quelques tracks avec statut completed
	track1 := &models.Track{
		UserID:   123,
		Title:    "Track 1",
		FilePath: "/test/track1.mp3",
		FileSize: 5 * 1024 * 1024,
		Format:   "MP3",
		Genre:    "Rock",
		Duration: 180,
		IsPublic: true,
		Status:   models.TrackStatusCompleted,
	}
	err := db.Create(track1).Error
	assert.NoError(t, err)

	track2 := &models.Track{
		UserID:   123,
		Title:    "Track 2",
		FilePath: "/test/track2.mp3",
		FileSize: 5 * 1024 * 1024,
		Format:   "MP3",
		Genre:    "Jazz",
		Duration: 200,
		IsPublic: true,
		Status:   models.TrackStatusCompleted,
	}
	err = db.Create(track2).Error
	assert.NoError(t, err)

	// Créer request
	req := httptest.NewRequest("GET", "/api/v1/tracks?page=1&limit=20", nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("user_id", int64(123))

	// Execute
	handler.ListTracks(c)

	// 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.Contains(t, response, "tracks")
	assert.Contains(t, response, "pagination")

	tracks := response["tracks"].([]interface{})
	assert.GreaterOrEqual(t, len(tracks), 2)

	pagination := response["pagination"].(map[string]interface{})
	assert.Equal(t, float64(1), pagination["page"])
	assert.Equal(t, float64(20), pagination["limit"])
}

func TestTrackHandler_ListTracks_WithFilters(t *testing.T) {
	handler, db, cleanup := setupTestTrackHandler(t)
	defer cleanup()

	// Créer tracks avec différents genres
	track1 := &models.Track{
		UserID:   123,
		Title:    "Rock Track",
		FilePath: "/test/track1.mp3",
		FileSize: 5 * 1024 * 1024,
		Format:   "MP3",
		Genre:    "Rock",
		Duration: 180,
		IsPublic: true,
		Status:   models.TrackStatusCompleted,
	}
	err := db.Create(track1).Error
	assert.NoError(t, err)

	track2 := &models.Track{
		UserID:   123,
		Title:    "Jazz Track",
		FilePath: "/test/track2.mp3",
		FileSize: 5 * 1024 * 1024,
		Format:   "MP3",
		Genre:    "Jazz",
		Duration: 200,
		IsPublic: true,
		Status:   models.TrackStatusCompleted,
	}
	err = db.Create(track2).Error
	assert.NoError(t, err)

	// Test avec filtre genre
	req := httptest.NewRequest("GET", "/api/v1/tracks?genre=Rock", nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("user_id", int64(123))

	handler.ListTracks(c)

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

	var response map[string]interface{}
	err = json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(t, err)

	tracks := response["tracks"].([]interface{})
	assert.Equal(t, 1, len(tracks))

	track := tracks[0].(map[string]interface{})
	assert.Equal(t, "Rock", track["genre"])
}

func TestTrackHandler_ListTracks_WithPagination(t *testing.T) {
	handler, db, cleanup := setupTestTrackHandler(t)
	defer cleanup()

	// Créer 5 tracks
	for i := 1; i <= 5; i++ {
		track := &models.Track{
			UserID:   123,
			Title:    "Track " + string(rune('0'+i)),
			FilePath: "/test/track" + string(rune('0'+i)) + ".mp3",
			FileSize: 5 * 1024 * 1024,
			Format:   "MP3",
			Duration: 180,
			IsPublic: true,
			Status:   models.TrackStatusCompleted,
		}
		err := db.Create(track).Error
		assert.NoError(t, err)
	}

	// Test page 1 avec limit 2
	req := httptest.NewRequest("GET", "/api/v1/tracks?page=1&limit=2", nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("user_id", int64(123))

	handler.ListTracks(c)

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

	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(t, err)

	tracks := response["tracks"].([]interface{})
	assert.Equal(t, 2, len(tracks))

	pagination := response["pagination"].(map[string]interface{})
	assert.Equal(t, float64(1), pagination["page"])
	assert.Equal(t, float64(2), pagination["limit"])
	assert.Equal(t, float64(5), pagination["total"])
}

func TestTrackHandler_ListTracks_WithSorting(t *testing.T) {
	handler, db, cleanup := setupTestTrackHandler(t)
	defer cleanup()

	// Créer tracks avec différents titres
	track1 := &models.Track{
		UserID:   123,
		Title:    "A Track",
		FilePath: "/test/track1.mp3",
		FileSize: 5 * 1024 * 1024,
		Format:   "MP3",
		Duration: 180,
		IsPublic: true,
		Status:   models.TrackStatusCompleted,
	}
	err := db.Create(track1).Error
	assert.NoError(t, err)

	track2 := &models.Track{
		UserID:   123,
		Title:    "Z Track",
		FilePath: "/test/track2.mp3",
		FileSize: 5 * 1024 * 1024,
		Format:   "MP3",
		Duration: 180,
		IsPublic: true,
		Status:   models.TrackStatusCompleted,
	}
	err = db.Create(track2).Error
	assert.NoError(t, err)

	// Test avec tri par titre asc
	req := httptest.NewRequest("GET", "/api/v1/tracks?sort_by=title&sort_order=asc", nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("user_id", int64(123))

	handler.ListTracks(c)

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

	var response map[string]interface{}
	err = json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(t, err)

	tracks := response["tracks"].([]interface{})
	assert.GreaterOrEqual(t, len(tracks), 2)

	// Vérifier que le tri est appliqué (A avant Z)
	firstTrack := tracks[0].(map[string]interface{})
	assert.Equal(t, "A Track", firstTrack["title"])
}

func TestTrackHandler_UpdateTrack_Success(t *testing.T) {
	handler, db, cleanup := setupTestTrackHandler(t)
	defer cleanup()

	// Créer un track
	track := &models.Track{
		UserID:   123,
		Title:    "Original Title",
		FilePath: "/test/track.mp3",
		FileSize: 5 * 1024 * 1024,
		Format:   "MP3",
		Genre:    "Rock",
		Duration: 180,
		IsPublic: true,
		Status:   models.TrackStatusCompleted,
	}
	err := db.Create(track).Error
	assert.NoError(t, err)

	// Créer request body
	updateData := map[string]interface{}{
		"title": "Updated Title",
		"genre": "Jazz",
	}
	body, _ := json.Marshal(updateData)

	// Créer request
	req := httptest.NewRequest("PUT", fmt.Sprintf("/api/v1/tracks/%d", track.ID), bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("user_id", int64(123))
	c.Params = gin.Params{gin.Param{Key: "id", Value: fmt.Sprintf("%d", track.ID)}}

	// Execute
	handler.UpdateTrack(c)

	// 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.Contains(t, response, "track")

	updatedTrack := response["track"].(map[string]interface{})
	assert.Equal(t, "Updated Title", updatedTrack["title"])
	assert.Equal(t, "Jazz", updatedTrack["genre"])
}

func TestTrackHandler_UpdateTrack_NotFound(t *testing.T) {
	handler, _, cleanup := setupTestTrackHandler(t)
	defer cleanup()

	// Créer request body
	updateData := map[string]interface{}{
		"title": "Updated Title",
	}
	body, _ := json.Marshal(updateData)

	// Créer request avec un ID qui n'existe pas
	req := httptest.NewRequest("PUT", "/api/v1/tracks/99999", bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")

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

	// Execute
	handler.UpdateTrack(c)

	// Assert
	assert.Equal(t, http.StatusNotFound, w.Code)

	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(t, err)
	assert.Equal(t, "track not found", response["error"])
}

func TestTrackHandler_UpdateTrack_Forbidden(t *testing.T) {
	handler, db, cleanup := setupTestTrackHandler(t)
	defer cleanup()

	// Créer un track appartenant à l'utilisateur 123
	track := &models.Track{
		UserID:   123,
		Title:    "Original Title",
		FilePath: "/test/track.mp3",
		FileSize: 5 * 1024 * 1024,
		Format:   "MP3",
		Duration: 180,
		IsPublic: true,
		Status:   models.TrackStatusCompleted,
	}
	err := db.Create(track).Error
	assert.NoError(t, err)

	// Créer request body
	updateData := map[string]interface{}{
		"title": "Updated Title",
	}
	body, _ := json.Marshal(updateData)

	// Créer request avec un autre utilisateur (456)
	req := httptest.NewRequest("PUT", fmt.Sprintf("/api/v1/tracks/%d", track.ID), bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("user_id", int64(456))
	c.Params = gin.Params{gin.Param{Key: "id", Value: fmt.Sprintf("%d", track.ID)}}

	// Execute
	handler.UpdateTrack(c)

	// Assert
	assert.Equal(t, http.StatusForbidden, w.Code)

	var response map[string]interface{}
	err = json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(t, err)
	assert.Equal(t, "forbidden", response["error"])
}

func TestTrackHandler_UpdateTrack_Unauthorized(t *testing.T) {
	handler, _, cleanup := setupTestTrackHandler(t)
	defer cleanup()

	// Créer request body
	updateData := map[string]interface{}{
		"title": "Updated Title",
	}
	body, _ := json.Marshal(updateData)

	// Créer request sans user_id
	req := httptest.NewRequest("PUT", "/api/v1/tracks/1", bytes.NewBuffer(body))
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	// Pas de user_id
	c.Params = gin.Params{gin.Param{Key: "id", Value: "1"}}

	// Execute
	handler.UpdateTrack(c)

	// Assert
	assert.Equal(t, http.StatusUnauthorized, w.Code)

	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(t, err)
	assert.Equal(t, "unauthorized", response["error"])
}

func TestTrackHandler_UpdateTrack_InvalidID(t *testing.T) {
	handler, _, cleanup := setupTestTrackHandler(t)
	defer cleanup()

	// Créer request body
	updateData := map[string]interface{}{
		"title": "Updated Title",
	}
	body, _ := json.Marshal(updateData)

	// Créer request avec un ID invalide
	req := httptest.NewRequest("PUT", "/api/v1/tracks/invalid", bytes.NewBuffer(body))
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("user_id", int64(123))
	c.Params = gin.Params{gin.Param{Key: "id", Value: "invalid"}}

	// Execute
	handler.UpdateTrack(c)

	// Assert
	assert.Equal(t, http.StatusBadRequest, w.Code)

	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(t, err)
	assert.Equal(t, "invalid track id", response["error"])
}

func TestTrackHandler_UpdateTrack_EmptyTitle(t *testing.T) {
	handler, db, cleanup := setupTestTrackHandler(t)
	defer cleanup()

	// Créer un track
	track := &models.Track{
		UserID:   123,
		Title:    "Original Title",
		FilePath: "/test/track.mp3",
		FileSize: 5 * 1024 * 1024,
		Format:   "MP3",
		Duration: 180,
		IsPublic: true,
		Status:   models.TrackStatusCompleted,
	}
	err := db.Create(track).Error
	assert.NoError(t, err)

	// Créer request body avec titre vide
	updateData := map[string]interface{}{
		"title": "",
	}
	body, _ := json.Marshal(updateData)

	// Créer request
	req := httptest.NewRequest("PUT", fmt.Sprintf("/api/v1/tracks/%d", track.ID), bytes.NewBuffer(body))
	req.Header.Set("Content-Type", "application/json")
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("user_id", int64(123))
	c.Params = gin.Params{gin.Param{Key: "id", Value: fmt.Sprintf("%d", track.ID)}}

	// Execute
	handler.UpdateTrack(c)

	// Assert
	assert.Equal(t, http.StatusBadRequest, w.Code)

	var response map[string]interface{}
	err = json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(t, err)
	assert.Contains(t, response["error"], "title cannot be empty")
}

func TestTrackHandler_DeleteTrack_Success(t *testing.T) {
	handler, db, cleanup := setupTestTrackHandler(t)
	defer cleanup()

	// Créer un track
	track := &models.Track{
		UserID:   123,
		Title:    "Test Track",
		FilePath: "/test/track.mp3",
		FileSize: 5 * 1024 * 1024,
		Format:   "MP3",
		Duration: 180,
		IsPublic: true,
		Status:   models.TrackStatusCompleted,
	}
	err := db.Create(track).Error
	assert.NoError(t, err)

	// Créer request
	req := httptest.NewRequest("DELETE", fmt.Sprintf("/api/v1/tracks/%d", track.ID), nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("user_id", int64(123))
	c.Params = gin.Params{gin.Param{Key: "id", Value: fmt.Sprintf("%d", track.ID)}}

	// Execute
	handler.DeleteTrack(c)

	// 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.Equal(t, "track deleted successfully", response["message"])

	// Vérifier que le track a été supprimé
	var deletedTrack models.Track
	err = db.First(&deletedTrack, track.ID).Error
	assert.Error(t, err)
	assert.Equal(t, gorm.ErrRecordNotFound, err)
}

func TestTrackHandler_DeleteTrack_NotFound(t *testing.T) {
	handler, _, cleanup := setupTestTrackHandler(t)
	defer cleanup()

	// Créer request avec un ID qui n'existe pas
	req := httptest.NewRequest("DELETE", "/api/v1/tracks/99999", nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("user_id", int64(123))
	c.Params = gin.Params{gin.Param{Key: "id", Value: "99999"}}

	// Execute
	handler.DeleteTrack(c)

	// Assert
	assert.Equal(t, http.StatusNotFound, w.Code)

	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(t, err)
	assert.Equal(t, "track not found", response["error"])
}

func TestTrackHandler_DeleteTrack_Forbidden(t *testing.T) {
	handler, db, cleanup := setupTestTrackHandler(t)
	defer cleanup()

	// Créer un track appartenant à l'utilisateur 123
	track := &models.Track{
		UserID:   123,
		Title:    "Test Track",
		FilePath: "/test/track.mp3",
		FileSize: 5 * 1024 * 1024,
		Format:   "MP3",
		Duration: 180,
		IsPublic: true,
		Status:   models.TrackStatusCompleted,
	}
	err := db.Create(track).Error
	assert.NoError(t, err)

	// Créer request avec un autre utilisateur (456)
	req := httptest.NewRequest("DELETE", fmt.Sprintf("/api/v1/tracks/%d", track.ID), nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("user_id", int64(456))
	c.Params = gin.Params{gin.Param{Key: "id", Value: fmt.Sprintf("%d", track.ID)}}

	// Execute
	handler.DeleteTrack(c)

	// Assert
	assert.Equal(t, http.StatusForbidden, w.Code)

	var response map[string]interface{}
	err = json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(t, err)
	assert.Equal(t, "forbidden", response["error"])

	// Vérifier que le track n'a pas été supprimé
	var existingTrack models.Track
	err = db.First(&existingTrack, track.ID).Error
	assert.NoError(t, err)
	assert.Equal(t, track.ID, existingTrack.ID)
}

func TestTrackHandler_DeleteTrack_Unauthorized(t *testing.T) {
	handler, _, cleanup := setupTestTrackHandler(t)
	defer cleanup()

	// Créer request sans user_id
	req := httptest.NewRequest("DELETE", "/api/v1/tracks/1", nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	// Pas de user_id
	c.Params = gin.Params{gin.Param{Key: "id", Value: "1"}}

	// Execute
	handler.DeleteTrack(c)

	// Assert
	assert.Equal(t, http.StatusUnauthorized, w.Code)

	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(t, err)
	assert.Equal(t, "unauthorized", response["error"])
}

func TestTrackHandler_DeleteTrack_InvalidID(t *testing.T) {
	handler, _, cleanup := setupTestTrackHandler(t)
	defer cleanup()

	// Créer request avec un ID invalide
	req := httptest.NewRequest("DELETE", "/api/v1/tracks/invalid", nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("user_id", int64(123))
	c.Params = gin.Params{gin.Param{Key: "id", Value: "invalid"}}

	// Execute
	handler.DeleteTrack(c)

	// Assert
	assert.Equal(t, http.StatusBadRequest, w.Code)

	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(t, err)
	assert.Equal(t, "invalid track id", response["error"])
}

func TestTrackHandler_GetTrack_Success(t *testing.T) {
	handler, db, cleanup := setupTestTrackHandler(t)
	defer cleanup()

	// Créer un track
	track := &models.Track{
		UserID:   123,
		Title:    "Test Track",
		FilePath: "/test/track.mp3",
		FileSize: 5 * 1024 * 1024,
		Format:   "MP3",
		Genre:    "Rock",
		Duration: 180,
		IsPublic: true,
		Status:   models.TrackStatusCompleted,
	}
	err := db.Create(track).Error
	assert.NoError(t, err)

	// Créer request
	req := httptest.NewRequest("GET", fmt.Sprintf("/api/v1/tracks/%d", track.ID), nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("user_id", int64(123))
	c.Params = gin.Params{gin.Param{Key: "id", Value: fmt.Sprintf("%d", track.ID)}}

	// Execute
	handler.GetTrack(c)

	// 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.Contains(t, response, "track")

	retrievedTrack := response["track"].(map[string]interface{})
	assert.Equal(t, float64(track.ID), retrievedTrack["id"])
	assert.Equal(t, track.Title, retrievedTrack["title"])
}

func TestTrackHandler_GetTrack_NotFound(t *testing.T) {
	handler, _, cleanup := setupTestTrackHandler(t)
	defer cleanup()

	// Créer request avec un ID qui n'existe pas
	req := httptest.NewRequest("GET", "/api/v1/tracks/99999", nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("user_id", int64(123))
	c.Params = gin.Params{gin.Param{Key: "id", Value: "99999"}}

	// Execute
	handler.GetTrack(c)

	// Assert
	assert.Equal(t, http.StatusNotFound, w.Code)

	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(t, err)
	assert.Equal(t, "track not found", response["error"])
}

func TestTrackHandler_GetTrack_InvalidID(t *testing.T) {
	handler, _, cleanup := setupTestTrackHandler(t)
	defer cleanup()

	// Créer request avec un ID invalide
	req := httptest.NewRequest("GET", "/api/v1/tracks/invalid", nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("user_id", int64(123))
	c.Params = gin.Params{gin.Param{Key: "id", Value: "invalid"}}

	// Execute
	handler.GetTrack(c)

	// Assert
	assert.Equal(t, http.StatusBadRequest, w.Code)

	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(t, err)
	assert.Equal(t, "invalid track id", response["error"])
}

func TestTrackHandler_GetTrack_MissingID(t *testing.T) {
	handler, _, cleanup := setupTestTrackHandler(t)
	defer cleanup()

	// Créer request sans ID
	req := httptest.NewRequest("GET", "/api/v1/tracks/", nil)
	w := httptest.NewRecorder()
	c, _ := gin.CreateTestContext(w)
	c.Request = req
	c.Set("user_id", int64(123))
	c.Params = gin.Params{}

	// Execute
	handler.GetTrack(c)

	// Assert
	assert.Equal(t, http.StatusBadRequest, w.Code)

	var response map[string]interface{}
	err := json.Unmarshal(w.Body.Bytes(), &response)
	assert.NoError(t, err)
	assert.Equal(t, "track id is required", response["error"])
}
