package testutils

import (
	"testing"

	"veza-backend-api/internal/models"

	"github.com/google/uuid"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestCreateTestUser(t *testing.T) {
	db := SetupTestDB()
	require.NotNil(t, db)
	defer CleanupTestDB(db)

	user, err := CreateTestUser(db)
	require.NoError(t, err)
	require.NotNil(t, user)

	// Username and email are made unique with UUID suffix, so check they contain the original values
	assert.Contains(t, user.Username, "testuser")
	assert.Contains(t, user.Email, "test")
	assert.Contains(t, user.Email, "@example.com")
	assert.True(t, user.IsActive)
	assert.True(t, user.IsVerified)
	assert.False(t, user.IsAdmin)
	assert.NotEqual(t, uuid.Nil, user.ID)
}

func TestCreateTestUserWithCustomData(t *testing.T) {
	db := SetupTestDB()
	require.NotNil(t, db)
	defer CleanupTestDB(db)

	username := "customuser"
	email := "custom@example.com"

	user, err := CreateTestUserWithCustomData(db, username, email)
	require.NoError(t, err)
	require.NotNil(t, user)

	// Username and email are made unique with UUID suffix, so check they contain the original values
	assert.Contains(t, user.Username, username)
	// Email is made unique by adding UUID to local part, so check it contains the original local part and domain
	assert.Contains(t, user.Email, "custom")
	assert.Contains(t, user.Email, "@example.com")
}

func TestCreateTestAdmin(t *testing.T) {
	db := SetupTestDB()
	require.NotNil(t, db)
	defer CleanupTestDB(db)

	admin, err := CreateTestAdmin(db)
	require.NoError(t, err)
	require.NotNil(t, admin)

	// Username and email are made unique with UUID suffix, so check they contain the original values
	assert.Contains(t, admin.Username, "admin")
	assert.Contains(t, admin.Email, "admin")
	assert.Contains(t, admin.Email, "@example.com")
	assert.True(t, admin.IsAdmin)
	assert.Equal(t, "admin", admin.Role)
}

func TestCreateTestTrack(t *testing.T) {
	db := SetupTestDB()
	require.NotNil(t, db)
	defer CleanupTestDB(db)

	user, err := CreateTestUser(db)
	require.NoError(t, err)

	track, err := CreateTestTrack(db, user.ID)
	require.NoError(t, err)
	require.NotNil(t, track)

	assert.Equal(t, "Test Track", track.Title)
	assert.Equal(t, "Test Artist", track.Artist)
	assert.Equal(t, 180, track.Duration)
	assert.Equal(t, user.ID, track.UserID) // Changed CreatorID to UserID
	assert.NotEqual(t, uuid.Nil, track.ID)
}

func TestCreateTestTrackWithCustomData(t *testing.T) {
	db := SetupTestDB()
	require.NotNil(t, db)
	defer CleanupTestDB(db)

	user, err := CreateTestUser(db)
	require.NoError(t, err)

	title := "Custom Track"
	artist := "Custom Artist"

	track, err := CreateTestTrackWithCustomData(db, user.ID, title, artist)
	require.NoError(t, err)
	require.NotNil(t, track)

	assert.Equal(t, title, track.Title)
	assert.Equal(t, artist, track.Artist)
}

func TestCreateTestPlaylist(t *testing.T) {
	db := SetupTestDB()
	require.NotNil(t, db)
	defer CleanupTestDB(db)

	user, err := CreateTestUser(db)
	require.NoError(t, err)

	playlist, err := CreateTestPlaylist(db, user.ID)
	require.NoError(t, err)
	require.NotNil(t, playlist)

	assert.Equal(t, "Test Playlist", playlist.Title) // Changed Name to Title
	assert.Equal(t, user.ID, playlist.UserID)
	assert.NotEqual(t, uuid.Nil, playlist.ID)
}

func TestCreateTestRoom(t *testing.T) {
	db := SetupTestDB()
	require.NotNil(t, db)
	defer CleanupTestDB(db)

	user, err := CreateTestUser(db)
	require.NoError(t, err)

	room, err := CreateTestRoom(db, user.ID) // CreatedBy is UUID now
	require.NoError(t, err)
	require.NotNil(t, room)

	assert.Equal(t, "Test Room", room.Name)
	assert.Equal(t, user.ID, room.CreatedBy)
	assert.False(t, room.IsPrivate)
	assert.Equal(t, "public", room.Type)
}

func TestCreateTestMessage(t *testing.T) {
	db := SetupTestDB()
	require.NotNil(t, db)
	defer CleanupTestDB(db)

	user, err := CreateTestUser(db)
	require.NoError(t, err)

	room, err := CreateTestRoom(db, user.ID)
	require.NoError(t, err)

	content := "Test message content"
	message, err := CreateTestMessage(db, room.ID, user.ID, content) // room.ID is UUID now
	require.NoError(t, err)
	require.NotNil(t, message)

	assert.Equal(t, content, message.Content)
	assert.Equal(t, room.ID, message.RoomID)
	assert.Equal(t, user.ID, message.UserID)
	assert.Equal(t, "text", message.Type)
	assert.False(t, message.IsEdited)
	assert.False(t, message.IsDeleted)
}

func TestCreateTestSession(t *testing.T) {
	db := SetupTestDB()
	require.NotNil(t, db)
	defer CleanupTestDB(db)

	user, err := CreateTestUser(db)
	require.NoError(t, err)

	session, err := CreateTestSession(db, user.ID) // User.ID is UUID
	require.NoError(t, err)
	require.NotNil(t, session)

	assert.Equal(t, user.ID, session.UserID)
	assert.NotEqual(t, uuid.Nil, session.ID) // Session.ID is uuid.UUID
}

func TestCreateMultipleTestUsers(t *testing.T) {
	db := SetupTestDB()
	require.NotNil(t, db)
	defer CleanupTestDB(db)

	count := 5
	users, err := CreateMultipleTestUsers(db, count)
	require.NoError(t, err)
	require.Len(t, users, count)

	// Vérifier que tous les utilisateurs ont des IDs uniques
	// IDs sont maintenant UUID, donc comparaison avec uuid.Nil
	for _, user := range users {
		assert.NotEqual(t, uuid.Nil, user.ID)
	}

	// Vérifier que les usernames sont différents
	usernames := make(map[string]bool)
	for _, user := range users {
		assert.False(t, usernames[user.Username], "Duplicate username")
		usernames[user.Username] = true
	}
}

func TestCreateMultipleTestTracks(t *testing.T) {
	db := SetupTestDB()
	require.NotNil(t, db)
	defer CleanupTestDB(db)

	user, err := CreateTestUser(db)
	require.NoError(t, err)

	count := 3
	tracks, err := CreateMultipleTestTracks(db, user.ID, count)
	require.NoError(t, err)
	require.Len(t, tracks, count)

	// Vérifier que tous les tracks ont le même créateur
	for _, track := range tracks {
		assert.Equal(t, user.ID, track.UserID) // Changed CreatorID to UserID
		assert.NotEqual(t, uuid.Nil, track.ID)
	}

	// Vérifier que les titres sont différents
	titles := make(map[string]bool)
	for _, track := range tracks {
		assert.False(t, titles[track.Title], "Duplicate track title")
		titles[track.Title] = true
	}
}

// Test helper pour vérifier que les fixtures respectent les contraintes
func TestFixtures_ForeignKeyConstraints(t *testing.T) {
	db := SetupTestDB()
	require.NotNil(t, db)
	defer CleanupTestDB(db)

	// Créer un utilisateur
	user, err := CreateTestUser(db)
	require.NoError(t, err)

	// Créer un track avec le bon userID
	track, err := CreateTestTrack(db, user.ID)
	require.NoError(t, err)
	assert.NotNil(t, track)

	// Essayer de créer un track avec un userID inexistant devrait échouer en production
	// mais SQLite en mémoire peut ne pas toujours faire respecter les contraintes
	invalidTrack := &models.Track{
		UserID:   uuid.New(), // Changed CreatorID to UserID, use new UUID
		Title:    "Invalid Track",
		Duration: 180,
		FilePath: "uploads/invalid.mp3",
		FileSize: 100,
		Format:   "mp3",
	}
	err = db.Create(invalidTrack).Error
	// En production, cela devrait échouer, mais en test SQLite, on peut l'ignorer
	_ = err
}
