package service_test

import (
	"context"
	"testing"

	"github.com/user-management-system/internal/auth"
	"github.com/user-management-system/internal/cache"
	"github.com/user-management-system/internal/domain"
	"github.com/user-management-system/internal/service"
)

// =============================================================================
// Auth Contact Binding Tests
// =============================================================================

func setupContactBindingTestEnv(t *testing.T) *authTestEnv {
	t.Helper()
	env := setupAuthTestEnv(t)
	if env == nil {
		return nil
	}

	// Setup email code service
	l1Cache := cache.NewL1Cache()
	l2Cache := cache.NewRedisCache(false)
	cacheManager := cache.NewCacheManager(l1Cache, l2Cache)
	emailProvider := &service.MockEmailProvider{}
	emailCodeSvc := service.NewEmailCodeService(emailProvider, cacheManager, service.DefaultEmailCodeConfig())
	env.authSvc.SetEmailCodeService(emailCodeSvc)

	// Setup SMS code service
	smsProvider := &service.MockSMSProvider{}
	smsCodeSvc := service.NewSMSCodeService(smsProvider, cacheManager, service.DefaultSMSCodeConfig())
	env.authSvc.SetSMSCodeService(smsCodeSvc)

	return env
}

func TestAuthService_SendEmailBindCode(t *testing.T) {
	env := setupContactBindingTestEnv(t)
	if env == nil {
		return
	}
	ctx := context.Background()

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

	t.Run("Send email bind code with nil service", func(t *testing.T) {
		var nilSvc *service.AuthService
		err := nilSvc.SendEmailBindCode(ctx, 1, "test@test.com")
		if err == nil {
			t.Error("Expected error for nil service")
		}
	})

	t.Run("Send email bind code for non-existent user", func(t *testing.T) {
		err := env.authSvc.SendEmailBindCode(ctx, 9999, "test@test.com")
		if err == nil {
			t.Error("Expected error for non-existent user")
		}
	})

	t.Run("Send email bind code with empty email", func(t *testing.T) {
		err := env.authSvc.SendEmailBindCode(ctx, user.ID, "")
		if err == nil {
			t.Error("Expected error for empty email")
		}
	})

	t.Run("Send email bind code success", func(t *testing.T) {
		err := env.authSvc.SendEmailBindCode(ctx, user.ID, "newemail@test.com")
		if err != nil {
			t.Fatalf("SendEmailBindCode failed: %v", err)
		}
	})

	t.Run("Send email bind code for already bound email", func(t *testing.T) {
		email := "alreadybound@test.com"
		userWithEmail := &domain.User{
			Username: "emailbounduser",
			Password: "$2a$10$hash",
			Status:   domain.UserStatusActive,
			Email:    &email,
		}
		env.userSvc.Create(ctx, userWithEmail)

		err := env.authSvc.SendEmailBindCode(ctx, userWithEmail.ID, email)
		if err == nil {
			t.Error("Expected error for already bound email")
		}
	})
}

func TestAuthService_BindEmail(t *testing.T) {
	env := setupContactBindingTestEnv(t)
	if env == nil {
		return
	}
	ctx := context.Background()

	// Create test user with password
	hashedPassword, _ := auth.HashPassword("Password123!")
	user := &domain.User{
		Username: "bindemailuser",
		Password: hashedPassword,
		Status:   domain.UserStatusActive,
	}
	env.userSvc.Create(ctx, user)

	t.Run("Bind email with nil service", func(t *testing.T) {
		var nilSvc *service.AuthService
		err := nilSvc.BindEmail(ctx, 1, "test@test.com", "code", "", "")
		if err == nil {
			t.Error("Expected error for nil service")
		}
	})

	t.Run("Bind email for non-existent user", func(t *testing.T) {
		err := env.authSvc.BindEmail(ctx, 9999, "test@test.com", "code", "", "")
		if err == nil {
			t.Error("Expected error for non-existent user")
		}
	})

	t.Run("Bind email with empty email", func(t *testing.T) {
		err := env.authSvc.BindEmail(ctx, user.ID, "", "code", "", "")
		if err == nil {
			t.Error("Expected error for empty email")
		}
	})

	t.Run("Bind email with wrong password", func(t *testing.T) {
		err := env.authSvc.BindEmail(ctx, user.ID, "bindemail@test.com", "123456", "wrongpassword", "")
		if err == nil {
			t.Error("Expected error for wrong password")
		}
	})
}

