package service_test

import (
	"context"
	"testing"

	"github.com/user-management-system/internal/cache"
	"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"
)

// =============================================================================
// Password Reset Service Tests
// =============================================================================

func setupPasswordResetTestEnv(t *testing.T) (*service.PasswordResetService, *gorm.DB) {
	t.Helper()

	db, err := gorm.Open(gormsqlite.New(gormsqlite.Config{
		DriverName: "sqlite",
		DSN:        "file:pwdreset_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)
	l1Cache := cache.NewL1Cache()
	l2Cache := cache.NewRedisCache(false)
	cacheManager := cache.NewCacheManager(l1Cache, l2Cache)
	cfg := service.DefaultPasswordResetConfig()
	svc := service.NewPasswordResetService(userRepo, cacheManager, cfg)

	return svc, db
}

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

	// Create test user with email
	email := "reset@test.com"
	user := &domain.User{
		Username: "resetuser",
		Password: "$2a$10$hash",
		Email:    &email,
		Status:   domain.UserStatusActive,
	}
	db.Create(user)

	t.Run("Forgot password for existing email", func(t *testing.T) {
		err := svc.ForgotPassword(ctx, "reset@test.com")
		// Should not return error even if email sending fails
		_ = err
		t.Logf("ForgotPassword returned: %v", err)
	})

	t.Run("Forgot password for non-existent email", func(t *testing.T) {
		err := svc.ForgotPassword(ctx, "nonexistent@test.com")
		// Should return nil to avoid user enumeration
		if err != nil {
			t.Errorf("Expected nil for non-existent email, got: %v", err)
		}
	})

	t.Run("Forgot password with empty email", func(t *testing.T) {
		err := svc.ForgotPassword(ctx, "")
		_ = err
		t.Logf("ForgotPassword with empty email returned: %v", err)
	})
}

func TestPasswordResetService_ResetPassword(t *testing.T) {
	svc, _ := setupPasswordResetTestEnv(t)
	ctx := context.Background()

	t.Run("Reset password with invalid token", func(t *testing.T) {
		err := svc.ResetPassword(ctx, "invalid_token", "NewPassword123!")
		if err == nil {
			t.Error("Expected error for invalid token")
		}
	})

	t.Run("Reset password with empty token", func(t *testing.T) {
		err := svc.ResetPassword(ctx, "", "NewPassword123!")
		if err == nil {
			t.Error("Expected error for empty token")
		}
	})

	t.Run("Reset password with empty password", func(t *testing.T) {
		err := svc.ResetPassword(ctx, "some_token", "")
		if err == nil {
			t.Error("Expected error for empty password")
		}
	})
}

func TestPasswordResetService_ValidateResetToken(t *testing.T) {
	svc, _ := setupPasswordResetTestEnv(t)
	ctx := context.Background()

	t.Run("Validate invalid token", func(t *testing.T) {
		valid, err := svc.ValidateResetToken(ctx, "invalid_token")
		if err != nil {
			t.Fatalf("ValidateResetToken should not return error: %v", err)
		}
		if valid {
			t.Error("Expected token to be invalid")
		}
	})

	t.Run("Validate empty token", func(t *testing.T) {
		_, err := svc.ValidateResetToken(ctx, "")
		if err == nil {
			t.Error("Expected error for empty token")
		}
	})
}

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

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

	t.Run("Forgot password by phone for existing user", func(t *testing.T) {
		_, err := svc.ForgotPasswordByPhone(ctx, "13800138000")
		// May fail if SMS service not configured
		_ = err
		t.Logf("ForgotPasswordByPhone returned: %v", err)
	})

	t.Run("Forgot password by phone for non-existent user", func(t *testing.T) {
		_, err := svc.ForgotPasswordByPhone(ctx, "19999999999")
		// Should return nil to avoid user enumeration
		_ = err
		t.Logf("ForgotPasswordByPhone non-existent returned: %v", err)
	})
}

func TestPasswordResetService_ResetPasswordByPhone(t *testing.T) {
	svc, _ := setupPasswordResetTestEnv(t)
	ctx := context.Background()

	t.Run("Reset password by phone with invalid code", func(t *testing.T) {
		req := &service.ResetPasswordByPhoneRequest{
			Phone:       "13800138000",
			Code:        "invalid_code",
			NewPassword: "NewPassword123!",
		}
		err := svc.ResetPasswordByPhone(ctx, req)
		if err == nil {
			t.Error("Expected error for invalid code")
		}
	})

	t.Run("Reset password by phone with empty fields", func(t *testing.T) {
		req := &service.ResetPasswordByPhoneRequest{}
		err := svc.ResetPasswordByPhone(ctx, req)
		if err == nil {
			t.Error("Expected error for empty fields")
		}
	})
}

func TestPasswordResetService_WithPasswordHistoryRepo(t *testing.T) {
	svc, _ := setupPasswordResetTestEnv(t)

	t.Run("WithPasswordHistoryRepo sets repository", func(t *testing.T) {
		result := svc.WithPasswordHistoryRepo(nil)
		if result == nil {
			t.Error("Expected service to be returned")
		}
	})
}

// =============================================================================
// ResetPassword Extended Tests
// =============================================================================

func TestPasswordResetService_ResetPassword_Extended(t *testing.T) {
	svc, _ := setupPasswordResetTestEnv(t)
	ctx := context.Background()

	t.Run("ResetPassword with empty token", func(t *testing.T) {
		err := svc.ResetPassword(ctx, "", "NewPassword123!")
		if err == nil {
			t.Error("Expected error for empty token")
		}
	})

	t.Run("ResetPassword with empty password", func(t *testing.T) {
		err := svc.ResetPassword(ctx, "sometoken", "")
		if err == nil {
			t.Error("Expected error for empty password")
		}
	})

	t.Run("ResetPassword with weak password", func(t *testing.T) {
		err := svc.ResetPassword(ctx, "sometoken", "weak")
		if err == nil {
			t.Error("Expected error for weak password")
		}
	})

	t.Run("ResetPassword with invalid token", func(t *testing.T) {
		err := svc.ResetPassword(ctx, "invalid_token", "NewPassword123!")
		if err == nil {
			t.Error("Expected error for invalid token")
		}
	})
}

func TestPasswordResetService_ResetPasswordByPhone_Extended(t *testing.T) {
	svc, _ := setupPasswordResetTestEnv(t)
	ctx := context.Background()

	t.Run("ResetPasswordByPhone with empty phone", func(t *testing.T) {
		req := &service.ResetPasswordByPhoneRequest{
			Code:        "123456",
			NewPassword: "NewPassword123!",
		}
		err := svc.ResetPasswordByPhone(ctx, req)
		if err == nil {
			t.Error("Expected error for empty phone")
		}
	})

	t.Run("ResetPasswordByPhone with empty code", func(t *testing.T) {
		req := &service.ResetPasswordByPhoneRequest{
			Phone:       "13800138000",
			NewPassword: "NewPassword123!",
		}
		err := svc.ResetPasswordByPhone(ctx, req)
		if err == nil {
			t.Error("Expected error for empty code")
		}
	})
}
