package services

import (
	"bytes"
	"mime/multipart"
	"net/http"
	"testing"

	"github.com/stretchr/testify/assert"
)

// createTestAudioFileHeader crée un FileHeader pour les tests
func createTestAudioFileHeader(filename string, data []byte) *multipart.FileHeader {
	body := new(bytes.Buffer)
	writer := multipart.NewWriter(body)
	part, err := writer.CreateFormFile("file", filename)
	if err != nil {
		return nil
	}
	if _, err := part.Write(data); err != nil {
		return nil
	}
	writer.Close()

	req, err := http.NewRequest("POST", "/test", body)
	if err != nil {
		return nil
	}
	req.Header.Set("Content-Type", writer.FormDataContentType())

	// Parse multipart form
	if err := req.ParseMultipartForm(10 << 20); err != nil {
		return nil
	}

	formFile := req.MultipartForm.File["file"]
	if len(formFile) == 0 {
		return nil
	}

	return formFile[0]
}

func TestTrackValidationService_ValidateFormat_MP3(t *testing.T) {
	service := NewTrackValidationService()

	// MP3 avec ID3v2
	mp3Data := []byte{'I', 'D', '3', 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
	fileHeader := createTestAudioFileHeader("test.mp3", mp3Data)
	assert.NotNil(t, fileHeader)

	err := service.ValidateFormat(fileHeader)
	assert.NoError(t, err)
}

func TestTrackValidationService_ValidateFormat_MP3_MPEG(t *testing.T) {
	service := NewTrackValidationService()

	// MP3 avec MPEG frame sync
	mp3Data := []byte{0xFF, 0xFB, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
	fileHeader := createTestAudioFileHeader("test.mp3", mp3Data)
	assert.NotNil(t, fileHeader)

	err := service.ValidateFormat(fileHeader)
	assert.NoError(t, err)
}

func TestTrackValidationService_ValidateFormat_FLAC(t *testing.T) {
	service := NewTrackValidationService()

	// FLAC
	flacData := []byte{'f', 'L', 'a', 'C', 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00}
	fileHeader := createTestAudioFileHeader("test.flac", flacData)
	assert.NotNil(t, fileHeader)

	err := service.ValidateFormat(fileHeader)
	assert.NoError(t, err)
}

func TestTrackValidationService_ValidateFormat_WAV(t *testing.T) {
	service := NewTrackValidationService()

	// WAV
	wavData := []byte{'R', 'I', 'F', 'F', 0x00, 0x00, 0x00, 0x00, 'W', 'A', 'V', 'E'}
	fileHeader := createTestAudioFileHeader("test.wav", wavData)
	assert.NotNil(t, fileHeader)

	err := service.ValidateFormat(fileHeader)
	assert.NoError(t, err)
}

func TestTrackValidationService_ValidateFormat_OGG(t *testing.T) {
	service := NewTrackValidationService()

	// OGG
	oggData := []byte{'O', 'g', 'g', 'S', 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
	fileHeader := createTestAudioFileHeader("test.ogg", oggData)
	assert.NotNil(t, fileHeader)

	err := service.ValidateFormat(fileHeader)
	assert.NoError(t, err)
}

func TestTrackValidationService_ValidateFormat_Invalid(t *testing.T) {
	service := NewTrackValidationService()

	// Fichier invalide
	invalidData := []byte("not an audio file")
	fileHeader := createTestAudioFileHeader("test.txt", invalidData)
	assert.NotNil(t, fileHeader)

	err := service.ValidateFormat(fileHeader)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "invalid audio file format")
}

func TestTrackValidationService_ValidateFileSize_Valid(t *testing.T) {
	service := NewTrackValidationService()

	data := make([]byte, 10*1024*1024) // 10MB
	fileHeader := createTestAudioFileHeader("test.mp3", data)
	assert.NotNil(t, fileHeader)

	err := service.ValidateFileSize(fileHeader)
	assert.NoError(t, err)
}

func TestTrackValidationService_ValidateFileSize_TooLarge(t *testing.T) {
	service := NewTrackValidationService()

	data := make([]byte, 101*1024*1024) // 101MB
	fileHeader := createTestAudioFileHeader("test.mp3", data)
	assert.NotNil(t, fileHeader)

	err := service.ValidateFileSize(fileHeader)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "file size exceeds maximum")
}

func TestTrackValidationService_ValidateFileSize_Empty(t *testing.T) {
	service := NewTrackValidationService()

	data := []byte{}
	fileHeader := createTestAudioFileHeader("test.mp3", data)
	assert.NotNil(t, fileHeader)

	err := service.ValidateFileSize(fileHeader)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "file is empty")
}

func TestTrackValidationService_ValidateDuration_Valid(t *testing.T) {
	service := NewTrackValidationService()

	// Durée valide (30 secondes)
	err := service.ValidateDuration(30)
	assert.NoError(t, err)

	// Durée valide (1 seconde - minimum)
	err = service.ValidateDuration(1)
	assert.NoError(t, err)

	// Durée valide (3 heures - maximum)
	err = service.ValidateDuration(MaxTrackDuration)
	assert.NoError(t, err)
}

func TestTrackValidationService_ValidateDuration_TooShort(t *testing.T) {
	service := NewTrackValidationService()

	// Durée trop courte
	err := service.ValidateDuration(0)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "too short")
}

