<?php

declare(strict_types=1);

use App\Framework\Analytics\AnalyticsCategory;
use App\Framework\Analytics\AnalyticsCollector;
use App\Framework\Analytics\Storage\AnalyticsStorage;
use App\Framework\Http\ServerEnvironment;
use App\Framework\Performance\Contracts\PerformanceCollectorInterface;
use App\Framework\Random\RandomGenerator;

describe('AnalyticsCollector', function () {
    beforeEach(function () {
        // Mock dependencies
        $this->performanceCollector = Mockery::mock(PerformanceCollectorInterface::class);
        $this->storage = Mockery::mock(AnalyticsStorage::class);
        $this->random = Mockery::mock(RandomGenerator::class);

        // Create real ServerEnvironment with test data (final class, can't be mocked)
        $this->serverEnvironment = new ServerEnvironment([
            'REMOTE_ADDR' => '127.0.0.1',
            'HTTP_USER_AGENT' => 'Test-Agent/1.0',
            'REQUEST_URI' => '/test',
            'HTTP_REFERER' => 'https://example.com',
        ]);

        // Allow any performance collector calls (framework internal)
        $this->performanceCollector
            ->shouldReceive('recordMetric')
            ->zeroOrMoreTimes();

        $this->performanceCollector
            ->shouldReceive('increment')
            ->zeroOrMoreTimes();

        // Allow storage aggregated calls (framework internal)
        $this->storage
            ->shouldReceive('storeAggregated')
            ->zeroOrMoreTimes();

        // Allow random float calls for sampling (may or may not be called)
        $this->random
            ->shouldReceive('float')
            ->with(0, 1)
            ->zeroOrMoreTimes()
            ->andReturn(0.5);

        // Allow random bytes calls for session ID generation (may or may not be called)
        $this->random
            ->shouldReceive('bytes')
            ->with(16)
            ->zeroOrMoreTimes()
            ->andReturn(str_repeat('a', 16));

        // Default: tracking enabled, 100% sampling for tests
        $this->collector = new AnalyticsCollector(
            performanceCollector: $this->performanceCollector,
            storage: $this->storage,
            random: $this->random,
            serverEnvironment: $this->serverEnvironment,
            enabled: true,
            samplingRate: 1.0
        );
    });

    afterEach(function () {
        Mockery::close();
    });

    describe('trackAction', function () {
        it('tracks user action with category and properties', function () {
            // Expect raw data storage with flexible array matcher
            $this->storage
                ->shouldReceive('storeRawData')
                ->once()
                ->with(
                    Mockery::on(function ($data) {
                        return is_array($data)
                            && $data['category'] === 'user_behavior'
                            && $data['action'] === 'button_click'
                            && isset($data['session_id'])
                            && isset($data['timestamp'])
                            && isset($data['button_id'])
                            && $data['button_id'] === 'submit-btn';
                    }),
                    1.0
                );

            // Track action
            $this->collector->trackAction(
                action: 'button_click',
                category: AnalyticsCategory::USER_BEHAVIOR,
                properties: ['button_id' => 'submit-btn']
            );
        });

        it('does not track when analytics disabled', function () {
            // Create collector with analytics disabled
            $disabledCollector = new AnalyticsCollector(
                performanceCollector: $this->performanceCollector,
                storage: $this->storage,
                random: $this->random,
                serverEnvironment: $this->serverEnvironment,
                enabled: false, // Disabled
                samplingRate: 1.0
            );

            // Storage should NOT be called
            $this->storage->shouldNotReceive('storeRawData');

            // Track action (should be ignored)
            $disabledCollector->trackAction('click', AnalyticsCategory::USER_BEHAVIOR);
        });

        it('respects sampling rate', function () {
            // Create new Random mock for this test
            $randomMock = Mockery::mock(RandomGenerator::class);

            // Random returns 0.6 (above 0.5 threshold) -> should NOT track (0.6 > 0.5)
            $randomMock->shouldReceive('float')->with(0, 1)->andReturn(0.6);

            // Create collector with 50% sampling
            $sampledCollector = new AnalyticsCollector(
                performanceCollector: $this->performanceCollector,
                storage: $this->storage,
                random: $randomMock,
                serverEnvironment: $this->serverEnvironment,
                enabled: true,
                samplingRate: 0.5
            );

            // Storage should NOT be called (sampled out)
            $this->storage->shouldNotReceive('storeRawData');

            // Track action (should be sampled out)
            $sampledCollector->trackAction('click', AnalyticsCategory::USER_BEHAVIOR);
        });
    });

    describe('trackPageView', function () {
        it('tracks page view with path and title', function () {
            // Expect raw data storage with flexible matcher
            $this->storage
                ->shouldReceive('storeRawData')
                ->once()
                ->with(
                    Mockery::on(function ($data) {
                        return is_array($data)
                            && $data['path'] === '/dashboard'
                            && $data['title'] === 'Dashboard'
                            && isset($data['timestamp'])
                            && isset($data['session_id']);
                    }),
                    1.0
                );

            // Track page view
            $this->collector->trackPageView(
                path: '/dashboard',
                title: 'Dashboard'
            );
        });
    });

    describe('trackError', function () {
        it('tracks error with type and message', function () {
            // trackError only logs to performance collector, not storage
            // Storage expectations are handled by global mocks

            // Track error
            $this->collector->trackError(
                errorType: 'ValidationException',
                message: 'Invalid email format'
            );

            // Test passes if no exceptions are thrown
            expect(true)->toBeTrue();
        });
    });

    describe('trackBusinessEvent', function () {
        it('tracks business event with value and currency', function () {
            // trackBusinessEvent only logs to performance collector, not storage
            // Storage expectations are handled by global mocks

            // Track business event
            $this->collector->trackBusinessEvent(
                event: 'purchase_completed',
                value: 99.99,
                currency: 'EUR'
            );

            // Test passes if no exceptions are thrown
            expect(true)->toBeTrue();
        });
    });

    describe('trackApiCall', function () {
        it('tracks API call with endpoint and metrics', function () {
            // trackApiCall only logs to performance collector, not storage
            // Storage expectations are handled by global mocks

            // Track API call
            $this->collector->trackApiCall(
                endpoint: '/api/users',
                method: 'GET',
                responseCode: 200,
                responseTime: 0.125
            );

            // Test passes if no exceptions are thrown
            expect(true)->toBeTrue();
        });
    });

    describe('edge cases', function () {
        it('handles zero sampling rate', function () {
            // Create new Random mock for this test
            $randomMock = Mockery::mock(RandomGenerator::class);

            // Random float will be called and return value > 0.0 (will fail sampling)
            $randomMock->shouldReceive('float')->with(0, 1)->andReturn(0.1);

            // Create collector with 0% sampling (no tracking)
            $noSamplingCollector = new AnalyticsCollector(
                performanceCollector: $this->performanceCollector,
                storage: $this->storage,
                random: $randomMock,
                serverEnvironment: $this->serverEnvironment,
                enabled: true,
                samplingRate: 0.0
            );

            // Storage should NOT be called
            $this->storage->shouldNotReceive('storeRawData');

            // Track action (should be sampled out)
            $noSamplingCollector->trackAction('click', AnalyticsCategory::USER_BEHAVIOR);
        });

        it('handles full sampling rate', function () {
            // With 100% sampling, float() should NOT be called (early return)
            // Expect storage to be called
            $this->storage
                ->shouldReceive('storeRawData')
                ->once()
                ->with(Mockery::type('array'), 1.0);

            // Track action (should be tracked)
            $this->collector->trackAction('click', AnalyticsCategory::USER_BEHAVIOR);
        });
    });
});
