package models

import (
	"testing"
	"time"

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

func setupTestTrackLikeDB(t *testing.T) (*gorm.DB, func()) {
	// Setup in-memory SQLite database
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	assert.NoError(t, err)
	db.Exec("PRAGMA foreign_keys = ON")
	sqlDB, _ := db.DB()
	sqlDB.SetMaxOpenConns(1)

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

	// Cleanup function
	cleanup := func() {
		// Database will be closed automatically
	}

	return db, cleanup
}

func TestTrackLike_Create(t *testing.T) {
	db, cleanup := setupTestTrackLikeDB(t)
	defer cleanup()

	userID := uuid.New()
	// Create test user
	user := &User{
		ID:       userID,
		Username: "testuser",
		Email:    "test@example.com",
		IsActive: true,
	}
	err := db.Create(user).Error
	assert.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
	assert.NoError(t, err)

	// Create track like
	trackLike := &TrackLike{
		UserID:  userID,
		TrackID: track.ID,
	}
	err = db.Create(trackLike).Error
	assert.NoError(t, err)

	// Verify track like was created
	var createdLike TrackLike
	err = db.First(&createdLike, trackLike.ID).Error
	assert.NoError(t, err)
	assert.Equal(t, userID, createdLike.UserID)
	assert.Equal(t, track.ID, createdLike.TrackID)
	assert.NotZero(t, createdLike.CreatedAt)
}

func TestTrackLike_UniqueConstraint(t *testing.T) {
	db, cleanup := setupTestTrackLikeDB(t)
	defer cleanup()

	userID := uuid.New()
	// Create test user
	user := &User{
		ID:       userID,
		Username: "testuser",
		Email:    "test@example.com",
		IsActive: true,
	}
	err := db.Create(user).Error
	assert.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
	assert.NoError(t, err)

	// Create first track like
	trackLike1 := &TrackLike{
		UserID:  userID,
		TrackID: track.ID,
	}
	err = db.Create(trackLike1).Error
	assert.NoError(t, err)

	// Try to create duplicate like (should fail due to unique constraint)
	trackLike2 := &TrackLike{
		UserID:  userID,
		TrackID: track.ID,
	}
	err = db.Create(trackLike2).Error
	assert.Error(t, err)
	// SQLite doesn't enforce unique constraints the same way as PostgreSQL,
	// but GORM should still catch this
}

func TestTrackLike_Relations(t *testing.T) {
	db, cleanup := setupTestTrackLikeDB(t)
	defer cleanup()

	userID := uuid.New()
	// Create test user
	user := &User{
		ID:       userID,
		Username: "testuser",
		Email:    "test@example.com",
		IsActive: true,
	}
	err := db.Create(user).Error
	assert.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
	assert.NoError(t, err)

	// Create track like
	trackLike := &TrackLike{
		UserID:  userID,
		TrackID: track.ID,
	}
	err = db.Create(trackLike).Error
	assert.NoError(t, err)

	// Test relation with User
	var likeWithUser TrackLike
	err = db.Preload("User").First(&likeWithUser, trackLike.ID).Error
	assert.NoError(t, err)
	assert.Equal(t, "testuser", likeWithUser.User.Username)
	assert.Equal(t, "test@example.com", likeWithUser.User.Email)

	// Test relation with Track
	var likeWithTrack TrackLike
	err = db.Preload("Track").First(&likeWithTrack, trackLike.ID).Error
	assert.NoError(t, err)
	assert.Equal(t, "Test Track", likeWithTrack.Track.Title)
	assert.Equal(t, userID, likeWithTrack.Track.UserID)
}

func TestTrackLike_CascadeDelete(t *testing.T) {
	db, cleanup := setupTestTrackLikeDB(t)
	defer cleanup()

	userID := uuid.New()
	// Create test user
	user := &User{
		ID:       userID,
		Username: "testuser",
		Email:    "test@example.com",
		IsActive: true,
	}
	err := db.Create(user).Error
	assert.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
	assert.NoError(t, err)

	// Create track like
	trackLike := &TrackLike{
		UserID:  userID,
		TrackID: track.ID,
	}
	err = db.Create(trackLike).Error
	assert.NoError(t, err)

	// Delete track (should cascade delete the like)
	err = db.Unscoped().Delete(track).Error
	assert.NoError(t, err)

	// Verify like was deleted
	var deletedLike TrackLike
	err = db.First(&deletedLike, trackLike.ID).Error
	assert.Error(t, err)
	assert.Equal(t, gorm.ErrRecordNotFound, err)
}

func TestTrackLike_TableName(t *testing.T) {
	trackLike := TrackLike{}
	assert.Equal(t, "track_likes", trackLike.TableName())
}

func TestTrackLike_Indexes(t *testing.T) {
	db, cleanup := setupTestTrackLikeDB(t)
	defer cleanup()

	userID1 := uuid.New()
	userID2 := uuid.New()

	// Create test users
	user1 := &User{
		ID:       userID1,
		Username: "testuser1",
		Email:    "test1@example.com",
		IsActive: true,
	}
	err := db.Create(user1).Error
	assert.NoError(t, err)

	user2 := &User{
		ID:       userID2,
		Username: "testuser2",
		Email:    "test2@example.com",
		IsActive: true,
	}
	err = db.Create(user2).Error
	assert.NoError(t, err)

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

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

	// Create multiple likes
	like1 := &TrackLike{UserID: userID1, TrackID: track1.ID}
	err = db.Create(like1).Error
	assert.NoError(t, err)

	like2 := &TrackLike{UserID: userID1, TrackID: track2.ID}
	err = db.Create(like2).Error
	assert.NoError(t, err)

	like3 := &TrackLike{UserID: userID2, TrackID: track1.ID}
	err = db.Create(like3).Error
	assert.NoError(t, err)

	// Test query by user_id (should use index)
	var userLikes []TrackLike
	err = db.Where("user_id = ?", userID1).Find(&userLikes).Error
	assert.NoError(t, err)
	assert.Equal(t, 2, len(userLikes))

	// Test query by track_id (should use index)
	var trackLikes []TrackLike
	err = db.Where("track_id = ?", track1.ID).Find(&trackLikes).Error
	assert.NoError(t, err)
	assert.Equal(t, 2, len(trackLikes))
}

func TestTrackLike_CreatedAt(t *testing.T) {
	db, cleanup := setupTestTrackLikeDB(t)
	defer cleanup()

	userID := uuid.New()
	// Create test user
	user := &User{
		ID:       userID,
		Username: "testuser",
		Email:    "test@example.com",
		IsActive: true,
	}
	err := db.Create(user).Error
	assert.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
	assert.NoError(t, err)

	// Create track like
	beforeCreate := time.Now()
	trackLike := &TrackLike{
		UserID:  userID,
		TrackID: track.ID,
	}
	err = db.Create(trackLike).Error
	assert.NoError(t, err)
	afterCreate := time.Now()

	// Verify CreatedAt is set
	assert.WithinDuration(t, beforeCreate, trackLike.CreatedAt, time.Second)
	assert.WithinDuration(t, afterCreate, trackLike.CreatedAt, time.Second)
}