func TestTrackValidationService_ValidateDuration_TooLong(t *testing.T) {
	service := NewTrackValidationService()

	// Durée trop longue
	err := service.ValidateDuration(MaxTrackDuration + 1)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "too long")
}

func TestTrackValidationService_ValidateCodec_Valid(t *testing.T) {
	service := NewTrackValidationService()

	validCodecs := []string{"mp3", "MP3", "flac", "FLAC", "pcm", "vorbis", "aac", "AAC"}
	for _, codec := range validCodecs {
		err := service.ValidateCodec(codec)
		assert.NoError(t, err, "codec %s should be valid", codec)
	}
}

func TestTrackValidationService_ValidateCodec_Invalid(t *testing.T) {
	service := NewTrackValidationService()

	// Codec invalide
	err := service.ValidateCodec("invalid_codec")
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "unsupported codec")
}

func TestTrackValidationService_ValidateCodec_Empty(t *testing.T) {
	service := NewTrackValidationService()

	// Codec vide
	err := service.ValidateCodec("")
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "codec is required")
}

func TestTrackValidationService_ValidateTrackFile_Success(t *testing.T) {
	service := NewTrackValidationService()

	// Créer un fichier MP3 valide
	mp3Data := []byte{'I', 'D', '3', 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
	fileHeader := createTestAudioFileHeader("test.mp3", mp3Data)
	assert.NotNil(t, fileHeader)

	result, err := service.ValidateTrackFile(fileHeader, 180, "mp3")
	assert.NoError(t, err)
	assert.NotNil(t, result)
	assert.True(t, result.Valid)
	assert.Equal(t, "mp3", result.Codec)
	assert.Equal(t, 180, result.Duration)
}

func TestTrackValidationService_ValidateTrackFile_InvalidFormat(t *testing.T) {
	service := NewTrackValidationService()

	// Fichier invalide
	invalidData := []byte("not an audio file")
	fileHeader := createTestAudioFileHeader("test.txt", invalidData)
	assert.NotNil(t, fileHeader)

	result, err := service.ValidateTrackFile(fileHeader, 180, "mp3")
	assert.Error(t, err)
	assert.NotNil(t, result)
	assert.False(t, result.Valid)
	assert.NotEmpty(t, result.Errors)
}

func TestTrackValidationService_ValidateTrackFile_InvalidSize(t *testing.T) {
	service := NewTrackValidationService()

	// Fichier trop grand
	largeData := make([]byte, 101*1024*1024) // 101MB
	fileHeader := createTestAudioFileHeader("test.mp3", largeData)
	assert.NotNil(t, fileHeader)

	result, err := service.ValidateTrackFile(fileHeader, 180, "mp3")
	assert.Error(t, err)
	assert.NotNil(t, result)
	assert.False(t, result.Valid)
}

func TestTrackValidationService_ValidateTrackFile_InvalidDuration(t *testing.T) {
	service := NewTrackValidationService()

	// Fichier valide mais durée invalide
	mp3Data := []byte{'I', 'D', '3', 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
	fileHeader := createTestAudioFileHeader("test.mp3", mp3Data)
	assert.NotNil(t, fileHeader)

	// Durée trop longue
	result, err := service.ValidateTrackFile(fileHeader, MaxTrackDuration+1, "mp3")
	assert.Error(t, err)
	assert.NotNil(t, result)
	assert.False(t, result.Valid)

	// Durée trop courte (0) est ignorée par ValidateTrackFile (considérée comme non fournie)
	// Donc on ne teste pas le cas 0 ici qui retourne valide par design (optionalité)
}

func TestTrackValidationService_ValidateTrackFile_InvalidCodec(t *testing.T) {
	service := NewTrackValidationService()

	// Fichier valide mais codec invalide
	mp3Data := []byte{'I', 'D', '3', 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
	fileHeader := createTestAudioFileHeader("test.mp3", mp3Data)
	assert.NotNil(t, fileHeader)

	result, err := service.ValidateTrackFile(fileHeader, 180, "invalid_codec")
	assert.Error(t, err)
	assert.NotNil(t, result)
	assert.False(t, result.Valid)
}

func TestTrackValidationService_DetectFormat(t *testing.T) {
	service := NewTrackValidationService()

	tests := []struct {
		name     string
		data     []byte
		expected string
	}{
		{
			name:     "MP3 ID3v2",
			data:     []byte{'I', 'D', '3', 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
			expected: "audio/mpeg",
		},
		{
			name:     "FLAC",
			data:     []byte{'f', 'L', 'a', 'C', 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00},
			expected: "audio/flac",
		},
		{
			name:     "WAV",
			data:     []byte{'R', 'I', 'F', 'F', 0x00, 0x00, 0x00, 0x00, 'W', 'A', 'V', 'E'},
			expected: "audio/wav",
		},
		{
			name:     "OGG",
			data:     []byte{'O', 'g', 'g', 'S', 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
			expected: "audio/ogg",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			fileHeader := createTestAudioFileHeader("test."+tt.name, tt.data)
			assert.NotNil(t, fileHeader)

			format := service.detectFormat(fileHeader)
			assert.Equal(t, tt.expected, format)
		})
	}
}