func TestAuthService_UnbindEmail(t *testing.T) {
	env := setupContactBindingTestEnv(t)
	if env == nil {
		return
	}
	ctx := context.Background()

	// Create test user with email and password
	hashedPassword, _ := auth.HashPassword("Password123!")
	email := "unbind@test.com"
	user := &domain.User{
		Username: "unbindemailuser",
		Password: hashedPassword,
		Status:   domain.UserStatusActive,
		Email:    &email,
	}
	env.userSvc.Create(ctx, user)

	t.Run("Unbind email with nil service", func(t *testing.T) {
		var nilSvc *service.AuthService
		err := nilSvc.UnbindEmail(ctx, 1, "", "")
		if err == nil {
			t.Error("Expected error for nil service")
		}
	})

	t.Run("Unbind email for non-existent user", func(t *testing.T) {
		err := env.authSvc.UnbindEmail(ctx, 9999, "", "")
		if err == nil {
			t.Error("Expected error for non-existent user")
		}
	})

	t.Run("Unbind email with wrong password", func(t *testing.T) {
		err := env.authSvc.UnbindEmail(ctx, user.ID, "wrongpassword", "")
		if err == nil {
			t.Error("Expected error for wrong password")
		}
	})

	t.Run("Unbind email for user without email", func(t *testing.T) {
		userNoEmail := &domain.User{
			Username: "noemailuser",
			Password: hashedPassword,
			Status:   domain.UserStatusActive,
		}
		env.userSvc.Create(ctx, userNoEmail)

		err := env.authSvc.UnbindEmail(ctx, userNoEmail.ID, "Password123!", "")
		if err == nil {
			t.Error("Expected error for user without email")
		}
	})
}

func TestAuthService_SendPhoneBindCode(t *testing.T) {
	env := setupContactBindingTestEnv(t)
	if env == nil {
		return
	}
	ctx := context.Background()

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

	t.Run("Send phone bind code with nil service", func(t *testing.T) {
		var nilSvc *service.AuthService
		_, err := nilSvc.SendPhoneBindCode(ctx, 1, "13800138000")
		if err == nil {
			t.Error("Expected error for nil service")
		}
	})

	t.Run("Send phone bind code for non-existent user", func(t *testing.T) {
		_, err := env.authSvc.SendPhoneBindCode(ctx, 9999, "13800138000")
		if err == nil {
			t.Error("Expected error for non-existent user")
		}
	})

	t.Run("Send phone bind code with empty phone", func(t *testing.T) {
		_, err := env.authSvc.SendPhoneBindCode(ctx, user.ID, "")
		if err == nil {
			t.Error("Expected error for empty phone")
		}
	})

	t.Run("Send phone bind code success", func(t *testing.T) {
		_, err := env.authSvc.SendPhoneBindCode(ctx, user.ID, "13800138001")
		if err != nil {
			t.Fatalf("SendPhoneBindCode failed: %v", err)
		}
	})
}

func TestAuthService_BindPhone(t *testing.T) {
	env := setupContactBindingTestEnv(t)
	if env == nil {
		return
	}
	ctx := context.Background()

	// Create test user with password
	hashedPassword, _ := auth.HashPassword("Password123!")
	user := &domain.User{
		Username: "bindphoneuser",
		Password: hashedPassword,
		Status:   domain.UserStatusActive,
	}
	env.userSvc.Create(ctx, user)

	t.Run("Bind phone with nil service", func(t *testing.T) {
		var nilSvc *service.AuthService
		err := nilSvc.BindPhone(ctx, 1, "13800138000", "code", "", "")
		if err == nil {
			t.Error("Expected error for nil service")
		}
	})

	t.Run("Bind phone for non-existent user", func(t *testing.T) {
		err := env.authSvc.BindPhone(ctx, 9999, "13800138000", "code", "", "")
		if err == nil {
			t.Error("Expected error for non-existent user")
		}
	})

	t.Run("Bind phone with empty phone", func(t *testing.T) {
		err := env.authSvc.BindPhone(ctx, user.ID, "", "code", "", "")
		if err == nil {
			t.Error("Expected error for empty phone")
		}
	})

	t.Run("Bind phone with wrong password", func(t *testing.T) {
		err := env.authSvc.BindPhone(ctx, user.ID, "13800138002", "123456", "wrongpassword", "")
		if err == nil {
			t.Error("Expected error for wrong password")
		}
	})
}

