package database

import (
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
	"veza-backend-api/internal/models"
)

// TestPasswordResetTokensTable_Creation teste que la table password_reset_tokens est créée correctement
func TestPasswordResetTokensTable_Creation(t *testing.T) {
	// Créer une base de données en mémoire
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	require.NoError(t, err, "Failed to open test database")

	// Créer la table users d'abord (requis pour la foreign key)
	err = db.AutoMigrate(&models.User{})
	require.NoError(t, err, "Failed to migrate users table")

	// Créer la table password_reset_tokens manuellement (simule la migration SQL)
	// Note: SQLite stocke UUIDs comme TEXT, user_id est maintenant UUID
	err = db.Exec(`
		CREATE TABLE password_reset_tokens (
			id TEXT PRIMARY KEY,
			user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
			token TEXT NOT NULL UNIQUE,
			expires_at TIMESTAMP NOT NULL,
			used INTEGER NOT NULL DEFAULT 0,
			created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
		)
	`).Error
	require.NoError(t, err, "Failed to create password_reset_tokens table")

	// Créer les index
	err = db.Exec("CREATE INDEX idx_password_reset_tokens_token ON password_reset_tokens(token)").Error
	require.NoError(t, err)
	err = db.Exec("CREATE INDEX idx_password_reset_tokens_user_id ON password_reset_tokens(user_id)").Error
	require.NoError(t, err)
	err = db.Exec("CREATE INDEX idx_password_reset_tokens_expires_at ON password_reset_tokens(expires_at)").Error
	require.NoError(t, err)

	// Vérifier que la table existe
	hasTable := db.Migrator().HasTable("password_reset_tokens")
	assert.True(t, hasTable, "password_reset_tokens table should exist")
}

// TestPasswordResetTokensTable_Columns teste que toutes les colonnes sont présentes
func TestPasswordResetTokensTable_Columns(t *testing.T) {
	// Créer une base de données en mémoire
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	require.NoError(t, err)

	// Créer la table users
	err = db.AutoMigrate(&models.User{})
	require.NoError(t, err)

	// Créer un utilisateur de test
	user := &models.User{
		Email:    "test@example.com",
		Username: "testuser",
		Role:     "user",
		IsActive: true,
	}
	err = db.Create(user).Error
	require.NoError(t, err)

	// Créer la table password_reset_tokens
	err = db.Exec(`
		CREATE TABLE password_reset_tokens (
			id INTEGER PRIMARY KEY AUTOINCREMENT,
			user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
			token TEXT NOT NULL UNIQUE,
			expires_at TIMESTAMP NOT NULL,
			used INTEGER NOT NULL DEFAULT 0,
			created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
		)
	`).Error
	require.NoError(t, err)

	// Vérifier que toutes les colonnes existent en insérant un token
	expiresAt := time.Now().Add(1 * time.Hour)
	err = db.Exec(`
		INSERT INTO password_reset_tokens (user_id, token, expires_at, used, created_at)
		VALUES (?, ?, ?, ?, ?)
	`, user.ID, "test-token-123", expiresAt, false, time.Now()).Error
	require.NoError(t, err, "Should be able to insert a password reset token")

	// Vérifier que le token a été inséré
	var count int64
	err = db.Raw("SELECT COUNT(*) FROM password_reset_tokens WHERE token = ?", "test-token-123").Scan(&count).Error
	require.NoError(t, err)
	assert.Equal(t, int64(1), count, "Token should be inserted")
}

