package database

import (
	"path/filepath"
	"testing"

	"github.com/user-management-system/internal/config"
	"github.com/user-management-system/internal/domain"
)

func newTestConfig(t *testing.T) *config.Config {
	t.Helper()

	return &config.Config{
		Database: config.DatabaseConfig{
			DBName: filepath.Join(t.TempDir(), "test.db"),
		},
	}
}

func newTestDB(t *testing.T, cfg *config.Config) *DB {
	t.Helper()

	db, err := NewDB(cfg)
	if err != nil {
		t.Fatalf("NewDB failed: %v", err)
	}

	sqlDB, err := db.DB.DB()
	if err != nil {
		t.Fatalf("resolve sql.DB failed: %v", err)
	}

	t.Cleanup(func() {
		_ = sqlDB.Close()
	})

	return db
}

func TestAutoMigrateSeedsDefaultRolesAndPermissions(t *testing.T) {
	cfg := newTestConfig(t)

	db := newTestDB(t, cfg)

	if err := db.AutoMigrate(cfg); err != nil {
		t.Fatalf("AutoMigrate failed: %v", err)
	}

	var roleCount int64
	if err := db.DB.Model(&domain.Role{}).Count(&roleCount).Error; err != nil {
		t.Fatalf("count roles failed: %v", err)
	}
	if roleCount != int64(len(domain.PredefinedRoles)) {
		t.Fatalf("expected %d predefined roles, got %d", len(domain.PredefinedRoles), roleCount)
	}

	var permissionCount int64
	if err := db.DB.Model(&domain.Permission{}).Count(&permissionCount).Error; err != nil {
		t.Fatalf("count permissions failed: %v", err)
	}
	if permissionCount == 0 {
		t.Fatal("expected default permissions to be seeded")
	}

	var userCount int64
	if err := db.DB.Model(&domain.User{}).Count(&userCount).Error; err != nil {
		t.Fatalf("count users failed: %v", err)
	}
	if userCount != 0 {
		t.Fatalf("expected no users when admin config is empty, got %d users", userCount)
	}
}

func TestAutoMigrateCreatesAllTables(t *testing.T) {
	cfg := newTestConfig(t)

	db := newTestDB(t, cfg)

	if err := db.AutoMigrate(cfg); err != nil {
		t.Fatalf("AutoMigrate failed: %v", err)
	}

	tables := []interface{}{
		&domain.User{},
		&domain.Role{},
		&domain.Permission{},
		&domain.UserRole{},
		&domain.RolePermission{},
		&domain.Device{},
		&domain.LoginLog{},
		&domain.OperationLog{},
		&domain.SocialAccount{},
		&domain.Webhook{},
		&domain.WebhookDelivery{},
		&domain.PasswordHistory{},
	}

	for _, table := range tables {
		if !db.DB.Migrator().HasTable(table) {
			t.Fatalf("expected table %T to exist", table)
		}
	}
}

func TestInitDefaultDataUpgradePathSeedsPermissionsForExistingRoles(t *testing.T) {
	cfg := newTestConfig(t)

	db := newTestDB(t, cfg)

	if err := db.DB.AutoMigrate(
		&domain.User{},
		&domain.Role{},
		&domain.Permission{},
		&domain.UserRole{},
		&domain.RolePermission{},
		&domain.Device{},
		&domain.LoginLog{},
		&domain.OperationLog{},
		&domain.SocialAccount{},
		&domain.Webhook{},
		&domain.WebhookDelivery{},
		&domain.PasswordHistory{},
	); err != nil {
		t.Fatalf("create schema failed: %v", err)
	}

	for _, predefinedRole := range domain.PredefinedRoles {
		role := predefinedRole
		if err := db.DB.Create(&role).Error; err != nil {
			t.Fatalf("seed role %s failed: %v", role.Code, err)
		}
	}

	if err := db.initDefaultData(cfg); err != nil {
		t.Fatalf("initDefaultData failed: %v", err)
	}

	var permissionCount int64
	if err := db.DB.Model(&domain.Permission{}).Count(&permissionCount).Error; err != nil {
		t.Fatalf("count permissions failed: %v", err)
	}
	if permissionCount == 0 {
		t.Fatal("expected permissions to be backfilled for existing roles")
	}

	var adminRole domain.Role
	if err := db.DB.Where("code = ?", "admin").First(&adminRole).Error; err != nil {
		t.Fatalf("load admin role failed: %v", err)
	}

	var adminRolePermissionCount int64
	if err := db.DB.Model(&domain.RolePermission{}).Where("role_id = ?", adminRole.ID).Count(&adminRolePermissionCount).Error; err != nil {
		t.Fatalf("count admin role permissions failed: %v", err)
	}
	if adminRolePermissionCount == 0 {
		t.Fatal("expected admin role permissions to be backfilled on upgrade path")
	}
}

func TestNewDBWithValidConfig(t *testing.T) {
	tmpDir := t.TempDir()
	dbPath := filepath.Join(tmpDir, "test.db")

	cfg := &config.Config{
		Database: config.DatabaseConfig{
			DBName: dbPath,
		},
	}

	db, err := NewDB(cfg)
	if err != nil {
		t.Fatalf("NewDB failed: %v", err)
	}

	if db == nil {
		t.Fatal("expected non-nil DB")
	}

	sqlDB, err := db.DB.DB()
	if err != nil {
		t.Fatalf("resolve sql.DB failed: %v", err)
	}

	if err := sqlDB.Close(); err != nil {
		t.Fatalf("close sql.DB failed: %v", err)
	}
}
