package account_audit

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

	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
)

type MockAccountOperationLogStore struct {
	mock.Mock
}

func (m *MockAccountOperationLogStore) Create(ctx context.Context, log *model.AccountOperationLog) error {
	args := m.Called(ctx, log)
	return args.Error(0)
}

func TestLogOperation_Success(t *testing.T) {
	mockStore := new(MockAccountOperationLogStore)
	service := NewService(mockStore)

	log := &model.AccountOperationLog{
		OperatorID:    1,
		OperatorType:  2,
		OperatorName:  "admin",
		OperationType: "create",
		OperationDesc: "创建账号: testuser",
	}

	mockStore.On("Create", mock.Anything, log).Return(nil)

	ctx := context.Background()
	service.LogOperation(ctx, log)

	time.Sleep(50 * time.Millisecond)

	mockStore.AssertCalled(t, "Create", mock.Anything, log)
}

func TestLogOperation_Failure(t *testing.T) {
	mockStore := new(MockAccountOperationLogStore)
	service := NewService(mockStore)

	log := &model.AccountOperationLog{
		OperatorID:    1,
		OperatorType:  2,
		OperatorName:  "admin",
		OperationType: "create",
		OperationDesc: "创建账号: testuser",
	}

	mockStore.On("Create", mock.Anything, log).Return(errors.New("database error"))

	ctx := context.Background()

	assert.NotPanics(t, func() {
		service.LogOperation(ctx, log)
	})

	time.Sleep(50 * time.Millisecond)

	mockStore.AssertCalled(t, "Create", mock.Anything, log)
}

func TestLogOperation_NonBlocking(t *testing.T) {
	mockStore := new(MockAccountOperationLogStore)
	service := NewService(mockStore)

	log := &model.AccountOperationLog{
		OperatorID:    1,
		OperatorType:  2,
		OperatorName:  "admin",
		OperationType: "create",
		OperationDesc: "创建账号: testuser",
	}

	mockStore.On("Create", mock.Anything, log).Run(func(args mock.Arguments) {
		time.Sleep(100 * time.Millisecond)
	}).Return(nil)

	ctx := context.Background()

	start := time.Now()
	service.LogOperation(ctx, log)
	elapsed := time.Since(start)

	assert.Less(t, elapsed, 50*time.Millisecond, "LogOperation should return immediately")

	time.Sleep(150 * time.Millisecond)
	mockStore.AssertCalled(t, "Create", mock.Anything, log)
}

func TestNewService(t *testing.T) {
	mockStore := new(MockAccountOperationLogStore)
	service := NewService(mockStore)

	assert.NotNil(t, service)
	assert.Equal(t, mockStore, service.store)
}

func TestLogOperation_WithAllFields(t *testing.T) {
	mockStore := new(MockAccountOperationLogStore)
	service := NewService(mockStore)

	targetAccountID := uint(10)
	targetUsername := "targetuser"
	targetUserType := 3
	requestID := "req-12345"
	ipAddress := "127.0.0.1"
	userAgent := "Mozilla/5.0"

	log := &model.AccountOperationLog{
		OperatorID:      1,
		OperatorType:    2,
		OperatorName:    "admin",
		TargetAccountID: &targetAccountID,
		TargetUsername:  &targetUsername,
		TargetUserType:  &targetUserType,
		OperationType:   "update",
		OperationDesc:   "更新账号: targetuser",
		BeforeData: model.JSONB{
			"username": "oldname",
		},
		AfterData: model.JSONB{
			"username": "newname",
		},
		RequestID: &requestID,
		IPAddress: &ipAddress,
		UserAgent: &userAgent,
	}

	mockStore.On("Create", mock.Anything, log).Return(nil)

	ctx := context.Background()
	service.LogOperation(ctx, log)

	time.Sleep(50 * time.Millisecond)

	mockStore.AssertCalled(t, "Create", mock.Anything, log)
}
