package servicemocks

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

	"veza-backend-api/internal/services"

	"github.com/google/uuid"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	"github.com/stretchr/testify/require"
)

func TestMockSessionService(t *testing.T) {
	mockService := NewMockSessionService()
	userID := uuid.New()

	SetupMockSessionSuccess(mockService, userID)

	req := &services.SessionCreateRequest{
		UserID:    userID,
		Token:     "test-token",
		IPAddress: "127.0.0.1",
		UserAgent: "test-agent",
		ExpiresIn: 24 * time.Hour,
	}

	session, err := mockService.CreateSession(context.Background(), req)
	require.NoError(t, err)
	assert.NotNil(t, session)
	assert.Equal(t, userID, session.UserID)
	assert.NotEmpty(t, session.ID)

	mockService.AssertExpectations(t)
}

func TestMockSessionService_ValidateSession(t *testing.T) {
	mockService := NewMockSessionService()
	userID := uuid.New()
	token := "valid-token"

	SetupMockSessionValidationSuccess(mockService, userID, token)

	session, err := mockService.ValidateSession(context.Background(), token)
	require.NoError(t, err)
	assert.NotNil(t, session)
	assert.Equal(t, userID, session.UserID)

	mockService.AssertExpectations(t)
}

func TestMockSessionService_ValidateSessionError(t *testing.T) {
	mockService := NewMockSessionService()
	token := "invalid-token"
	expectedErr := errors.New("session not found")

	SetupMockSessionValidationError(mockService, token, expectedErr)

	session, err := mockService.ValidateSession(context.Background(), token)
	assert.Error(t, err)
	assert.Nil(t, session)
	assert.Equal(t, expectedErr, err)

	mockService.AssertExpectations(t)
}

func TestMockSessionService_RevokeSession(t *testing.T) {
	mockService := NewMockSessionService()
	token := "token-to-revoke"

	SetupMockSessionRevokeSuccess(mockService, token)

	err := mockService.RevokeSession(context.Background(), token)
	assert.NoError(t, err)

	mockService.AssertExpectations(t)
}

func TestMockSessionService_RevokeAllUserSessions(t *testing.T) {
	mockService := NewMockSessionService()
	userID := uuid.New()

	mockService.On("RevokeAllUserSessions", mock.Anything, userID).Return(int64(3), nil)

	count, err := mockService.RevokeAllUserSessions(context.Background(), userID)
	require.NoError(t, err)
	assert.Equal(t, int64(3), count)

	mockService.AssertExpectations(t)
}

func TestMockSessionService_GetUserSessions(t *testing.T) {
	mockService := NewMockSessionService()
	userID := uuid.New()

	sessions := []*services.Session{
		{
			ID:        uuid.New(),
			UserID:    userID,
			CreatedAt: time.Now(),
			ExpiresAt: time.Now().Add(24 * time.Hour),
		},
		{
			ID:        uuid.New(),
			UserID:    userID,
			CreatedAt: time.Now().Add(-1 * time.Hour),
			ExpiresAt: time.Now().Add(23 * time.Hour),
		},
	}

	mockService.On("GetUserSessions", mock.Anything, userID).Return(sessions, nil)

	result, err := mockService.GetUserSessions(context.Background(), userID)
	require.NoError(t, err)
	assert.Len(t, result, 2)
	assert.Equal(t, userID, result[0].UserID)

	mockService.AssertExpectations(t)
}

func TestMockSessionService_CleanupExpiredSessions(t *testing.T) {
	mockService := NewMockSessionService()

	mockService.On("CleanupExpiredSessions", mock.Anything).Return(int64(10), nil)

	count, err := mockService.CleanupExpiredSessions(context.Background())
	require.NoError(t, err)
	assert.Equal(t, int64(10), count)

	mockService.AssertExpectations(t)
}

func TestMockSessionService_RefreshSession(t *testing.T) {
	mockService := NewMockSessionService()
	token := "token-to-refresh"
	newExpiresIn := 48 * time.Hour

	mockService.On("RefreshSession", mock.Anything, token, newExpiresIn).Return(nil)

	err := mockService.RefreshSession(context.Background(), token, newExpiresIn)
	assert.NoError(t, err)

	mockService.AssertExpectations(t)
}

func TestMockSessionService_GetSessionStats(t *testing.T) {
	mockService := NewMockSessionService()

	stats := map[string]interface{}{
		"total_sessions":   100,
		"active_sessions":  50,
		"expired_sessions": 50,
	}

	mockService.On("GetSessionStats", mock.Anything).Return(stats, nil)

	result, err := mockService.GetSessionStats(context.Background())
	require.NoError(t, err)
	assert.NotNil(t, result)
	assert.Equal(t, 100, result["total_sessions"])

	mockService.AssertExpectations(t)
}

