package service_test

import (
	"context"
	"testing"

	"github.com/user-management-system/internal/domain"
	"github.com/user-management-system/internal/repository"
	"github.com/user-management-system/internal/service"
	gormsqlite "gorm.io/driver/sqlite"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

// =============================================================================
// TOTP Service Tests
// =============================================================================

func setupTOTPTestEnv(t *testing.T) (*service.TOTPService, *gorm.DB) {
	t.Helper()

	db, err := gorm.Open(gormsqlite.New(gormsqlite.Config{
		DriverName: "sqlite",
		DSN:        "file:totp_test?mode=memory&cache=shared",
	}), &gorm.Config{
		Logger: logger.Default.LogMode(logger.Silent),
	})
	if err != nil {
		t.Fatalf("failed to connect database: %v", err)
	}

	if err := db.AutoMigrate(&domain.User{}); err != nil {
		t.Fatalf("failed to migrate: %v", err)
	}

	userRepo := repository.NewUserRepository(db)
	totpSvc := service.NewTOTPService(userRepo)

	return totpSvc, db
}

func TestTOTPService_SetupTOTP(t *testing.T) {
	svc, db := setupTOTPTestEnv(t)
	ctx := context.Background()

	// Create test user
	user := &domain.User{
		Username: "totpuser",
		Password: "$2a$10$hash",
		Status:   domain.UserStatusActive,
	}
	db.Create(user)

	t.Run("Setup TOTP for non-existent user", func(t *testing.T) {
		_, err := svc.SetupTOTP(ctx, 99999)
		if err == nil {
			t.Error("Expected error for non-existent user")
		}
	})

	t.Run("Setup TOTP for existing user", func(t *testing.T) {
		resp, err := svc.SetupTOTP(ctx, user.ID)
		if err != nil {
			t.Fatalf("SetupTOTP failed: %v", err)
		}
		if resp.Secret == "" {
			t.Error("Expected secret to be returned")
		}
		if resp.QRCodeBase64 == "" {
			t.Error("Expected QR code to be returned")
		}
		if len(resp.RecoveryCodes) == 0 {
			t.Error("Expected recovery codes to be returned")
		}
	})

	t.Run("Setup TOTP for user with TOTP already enabled", func(t *testing.T) {
		// Enable TOTP for user first
		db.Model(&domain.User{}).Where("id = ?", user.ID).Update("totp_enabled", true)

		_, err := svc.SetupTOTP(ctx, user.ID)
		if err == nil {
			t.Error("Expected error for user with TOTP already enabled")
		}
	})
}

func TestTOTPService_GetTOTPStatus(t *testing.T) {
	svc, db := setupTOTPTestEnv(t)
	ctx := context.Background()

	// Create test user
	user := &domain.User{
		Username: "totpstatususer",
		Password: "$2a$10$hash",
		Status:   domain.UserStatusActive,
	}
	db.Create(user)

	t.Run("Get TOTP status for non-existent user", func(t *testing.T) {
		_, err := svc.GetTOTPStatus(ctx, 99999)
		if err == nil {
			t.Error("Expected error for non-existent user")
		}
	})

	t.Run("Get TOTP status for existing user", func(t *testing.T) {
		enabled, err := svc.GetTOTPStatus(ctx, user.ID)
		if err != nil {
			t.Fatalf("GetTOTPStatus failed: %v", err)
		}
		if enabled {
			t.Error("Expected TOTP to be disabled by default")
		}
	})
}

func TestTOTPService_EnableTOTP(t *testing.T) {
	svc, db := setupTOTPTestEnv(t)
	ctx := context.Background()

	// Create test user
	user := &domain.User{
		Username: "enabletotpuser",
		Password: "$2a$10$hash",
		Status:   domain.UserStatusActive,
	}
	db.Create(user)

	t.Run("Enable TOTP for non-existent user", func(t *testing.T) {
		err := svc.EnableTOTP(ctx, 99999, "123456")
		if err == nil {
			t.Error("Expected error for non-existent user")
		}
	})

	t.Run("Enable TOTP without setup", func(t *testing.T) {
		err := svc.EnableTOTP(ctx, user.ID, "123456")
		if err == nil {
			t.Error("Expected error when TOTP not set up")
		}
	})

	t.Run("Enable TOTP with empty code", func(t *testing.T) {
		// Setup TOTP first
		user2 := &domain.User{
			Username:   "enabletotpuser2",
			Password:   "$2a$10$hash",
			Status:     domain.UserStatusActive,
			TOTPSecret: "JBSWY3DPEHPK3PXP",
		}
		db.Create(user2)

		err := svc.EnableTOTP(ctx, user2.ID, "")
		if err == nil {
			t.Error("Expected error for empty code")
		}
	})
}

func TestTOTPService_DisableTOTP(t *testing.T) {
	svc, db := setupTOTPTestEnv(t)
	ctx := context.Background()

	// Create test user
	user := &domain.User{
		Username:    "disabletotpuser",
		Password:    "$2a$10$hash",
		Status:      domain.UserStatusActive,
		TOTPEnabled: true,
		TOTPSecret:  "testsecret",
	}
	db.Create(user)

	t.Run("Disable TOTP for non-existent user", func(t *testing.T) {
		err := svc.DisableTOTP(ctx, 99999, "123456")
		if err == nil {
			t.Error("Expected error for non-existent user")
		}
	})

	t.Run("Disable TOTP without setup", func(t *testing.T) {
		// Create user without TOTP
		user2 := &domain.User{
			Username: "nototpsetup",
			Password: "$2a$10$hash",
			Status:   domain.UserStatusActive,
		}
		db.Create(user2)

		err := svc.DisableTOTP(ctx, user2.ID, "123456")
		if err == nil {
			t.Error("Expected error when TOTP not enabled")
		}
	})

	t.Run("Disable TOTP with wrong code", func(t *testing.T) {
		err := svc.DisableTOTP(ctx, user.ID, "wrongcode")
		if err == nil {
			t.Error("Expected error for wrong code")
		}
	})

	t.Run("Disable TOTP with empty code", func(t *testing.T) {
		err := svc.DisableTOTP(ctx, user.ID, "")
		if err == nil {
			t.Error("Expected error for empty code")
		}
	})
}

func TestTOTPService_VerifyTOTP(t *testing.T) {
	svc, db := setupTOTPTestEnv(t)
	ctx := context.Background()

	// Create test user without TOTP
	user := &domain.User{
		Username: "verifytotpuser",
		Password: "$2a$10$hash",
		Status:   domain.UserStatusActive,
	}
	db.Create(user)

	t.Run("Verify TOTP for non-existent user", func(t *testing.T) {
		err := svc.VerifyTOTP(ctx, 99999, "123456")
		if err == nil {
			t.Error("Expected error for non-existent user")
		}
	})

	t.Run("Verify TOTP when disabled", func(t *testing.T) {
		// When TOTP is disabled, VerifyTOTP should return nil (no error)
		err := svc.VerifyTOTP(ctx, user.ID, "123456")
		if err != nil {
			t.Errorf("Expected no error when TOTP disabled, got: %v", err)
		}
	})
}
