package system

import (
	"context"
	"errors"
	"testing"
	"time"
)

type securityUpdateRepo struct {
	current       *SecurityConfig
	backfillErr   error
	callOrder     []string
	backfillAt    time.Time
	persistedCopy *SecurityConfig
}

func (r *securityUpdateRepo) SecurityConfig(context.Context) (*SecurityConfig, error) {
	r.callOrder = append(r.callOrder, "get")
	copy := *r.current
	return &copy, nil
}

func (r *securityUpdateRepo) SaveSecurityConfig(_ context.Context, value *SecurityConfig) error {
	r.callOrder = append(r.callOrder, "save")
	value.UpdatedAt = value.UpdatedAt.Add(time.Second)
	copy := *value
	r.persistedCopy = &copy
	return nil
}

func (r *securityUpdateRepo) BackfillPasswordUpdatedAt(_ context.Context, at time.Time) error {
	r.callOrder = append(r.callOrder, "backfill")
	r.backfillAt = at
	return r.backfillErr
}

func TestUpdateSecurityKeepsSavedConfigWhenPasswordBackfillFails(t *testing.T) {
	createdAt := time.Date(2026, time.August, 16, 12, 0, 0, 0, time.Local)
	backfillErr := errors.New("backfill failed")
	repo := &securityUpdateRepo{
		current:     &SecurityConfig{ID: 1, CreatedAt: createdAt, UpdatedAt: createdAt, PwdExpireEnable: false},
		backfillErr: backfillErr,
	}
	uc := NewSecurityUsecase(repo, nil, nil, nil)
	next := &SecurityConfig{ID: 99, PwdExpireEnable: true, PwdExpireDays: 30}

	err := uc.UpdateSecurity(context.Background(), next)
	if !errors.Is(err, backfillErr) {
		t.Fatalf("UpdateSecurity() error = %v, want %v", err, backfillErr)
	}
	if got := repo.callOrder; len(got) != 3 || got[0] != "get" || got[1] != "save" || got[2] != "backfill" {
		t.Fatalf("call order = %v, want [get save backfill]", got)
	}
	if repo.persistedCopy == nil || repo.persistedCopy.ID != 1 || !repo.persistedCopy.PwdExpireEnable {
		t.Fatalf("persisted config = %+v", repo.persistedCopy)
	}
	if repo.backfillAt.IsZero() {
		t.Fatal("password timestamp backfill was not attempted")
	}
	cached, currentErr := uc.Current(context.Background())
	if currentErr != nil {
		t.Fatalf("Current() error = %v", currentErr)
	}
	if !cached.PwdExpireEnable || cached.ID != 1 {
		t.Fatalf("effective config = %+v, want newly saved config", cached)
	}
	if got := repo.callOrder; len(got) != 3 {
		t.Fatalf("Current() unexpectedly reloaded repository: %v", got)
	}
}

func TestUpdateSecurityDoesNotBackfillWithoutDisabledToEnabledTransition(t *testing.T) {
	repo := &securityUpdateRepo{current: &SecurityConfig{ID: 1, PwdExpireEnable: true}}
	uc := NewSecurityUsecase(repo, nil, nil, nil)

	if err := uc.UpdateSecurity(context.Background(), &SecurityConfig{PwdExpireEnable: true}); err != nil {
		t.Fatalf("UpdateSecurity() error = %v", err)
	}
	if got := repo.callOrder; len(got) != 2 || got[0] != "get" || got[1] != "save" {
		t.Fatalf("call order = %v, want [get save]", got)
	}
}

func TestSecurityReadsPersistedValueWhileCurrentUsesCache(t *testing.T) {
	repo := &securityUpdateRepo{current: &SecurityConfig{ID: 1, LimitCount: 10}}
	uc := NewSecurityUsecase(repo, nil, nil, nil)

	cached, err := uc.Current(context.Background())
	if err != nil {
		t.Fatal(err)
	}
	if cached.LimitCount != 10 {
		t.Fatalf("initial cached limit = %d", cached.LimitCount)
	}
	repo.current.LimitCount = 20

	current, err := uc.Current(context.Background())
	if err != nil {
		t.Fatal(err)
	}
	if current.LimitCount != 10 {
		t.Fatalf("Current() bypassed cache: %d", current.LimitCount)
	}
	fresh, err := uc.Security(context.Background())
	if err != nil {
		t.Fatal(err)
	}
	if fresh.LimitCount != 20 {
		t.Fatalf("Security() returned stale value: %d", fresh.LimitCount)
	}
}