// TestPasswordResetTokensTable_ForeignKey teste que la foreign key fonctionne correctement
func TestPasswordResetTokensTable_ForeignKey(t *testing.T) {
	// Créer une base de données en mémoire
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	require.NoError(t, err)

	// Activer les foreign keys pour SQLite (requis pour CASCADE DELETE)
	err = db.Exec("PRAGMA foreign_keys = ON").Error
	require.NoError(t, err)

	// Créer la table users
	err = db.AutoMigrate(&models.User{})
	require.NoError(t, err)

	// Créer la table password_reset_tokens
	err = db.Exec(`
		CREATE TABLE password_reset_tokens (
			id INTEGER PRIMARY KEY AUTOINCREMENT,
			user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
			token TEXT NOT NULL UNIQUE,
			expires_at TIMESTAMP NOT NULL,
			used INTEGER NOT NULL DEFAULT 0,
			created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
		)
	`).Error
	require.NoError(t, err)

	// Créer un utilisateur
	user := &models.User{
		Email:    "test@example.com",
		Username: "testuser",
		Role:     "user",
		IsActive: true,
	}
	err = db.Create(user).Error
	require.NoError(t, err)

	// Insérer un token valide
	expiresAt := time.Now().Add(1 * time.Hour)
	err = db.Exec(`
		INSERT INTO password_reset_tokens (user_id, token, expires_at, used, created_at)
		VALUES (?, ?, ?, ?, ?)
	`, user.ID, "valid-token", expiresAt, false, time.Now()).Error
	require.NoError(t, err, "Should be able to insert token for existing user")

	// Tenter d'insérer un token avec un user_id inexistant (devrait échouer)
	// Utiliser un UUID valide mais inexistant
	fakeUserID := "00000000-0000-0000-0000-000000000999"
	err = db.Exec(`
		INSERT INTO password_reset_tokens (user_id, token, expires_at, used, created_at)
		VALUES (?, ?, ?, ?, ?)
	`, fakeUserID, "invalid-token", expiresAt, false, time.Now()).Error
	assert.Error(t, err, "Should not be able to insert token with non-existent user_id")

	// Vérifier que le CASCADE DELETE fonctionne
	// Utiliser Unscoped() pour forcer la suppression réelle (pas soft delete)
	err = db.Unscoped().Delete(user).Error
	require.NoError(t, err)

	// Vérifier que le token a été supprimé automatiquement
	var count int64
	err = db.Raw("SELECT COUNT(*) FROM password_reset_tokens WHERE token = ?", "valid-token").Scan(&count).Error
	require.NoError(t, err)
	assert.Equal(t, int64(0), count, "Token should be deleted when user is deleted")
}

// TestPasswordResetTokensTable_UniqueToken teste que le token doit être unique
func TestPasswordResetTokensTable_UniqueToken(t *testing.T) {
	// Créer une base de données en mémoire
	db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
	require.NoError(t, err)

	// Créer la table users
	err = db.AutoMigrate(&models.User{})
	require.NoError(t, err)

	// Créer la table password_reset_tokens
	err = db.Exec(`
		CREATE TABLE password_reset_tokens (
			id INTEGER PRIMARY KEY AUTOINCREMENT,
			user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
			token TEXT NOT NULL UNIQUE,
			expires_at TIMESTAMP NOT NULL,
			used INTEGER NOT NULL DEFAULT 0,
			created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
		)
	`).Error
	require.NoError(t, err)

	// Créer un utilisateur
	user := &models.User{
		Email:    "test@example.com",
		Username: "testuser",
		Role:     "user",
		IsActive: true,
	}
	err = db.Create(user).Error
	require.NoError(t, err)

	// Insérer un token
	expiresAt := time.Now().Add(1 * time.Hour)
	err = db.Exec(`
		INSERT INTO password_reset_tokens (user_id, token, expires_at, used, created_at)
		VALUES (?, ?, ?, ?, ?)
	`, user.ID, "unique-token", expiresAt, false, time.Now()).Error
	require.NoError(t, err, "Should be able to insert first token")

	// Tenter d'insérer un token avec le même token (devrait échouer)
	err = db.Exec(`
		INSERT INTO password_reset_tokens (user_id, token, expires_at, used, created_at)
		VALUES (?, ?, ?, ?, ?)
	`, user.ID, "unique-token", expiresAt, false, time.Now()).Error
	assert.Error(t, err, "Should not be able to insert duplicate token")
}
