package models

import (
	"testing"
	"time"

	"github.com/google/uuid"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

func setupTestBitrateAdaptationDB(t *testing.T) *gorm.DB {
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	require.NoError(t, err)

	// Enable foreign keys for SQLite
	db.Exec("PRAGMA foreign_keys = ON")
	sqlDB, _ := db.DB()
	sqlDB.SetMaxOpenConns(1)

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

	return db
}

func TestBitrateAdaptationLog_Create(t *testing.T) {
	db := setupTestBitrateAdaptationDB(t)

	userID := uuid.New()

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

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

	// Create bitrate adaptation log
	log := &BitrateAdaptationLog{
		TrackID:          track.ID,
		UserID:           user.ID,
		OldBitrate:       128,
		NewBitrate:       192,
		Reason:           BitrateReasonNetworkFast,
		NetworkBandwidth: intPtr(5000), // 5 Mbps
	}
	err = db.Create(log).Error
	require.NoError(t, err)

	assert.NotEqual(t, uuid.Nil, log.ID)
	assert.Equal(t, track.ID, log.TrackID)
	assert.Equal(t, user.ID, log.UserID)
	assert.Equal(t, 128, log.OldBitrate)
	assert.Equal(t, 192, log.NewBitrate)
	assert.Equal(t, BitrateReasonNetworkFast, log.Reason)
	assert.NotNil(t, log.NetworkBandwidth)
	assert.Equal(t, 5000, *log.NetworkBandwidth)
	assert.False(t, log.CreatedAt.IsZero())
}

func TestBitrateAdaptationLog_DefaultValues(t *testing.T) {
	db := setupTestBitrateAdaptationDB(t)

	userID := uuid.New()

	// Create test user and track
	user := &User{
		ID:       userID,
		Username: "testuser",
		Email:    "test@example.com",
		IsActive: true,
	}
	require.NoError(t, db.Create(user).Error)

	track := &Track{
		UserID:   userID,
		Title:    "Test Track",
		FilePath: "/test/track.mp3",
		FileSize: 5 * 1024 * 1024,
		Format:   "MP3",
		Duration: 180,
		IsPublic: true,
		Status:   TrackStatusCompleted,
	}
	require.NoError(t, db.Create(track).Error)

	// Create log without network_bandwidth
	log := &BitrateAdaptationLog{
		TrackID:    track.ID,
		UserID:     user.ID,
		OldBitrate: 192,
		NewBitrate: 128,
		Reason:     BitrateReasonNetworkSlow,
	}
	err := db.Create(log).Error
	require.NoError(t, err)

	assert.Nil(t, log.NetworkBandwidth)
	assert.False(t, log.CreatedAt.IsZero())
}

func TestBitrateAdaptationLog_Relations(t *testing.T) {
	db := setupTestBitrateAdaptationDB(t)

	userID := uuid.New()

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

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

	// Create bitrate adaptation log
	log := &BitrateAdaptationLog{
		TrackID:    track.ID,
		UserID:     user.ID,
		OldBitrate: 128,
		NewBitrate: 192,
		Reason:     BitrateReasonUserSelected,
	}
	err = db.Create(log).Error
	require.NoError(t, err)

	// Test relation with Track
	var loadedLog BitrateAdaptationLog
	err = db.Preload("Track").First(&loadedLog, log.ID).Error
	require.NoError(t, err)
	assert.Equal(t, track.ID, loadedLog.Track.ID)
	assert.Equal(t, track.Title, loadedLog.Track.Title)

	// Test relation with User
	err = db.Preload("User").First(&loadedLog, log.ID).Error
	require.NoError(t, err)
	assert.Equal(t, user.ID, loadedLog.User.ID)
	assert.Equal(t, user.Username, loadedLog.User.Username)
}

func TestBitrateAdaptationLog_CascadeDelete(t *testing.T) {
	db := setupTestBitrateAdaptationDB(t)

	userID := uuid.New()

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

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

	// Create bitrate adaptation log
	log := &BitrateAdaptationLog{
		TrackID:    track.ID,
		UserID:     user.ID,
		OldBitrate: 128,
		NewBitrate: 192,
		Reason:     BitrateReasonNetworkFast,
	}
	err = db.Create(log).Error
	require.NoError(t, err)

	// Delete track - should cascade delete the log
	err = db.Unscoped().Delete(track).Error
	require.NoError(t, err)

	// Verify log is deleted
	var count int64
	db.Model(&BitrateAdaptationLog{}).Where("id = ?", log.ID).Count(&count)
	assert.Equal(t, int64(0), count, "Log should be deleted when track is deleted")
}

func TestBitrateAdaptationLog_ReasonValues(t *testing.T) {
	db := setupTestBitrateAdaptationDB(t)

	userID := uuid.New()

	// Create test user and track
	user := &User{
		ID:       userID,
		Username: "testuser",
		Email:    "test@example.com",
		IsActive: true,
	}
	require.NoError(t, db.Create(user).Error)

	track := &Track{
		UserID:   userID,
		Title:    "Test Track",
		FilePath: "/test/track.mp3",
		FileSize: 5 * 1024 * 1024,
		Format:   "MP3",
		Duration: 180,
		IsPublic: true,
		Status:   TrackStatusCompleted,
	}
	require.NoError(t, db.Create(track).Error)

	// Test all reason values
	reasons := []BitrateAdaptationReason{
		BitrateReasonNetworkSlow,
		BitrateReasonNetworkFast,
		BitrateReasonUserSelected,
		BitrateReasonBufferLow,
	}

	for _, reason := range reasons {
		log := &BitrateAdaptationLog{
			TrackID:    track.ID,
			UserID:     user.ID,
			OldBitrate: 128,
			NewBitrate: 192,
			Reason:     reason,
		}
		err := db.Create(log).Error
		require.NoError(t, err, "Failed to create log with reason: %s", reason)
		assert.Equal(t, reason, log.Reason)
	}
}

func TestBitrateAdaptationLog_Indexes(t *testing.T) {
	db := setupTestBitrateAdaptationDB(t)

	userID := uuid.New()

	// Create test user and track
	user := &User{
		ID:       userID,
		Username: "testuser",
		Email:    "test@example.com",
		IsActive: true,
	}
	require.NoError(t, db.Create(user).Error)

	track := &Track{
		UserID:   userID,
		Title:    "Test Track",
		FilePath: "/test/track.mp3",
		FileSize: 5 * 1024 * 1024,
		Format:   "MP3",
		Duration: 180,
		IsPublic: true,
		Status:   TrackStatusCompleted,
	}
	require.NoError(t, db.Create(track).Error)

	// Create multiple logs
	for i := 0; i < 5; i++ {
		log := &BitrateAdaptationLog{
			TrackID:    track.ID,
			UserID:     user.ID,
			OldBitrate: 128 + i*32,
			NewBitrate: 192 + i*32,
			Reason:     BitrateReasonNetworkFast,
		}
		require.NoError(t, db.Create(log).Error)
	}

	// Test query by track_id (should use index)
	var logsByTrack []BitrateAdaptationLog
	err := db.Where("track_id = ?", track.ID).Find(&logsByTrack).Error
	require.NoError(t, err)
	assert.Equal(t, 5, len(logsByTrack))

	// Test query by user_id (should use index)
	var logsByUser []BitrateAdaptationLog
	err = db.Where("user_id = ?", user.ID).Find(&logsByUser).Error
	require.NoError(t, err)
	assert.Equal(t, 5, len(logsByUser))

	// Test query by created_at (should use index)
	var logsByDate []BitrateAdaptationLog
	now := time.Now()
	err = db.Where("created_at >= ?", now.Add(-1*time.Hour)).Find(&logsByDate).Error
	require.NoError(t, err)
	assert.GreaterOrEqual(t, len(logsByDate), 5)
}

func TestBitrateAdaptationLog_TableName(t *testing.T) {
	log := BitrateAdaptationLog{}
	assert.Equal(t, "bitrate_adaptation_logs", log.TableName())
}

// Helper function
func intPtr(i int) *int {
	return &i
}
