package service_test

import (
	"context"
	"fmt"
	"testing"
	"time"

	"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/repository"
	"github.com/user-management-system/internal/service"
	gormsqlite "gorm.io/driver/sqlite"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

// =============================================================================
// Auth Core Methods Tests - Phase 1: Coverage to 35%
// =============================================================================

type authTestEnv struct {
	db          *gorm.DB
	authSvc     *service.AuthService
	userSvc     *service.UserService
}

func setupAuthTestEnv(t *testing.T) *authTestEnv {
	t.Helper()

	dsn := fmt.Sprintf("file:authtest_%s_%d?mode=memory&cache=shared", sanitizeTestName(t.Name()), time.Now().UnixNano())
	db, err := gorm.Open(gormsqlite.New(gormsqlite.Config{
		DriverName: "sqlite",
		DSN:        dsn,
	}), &gorm.Config{
		Logger: logger.Default.LogMode(logger.Silent),
	})
	if err != nil {
		t.Skipf("skipping test (SQLite unavailable): %v", err)
		return nil
	}

	db.Exec("PRAGMA journal_mode=WAL")

	if err := db.AutoMigrate(
		&domain.User{},
		&domain.Role{},
		&domain.UserRole{},
		&domain.LoginLog{},
		&domain.PasswordHistory{},
	); err != nil {
		t.Fatalf("db migration failed: %v", err)
	}

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

	jwtManager, _ := auth.NewJWTWithOptions(auth.JWTOptions{
		HS256Secret:        fmt.Sprintf("test-secret-%d", time.Now().UnixNano()),
		AccessTokenExpire:  15 * time.Minute,
		RefreshTokenExpire: 7 * 24 * time.Hour,
	})

	l1Cache := cache.NewL1Cache()
	l2Cache := cache.NewRedisCache(false)
	cacheManager := cache.NewCacheManager(l1Cache, l2Cache)

	userRepo := repository.NewUserRepository(db)
	roleRepo := repository.NewRoleRepository(db)
	userRoleRepo := repository.NewUserRoleRepository(db)
	passwordHistoryRepo := repository.NewPasswordHistoryRepository(db)

	authSvc := service.NewAuthService(userRepo, nil, jwtManager, cacheManager, 8, 5, 15*time.Minute)
	authSvc.SetRoleRepositories(userRoleRepo, roleRepo)
	userSvc := service.NewUserService(userRepo, userRoleRepo, roleRepo, passwordHistoryRepo)

	t.Cleanup(func() {
		if sqlDB, err := db.DB(); err == nil {
			sqlDB.Close()
		}
	})

	return &authTestEnv{
		db:      db,
		authSvc: authSvc,
		userSvc: userSvc,
	}
}

// Test RefreshToken method
func TestAuthService_RefreshToken(t *testing.T) {
	env := setupAuthTestEnv(t)
	if env == nil {
		return
	}
	ctx := context.Background()

	// First register a user
	req := &service.RegisterRequest{
		Username: "refreshuser",
		Password: "Test123!",
		Email:    "refresh@test.com",
	}
	authResp, err := env.authSvc.Register(ctx, req)
	if err != nil {
		t.Fatalf("Register failed: %v", err)
	}
	userID := authResp.ID

	// Login to get refresh token
	loginResp, err := env.authSvc.Login(ctx, &service.LoginRequest{
		Username: "refreshuser",
		Password: "Test123!",
	}, "127.0.0.1")
	if err != nil {
		t.Fatalf("Login failed: %v", err)
	}
	refreshToken := loginResp.RefreshToken

	t.Run("Refresh token success", func(t *testing.T) {
		resp, err := env.authSvc.RefreshToken(ctx, refreshToken)
		if err != nil {
			t.Fatalf("RefreshToken failed: %v", err)
		}
		if resp.AccessToken == "" {
			t.Error("Expected access token to be returned")
		}
		if resp.RefreshToken == "" {
			t.Error("Expected refresh token to be returned")
		}
	})

	t.Run("Refresh token with invalid token", func(t *testing.T) {
		_, err := env.authSvc.RefreshToken(ctx, "invalid-token")
		if err == nil {
			t.Error("Expected error for invalid token")
		}
	})

	t.Run("Refresh token with empty token", func(t *testing.T) {
		_, err := env.authSvc.RefreshToken(ctx, "")
		if err == nil {
			t.Error("Expected error for empty token")
		}
	})

	t.Run("Refresh token for locked user", func(t *testing.T) {
		// Lock the user
		env.userSvc.UpdateStatus(ctx, userID, domain.UserStatusLocked)

		// Try to refresh token - should fail
		_, err := env.authSvc.RefreshToken(ctx, refreshToken)
		if err == nil {
			t.Error("Expected error for locked user")
		}

		// Unlock user for cleanup
		env.userSvc.UpdateStatus(ctx, userID, domain.UserStatusActive)
	})

	t.Run("Refresh token with nil service", func(t *testing.T) {
		var nilSvc *service.AuthService
		_, err := nilSvc.RefreshToken(ctx, refreshToken)
		if err == nil {
			t.Error("Expected error for nil service")
		}
	})
}

// Test GetUserInfo method
func TestAuthService_GetUserInfo(t *testing.T) {
	env := setupAuthTestEnv(t)
	if env == nil {
		return
	}
	ctx := context.Background()

	// Register a user
	req := &service.RegisterRequest{
		Username: "infouser",
		Password: "Test123!",
		Email:    "info@test.com",
		Nickname: "Info User",
	}
	authResp, err := env.authSvc.Register(ctx, req)
	if err != nil {
		t.Fatalf("Register failed: %v", err)
	}
	userID := authResp.ID

	t.Run("Get user info success", func(t *testing.T) {
		info, err := env.authSvc.GetUserInfo(ctx, userID)
		if err != nil {
			t.Fatalf("GetUserInfo failed: %v", err)
		}
		if info.ID != userID {
			t.Errorf("Expected user ID %d, got %d", userID, info.ID)
		}
		if info.Username != "infouser" {
			t.Errorf("Expected username 'infouser', got %s", info.Username)
		}
		if info.Nickname != "Info User" {
			t.Errorf("Expected nickname 'Info User', got %s", info.Nickname)
		}
		if info.Email != "info@test.com" {
			t.Errorf("Expected email 'info@test.com', got %s", info.Email)
		}
	})

	t.Run("Get user info from cache", func(t *testing.T) {
		// Second call should hit cache
		info, err := env.authSvc.GetUserInfo(ctx, userID)
		if err != nil {
			t.Fatalf("GetUserInfo from cache failed: %v", err)
		}
		if info.ID != userID {
			t.Errorf("Expected user ID %d, got %d", userID, info.ID)
		}
	})

	t.Run("Get user info for non-existent user", func(t *testing.T) {
		_, err := env.authSvc.GetUserInfo(ctx, 99999)
		if err == nil {
			t.Error("Expected error for non-existent user")
		}
	})

	t.Run("Get user info with nil service", func(t *testing.T) {
		var nilSvc *service.AuthService
		_, err := nilSvc.GetUserInfo(ctx, userID)
		if err == nil {
			t.Error("Expected error for nil service")
		}
	})

	t.Run("Get user info with zero ID", func(t *testing.T) {
		_, err := env.authSvc.GetUserInfo(ctx, 0)
		if err == nil {
			t.Error("Expected error for zero user ID")
		}
	})
}

// Test Logout method
func TestAuthService_Logout(t *testing.T) {
	env := setupAuthTestEnv(t)
	if env == nil {
		return
	}
	ctx := context.Background()

	// Register and login a user
	req := &service.RegisterRequest{
		Username: "logoutuser",
		Password: "Test123!",
	}
	_, err := env.authSvc.Register(ctx, req)
	if err != nil {
		t.Fatalf("Register failed: %v", err)
	}

	loginResp, err := env.authSvc.Login(ctx, &service.LoginRequest{
		Username: "logoutuser",
		Password: "Test123!",
	}, "127.0.0.1")
	if err != nil {
		t.Fatalf("Login failed: %v", err)
	}

	t.Run("Logout success", func(t *testing.T) {
		err := env.authSvc.Logout(ctx, "logoutuser", &service.LogoutRequest{
			AccessToken:  loginResp.AccessToken,
			RefreshToken: loginResp.RefreshToken,
		})
		if err != nil {
			t.Errorf("Logout failed: %v", err)
		}
	})

	t.Run("Logout with nil request", func(t *testing.T) {
		err := env.authSvc.Logout(ctx, "logoutuser", nil)
		if err != nil {
			t.Errorf("Logout with nil request should not error: %v", err)
		}
	})

	t.Run("Logout with nil service", func(t *testing.T) {
		var nilSvc *service.AuthService
		err := nilSvc.Logout(ctx, "logoutuser", &service.LogoutRequest{
			AccessToken:  loginResp.AccessToken,
			RefreshToken: loginResp.RefreshToken,
		})
		if err != nil {
			t.Errorf("Logout with nil service should not error: %v", err)
		}
	})
}