func TestMockAuditService(t *testing.T) {
	mockService := NewMockAuditService()

	req := &services.AuditLogCreateRequest{
		UserID:    uuidPtr(uuid.New()),
		Action:    "test_action",
		Resource:  "test_resource",
		IPAddress: "127.0.0.1",
		UserAgent: "test-agent",
		Metadata:  map[string]interface{}{"key": "value"},
	}

	SetupMockAuditLogActionSuccess(mockService, req)

	err := mockService.LogAction(context.Background(), req)
	assert.NoError(t, err)

	mockService.AssertExpectations(t)
}

func TestMockAuditService_LogLogin(t *testing.T) {
	mockService := NewMockAuditService()
	userID := uuidPtr(uuid.New())

	SetupMockAuditLogLoginSuccess(mockService, userID, true)

	err := mockService.LogLogin(context.Background(), userID, true, "127.0.0.1", "test-agent", map[string]interface{}{})
	assert.NoError(t, err)

	mockService.AssertExpectations(t)
}

func TestMockAuditService_LogLogout(t *testing.T) {
	mockService := NewMockAuditService()
	userID := uuid.New()

	SetupMockAuditLogLogoutSuccess(mockService, userID)

	err := mockService.LogLogout(context.Background(), userID, "127.0.0.1", "test-agent")
	assert.NoError(t, err)

	mockService.AssertExpectations(t)
}

func TestMockAuditService_LogUpload(t *testing.T) {
	mockService := NewMockAuditService()
	userID := uuid.New()
	resourceID := uuid.New()

	SetupMockAuditLogUploadSuccess(mockService, userID, resourceID)

	err := mockService.LogUpload(context.Background(), userID, resourceID, "test.mp3", 1024, "127.0.0.1", "test-agent")
	assert.NoError(t, err)

	mockService.AssertExpectations(t)
}

func TestMockAuditService_LogPermissionChange(t *testing.T) {
	mockService := NewMockAuditService()
	userID := uuid.New()
	targetUserID := uuid.New()

	SetupMockAuditLogPermissionChangeSuccess(mockService, userID, targetUserID)

	err := mockService.LogPermissionChange(context.Background(), userID, targetUserID,
		[]string{"read"}, []string{"read", "write"},
		"127.0.0.1", "test-agent")
	assert.NoError(t, err)

	mockService.AssertExpectations(t)
}

func TestMockAuditService_LogDeletion(t *testing.T) {
	mockService := NewMockAuditService()
	userID := uuid.New()
	resourceID := uuid.New()

	SetupMockAuditLogDeletionSuccess(mockService, userID, resourceID)

	err := mockService.LogDeletion(context.Background(), userID, "track", resourceID, "127.0.0.1", "test-agent")
	assert.NoError(t, err)

	mockService.AssertExpectations(t)
}

func TestMockAuditService_SearchLogs(t *testing.T) {
	mockService := NewMockAuditService()
	userID := uuid.New()

	req := &services.AuditLogSearchRequest{
		UserID: &userID,
		Action: "login",
		Limit:  10,
	}

	logs := []*services.AuditLog{
		{
			ID:        uuid.New(),
			UserID:    &userID,
			Action:    "login",
			Resource:  "user",
			Timestamp: time.Now(),
		},
	}

	SetupMockAuditSearchLogs(mockService, req, logs)

	result, err := mockService.SearchLogs(context.Background(), req)
	require.NoError(t, err)
	assert.Len(t, result, 1)
	assert.Equal(t, "login", result[0].Action)

	mockService.AssertExpectations(t)
}

func TestMockAuditService_SearchLogsError(t *testing.T) {
	mockService := NewMockAuditService()

	req := &services.AuditLogSearchRequest{
		Limit: 10,
	}
	expectedErr := errors.New("database error")

	SetupMockAuditSearchLogsError(mockService, req, expectedErr)

	result, err := mockService.SearchLogs(context.Background(), req)
	assert.Error(t, err)
	assert.Nil(t, result)
	assert.Equal(t, expectedErr, err)

	mockService.AssertExpectations(t)
}

func TestMockAuditService_GetStats(t *testing.T) {
	mockService := NewMockAuditService()

	stats := []*services.AuditStats{
		{
			Action:      "login",
			Resource:    "user",
			ActionCount: 100,
			UniqueUsers: 50,
			UniqueIPs:   30,
		},
	}

	startDate := time.Now().Add(-24 * time.Hour)
	endDate := time.Now()

	mockService.On("GetStats", mock.Anything, startDate, endDate).Return(stats, nil)

	result, err := mockService.GetStats(context.Background(), startDate, endDate)
	require.NoError(t, err)
	assert.Len(t, result, 1)
	assert.Equal(t, "login", result[0].Action)

	mockService.AssertExpectations(t)
}

func TestNewMockSessionService(t *testing.T) {
	mockService := NewMockSessionService()
	assert.NotNil(t, mockService)
}

func TestNewMockAuditService(t *testing.T) {
	mockService := NewMockAuditService()
	assert.NotNil(t, mockService)
}

// uuidPtr retourne un pointeur vers un UUID
func uuidPtr(u uuid.UUID) *uuid.UUID {
	return &u
}
