package service

import (
	"context"
	"errors"
	"fmt"
	"strings"
	"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"
	gormsqlite "gorm.io/driver/sqlite"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

type failingL2Cache struct {
	setErr error
}

func (f *failingL2Cache) Set(ctx context.Context, key string, value interface{}, ttl time.Duration) error {
	return f.setErr
}
func (f *failingL2Cache) Get(ctx context.Context, key string) (interface{}, error) { return nil, nil }
func (f *failingL2Cache) Delete(ctx context.Context, key string) error             { return nil }
func (f *failingL2Cache) Exists(ctx context.Context, key string) (bool, error)     { return false, nil }
func (f *failingL2Cache) Clear(ctx context.Context) error                          { return nil }
func (f *failingL2Cache) Increment(ctx context.Context, key string, delta int64, ttl time.Duration) (int64, error) {
	return 0, nil
}
func (f *failingL2Cache) Close() error { return nil }

func TestAuthService_Logout_FailsClosedWhenBlacklistWriteFails(t *testing.T) {
	dsn := fmt.Sprintf("file:logoutfailclosed_%d?mode=memory&cache=shared", 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.Fatalf("open db failed: %v", err)
	}
	if err := db.AutoMigrate(&domain.User{}, &domain.Role{}, &domain.UserRole{}, &domain.LoginLog{}, &domain.PasswordHistory{}); err != nil {
		t.Fatalf("migrate failed: %v", err)
	}
	for _, role := range domain.PredefinedRoles {
		roleCopy := role
		if err := db.Create(&roleCopy).Error; err != nil {
			t.Fatalf("seed role %s failed: %v", role.Code, err)
		}
	}

	jwtManager, err := auth.NewJWTWithOptions(auth.JWTOptions{
		HS256Secret:        fmt.Sprintf("logout-failclosed-secret-%d", time.Now().UnixNano()),
		AccessTokenExpire:  15 * time.Minute,
		RefreshTokenExpire: 7 * 24 * time.Hour,
	})
	if err != nil {
		t.Fatalf("create jwt manager failed: %v", err)
	}

	userRepo := repository.NewUserRepository(db)
	userRoleRepo := repository.NewUserRoleRepository(db)
	roleRepo := repository.NewRoleRepository(db)
	cacheManager := cache.NewCacheManager(cache.NewL1Cache(), &failingL2Cache{setErr: errors.New("forced blacklist failure")})

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

	ctx := context.Background()
	if _, err := authSvc.Register(ctx, &RegisterRequest{Username: "logoutfail", Password: "Password123!"}); err != nil {
		t.Fatalf("register failed: %v", err)
	}
	loginResp, err := authSvc.Login(ctx, &LoginRequest{Username: "logoutfail", Password: "Password123!"}, "127.0.0.1")
	if err != nil {
		t.Fatalf("login failed: %v", err)
	}

	err = authSvc.Logout(ctx, "logoutfail", &LogoutRequest{AccessToken: loginResp.AccessToken, RefreshToken: loginResp.RefreshToken})
	if err == nil {
		t.Fatal("expected logout to fail closed when blacklist write fails")
	}
	if !strings.Contains(err.Error(), "forced blacklist failure") {
		t.Fatalf("expected propagated blacklist error, got: %v", err)
	}
}