func TestAuthService_UnbindPhone(t *testing.T) {
	env := setupContactBindingTestEnv(t)
	if env == nil {
		return
	}
	ctx := context.Background()

	// Create test user with phone and password
	hashedPassword, _ := auth.HashPassword("Password123!")
	phone := "13900139000"
	user := &domain.User{
		Username: "unbindphoneuser",
		Password: hashedPassword,
		Status:   domain.UserStatusActive,
		Phone:    &phone,
	}
	env.userSvc.Create(ctx, user)

	t.Run("Unbind phone with nil service", func(t *testing.T) {
		var nilSvc *service.AuthService
		err := nilSvc.UnbindPhone(ctx, 1, "", "")
		if err == nil {
			t.Error("Expected error for nil service")
		}
	})

	t.Run("Unbind phone for non-existent user", func(t *testing.T) {
		err := env.authSvc.UnbindPhone(ctx, 9999, "", "")
		if err == nil {
			t.Error("Expected error for non-existent user")
		}
	})

	t.Run("Unbind phone with wrong password", func(t *testing.T) {
		err := env.authSvc.UnbindPhone(ctx, user.ID, "wrongpassword", "")
		if err == nil {
			t.Error("Expected error for wrong password")
		}
	})

	t.Run("Unbind phone for user without phone", func(t *testing.T) {
		userNoPhone := &domain.User{
			Username: "nophoneuser",
			Password: hashedPassword,
			Status:   domain.UserStatusActive,
		}
		env.userSvc.Create(ctx, userNoPhone)

		err := env.authSvc.UnbindPhone(ctx, userNoPhone.ID, "Password123!", "")
		if err == nil {
			t.Error("Expected error for user without phone")
		}
	})
}

// =============================================================================
// BindEmail Extended Tests
// =============================================================================

func TestAuthService_BindEmail_Extended(t *testing.T) {
	env := setupContactBindingTestEnv(t)
	if env == nil {
		return
	}
	ctx := context.Background()

	hashedPassword, _ := auth.HashPassword("Password123!")

	t.Run("BindEmail with nil service", func(t *testing.T) {
		var nilSvc *service.AuthService
		err := nilSvc.BindEmail(ctx, 1, "test@example.com", "code", "password", "")
		if err == nil {
			t.Error("Expected error for nil service")
		}
	})

	t.Run("BindEmail for non-existent user", func(t *testing.T) {
		err := env.authSvc.BindEmail(ctx, 9999, "test@example.com", "code", "password", "")
		if err == nil {
			t.Error("Expected error for non-existent user")
		}
	})

	t.Run("BindEmail with empty email", func(t *testing.T) {
		user := &domain.User{
			Username: "bindemailuser",
			Password: hashedPassword,
			Status:   domain.UserStatusActive,
		}
		env.userSvc.Create(ctx, user)

		err := env.authSvc.BindEmail(ctx, user.ID, "", "code", "Password123!", "")
		if err == nil {
			t.Error("Expected error for empty email")
		}
	})
}

// =============================================================================
// BindPhone Extended Tests
// =============================================================================

func TestAuthService_BindPhone_Extended(t *testing.T) {
	env := setupContactBindingTestEnv(t)
	if env == nil {
		return
	}
	ctx := context.Background()

	hashedPassword, _ := auth.HashPassword("Password123!")

	t.Run("BindPhone with nil service", func(t *testing.T) {
		var nilSvc *service.AuthService
		err := nilSvc.BindPhone(ctx, 1, "13800138000", "code", "password", "")
		if err == nil {
			t.Error("Expected error for nil service")
		}
	})

	t.Run("BindPhone for non-existent user", func(t *testing.T) {
		err := env.authSvc.BindPhone(ctx, 9999, "13800138000", "code", "password", "")
		if err == nil {
			t.Error("Expected error for non-existent user")
		}
	})

	t.Run("BindPhone with empty phone", func(t *testing.T) {
		user := &domain.User{
			Username: "bindphoneuser",
			Password: hashedPassword,
			Status:   domain.UserStatusActive,
		}
		env.userSvc.Create(ctx, user)

		err := env.authSvc.BindPhone(ctx, user.ID, "", "code", "Password123!", "")
		if err == nil {
			t.Error("Expected error for empty phone")
		}
	})
}
