package services

import (
	"github.com/google/uuid"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"go.uber.org/zap"
)

func setupTestTrackChunkServiceForResume(t *testing.T) (*TrackChunkService, func()) {
	logger := zap.NewNop()
	service := NewTrackChunkService("test_uploads/tracks/chunks", logger)

	cleanup := func() {
		// Cleanup will be handled by the service
	}

	return service, cleanup
}

func TestTrackChunkService_GetUploadState_Success(t *testing.T) {
	service, cleanup := setupTestTrackChunkServiceForResume(t)
	defer cleanup()

	// Initialiser un upload
	uploadID, err := service.InitiateChunkedUpload(123, 5, 1024*1024*50, "test.mp3")
	assert.NoError(t, err)
	assert.NotEmpty(t, uploadID)

	// Récupérer l'état initial (aucun chunk reçu)
	state, err := service.GetUploadState(uploadID)
	assert.NoError(t, err)
	assert.NotNil(t, state)
	assert.Equal(t, uploadID, state.UploadID)
	assert.Equal(t, int64(123), state.UserID)
	assert.Equal(t, 5, state.TotalChunks)
	assert.Equal(t, int64(1024*1024*50), state.TotalSize)
	assert.Equal(t, "test.mp3", state.Filename)
	assert.Empty(t, state.ChunksReceived)
	assert.Equal(t, 0, state.LastChunk)
	assert.Equal(t, 0, state.ReceivedCount)
	assert.Equal(t, 0, state.Progress)
}

func TestTrackChunkService_GetUploadState_NotFound(t *testing.T) {
	service, cleanup := setupTestTrackChunkServiceForResume(t)
	defer cleanup()

	// Essayer de récupérer l'état d'un upload inexistant
	state, err := service.GetUploadState("non-existent-upload-id")
	assert.Error(t, err)
	assert.Nil(t, state)
	assert.Contains(t, err.Error(), "upload not found")
}

func TestTrackChunkService_GetUploadState_WithChunks(t *testing.T) {
	service, cleanup := setupTestTrackChunkServiceForResume(t)
	defer cleanup()

	// Initialiser un upload
	uploadID, err := service.InitiateChunkedUpload(123, 5, 1024*1024*50, "test.mp3")
	assert.NoError(t, err)

	// Simuler l'ajout de quelques chunks en modifiant directement la structure
	service.mu.Lock()
	uploadInfo, exists := service.uploads[uploadID]
	assert.True(t, exists)

	uploadInfo.mu.Lock()
	uploadInfo.Chunks[1] = ChunkInfo{
		ChunkNumber: 1,
		Size:        1024 * 1024 * 10,
		MD5:         "chunk1md5",
		FilePath:    "test/chunk_1",
		Received:    true,
	}
	uploadInfo.Chunks[2] = ChunkInfo{
		ChunkNumber: 2,
		Size:        1024 * 1024 * 10,
		MD5:         "chunk2md5",
		FilePath:    "test/chunk_2",
		Received:    true,
	}
	uploadInfo.Chunks[4] = ChunkInfo{
		ChunkNumber: 4,
		Size:        1024 * 1024 * 10,
		MD5:         "chunk4md5",
		FilePath:    "test/chunk_4",
		Received:    true,
	}
	uploadInfo.UpdatedAt = time.Now()
	uploadInfo.mu.Unlock()
	service.mu.Unlock()

	// Récupérer l'état
	state, err := service.GetUploadState(uploadID)
	assert.NoError(t, err)
	assert.NotNil(t, state)

	// Vérifier les chunks reçus
	assert.Equal(t, 3, state.ReceivedCount)
	assert.Equal(t, 4, state.LastChunk) // Le dernier chunk reçu est le 4
	assert.Equal(t, 60, state.Progress) // 3/5 = 60%
	assert.Contains(t, state.ChunksReceived, 1)
	assert.Contains(t, state.ChunksReceived, 2)
	assert.Contains(t, state.ChunksReceived, 4)
	assert.NotContains(t, state.ChunksReceived, 3)
	assert.NotContains(t, state.ChunksReceived, 5)
}

func TestTrackChunkService_GetUploadState_Complete(t *testing.T) {
	service, cleanup := setupTestTrackChunkServiceForResume(t)
	defer cleanup()

	// Initialiser un upload
	uploadID, err := service.InitiateChunkedUpload(123, 3, 1024*1024*30, "complete.mp3")
	assert.NoError(t, err)

	// Simuler tous les chunks reçus
	service.mu.Lock()
	uploadInfo, exists := service.uploads[uploadID]
	assert.True(t, exists)

	uploadInfo.mu.Lock()
	for i := 1; i <= 3; i++ {
		uploadInfo.Chunks[i] = ChunkInfo{
			ChunkNumber: i,
			Size:        1024 * 1024 * 10,
			MD5:         "chunkmd5",
			FilePath:    "test/chunk_" + string(rune(i)),
			Received:    true,
		}
	}
	uploadInfo.UpdatedAt = time.Now()
	uploadInfo.mu.Unlock()
	service.mu.Unlock()

	// Récupérer l'état
	state, err := service.GetUploadState(uploadID)
	assert.NoError(t, err)
	assert.NotNil(t, state)

	assert.Equal(t, 3, state.ReceivedCount)
	assert.Equal(t, 3, state.LastChunk)
	assert.Equal(t, 100, state.Progress)
	assert.Equal(t, 3, len(state.ChunksReceived))
}

func TestTrackChunkService_GetUploadState_MultipleUsers(t *testing.T) {
	service, cleanup := setupTestTrackChunkServiceForResume(t)
	defer cleanup()

	// Créer deux uploads pour deux utilisateurs différents
	uploadID1, err := service.InitiateChunkedUpload(123, 5, 1024*1024*50, "user1.mp3")
	assert.NoError(t, err)

	uploadID2, err := service.InitiateChunkedUpload(456, 3, 1024*1024*30, "user2.mp3")
	assert.NoError(t, err)

	// Récupérer les états
	state1, err := service.GetUploadState(uploadID1)
	assert.NoError(t, err)
	assert.Equal(t, int64(123), state1.UserID)

	state2, err := service.GetUploadState(uploadID2)
	assert.NoError(t, err)
	assert.Equal(t, int64(456), state2.UserID)

	// Vérifier que les états sont isolés
	assert.NotEqual(t, state1.UploadID, state2.UploadID)
}
