<?php

declare(strict_types=1);

/**
 * Integration tests for LiveComponent DevTools functionality
 *
 * Tests the client-side DevTools overlay integration with the LiveComponent system.
 * These tests verify that DevTools correctly tracks component lifecycle events,
 * actions, network requests, and performance metrics.
 */

describe('LiveComponent DevTools Integration', function () {
    it('initializes devtools in development mode', function () {
        // Simulate development environment
        $html = <<<'HTML'
<!DOCTYPE html>
<html data-env="development">
<head>
    <script type="module" src="/resources/js/main.js"></script>
</head>
<body>
    <div data-component-id="test-123" data-component-name="TestComponent">
        Test Component
    </div>
</body>
</html>
HTML;

        expect($html)->toContain('data-env="development"');
        expect($html)->toContain('data-component-id');
    });

    it('does not initialize devtools in production mode', function () {
        $html = <<<'HTML'
<!DOCTYPE html>
<html data-env="production">
<head>
    <script type="module" src="/resources/js/main.js"></script>
</head>
<body>
    <div data-component-id="test-123" data-component-name="TestComponent">
        Test Component
    </div>
</body>
</html>
HTML;

        expect($html)->toContain('data-env="production"');
    });

    it('tracks component initialization', function () {
        // Simulate component registration event
        $event = [
            'type' => 'component:initialized',
            'detail' => [
                'componentId' => 'comp-abc-123',
                'componentName' => 'UserProfile',
                'initialState' => ['userId' => 1, 'name' => 'John Doe']
            ]
        ];

        expect($event['type'])->toBe('component:initialized');
        expect($event['detail'])->toHaveKey('componentId');
        expect($event['detail'])->toHaveKey('componentName');
        expect($event['detail'])->toHaveKey('initialState');
    });

    it('tracks component actions with timing', function () {
        $actionEvent = [
            'type' => 'component:action',
            'detail' => [
                'componentId' => 'comp-abc-123',
                'actionName' => 'handleClick',
                'startTime' => 1000.5,
                'endTime' => 1025.8,
                'duration' => 25.3,
                'success' => true
            ]
        ];

        expect($actionEvent['detail']['duration'])->toBeGreaterThan(0);
        expect($actionEvent['detail']['success'])->toBeTrue();
    });

    it('tracks network requests', function () {
        $networkEvent = [
            'type' => 'component:network',
            'detail' => [
                'componentId' => 'comp-abc-123',
                'method' => 'POST',
                'url' => '/api/users',
                'status' => 200,
                'duration' => 145.5,
                'requestBody' => ['name' => 'John'],
                'responseBody' => ['id' => 1, 'name' => 'John']
            ]
        ];

        expect($networkEvent['detail']['status'])->toBe(200);
        expect($networkEvent['detail']['duration'])->toBeGreaterThan(0);
    });

    it('tracks component state changes', function () {
        $stateEvent = [
            'type' => 'component:state-changed',
            'detail' => [
                'componentId' => 'comp-abc-123',
                'previousState' => ['count' => 0],
                'newState' => ['count' => 1],
                'timestamp' => time()
            ]
        ];

        expect($stateEvent['detail']['newState']['count'])->toBe(1);
        expect($stateEvent['detail']['previousState']['count'])->toBe(0);
    });

    it('tracks component destruction', function () {
        $destroyEvent = [
            'type' => 'component:destroyed',
            'detail' => [
                'componentId' => 'comp-abc-123',
                'componentName' => 'UserProfile',
                'timestamp' => time()
            ]
        ];

        expect($destroyEvent['type'])->toBe('component:destroyed');
        expect($destroyEvent['detail'])->toHaveKey('componentId');
    });

    it('filters action log by component', function () {
        $actionLog = [
            ['componentId' => 'comp-1', 'actionName' => 'handleClick'],
            ['componentId' => 'comp-2', 'actionName' => 'handleSubmit'],
            ['componentId' => 'comp-1', 'actionName' => 'handleChange'],
            ['componentId' => 'comp-3', 'actionName' => 'handleClick'],
        ];

        $filtered = array_filter($actionLog, fn($log) => $log['componentId'] === 'comp-1');

        expect(count($filtered))->toBe(2);
    });

    it('filters action log by action name', function () {
        $actionLog = [
            ['componentId' => 'comp-1', 'actionName' => 'handleClick'],
            ['componentId' => 'comp-2', 'actionName' => 'handleSubmit'],
            ['componentId' => 'comp-3', 'actionName' => 'handleClick'],
        ];

        $filtered = array_filter($actionLog, fn($log) => $log['actionName'] === 'handleClick');

        expect(count($filtered))->toBe(2);
    });

    it('clears action log', function () {
        $actionLog = [
            ['componentId' => 'comp-1', 'actionName' => 'handleClick'],
            ['componentId' => 'comp-2', 'actionName' => 'handleSubmit'],
        ];

        expect(count($actionLog))->toBe(2);

        $actionLog = [];

        expect(count($actionLog))->toBe(0);
    });

    it('exports action log as JSON', function () {
        $actionLog = [
            ['componentId' => 'comp-1', 'actionName' => 'handleClick', 'duration' => 25.5],
            ['componentId' => 'comp-2', 'actionName' => 'handleSubmit', 'duration' => 35.8],
        ];

        $json = json_encode($actionLog, JSON_PRETTY_PRINT);

        expect($json)->toBeString();
        expect($json)->toContain('handleClick');
        expect($json)->toContain('handleSubmit');
    });

    it('creates DOM badge for component', function () {
        $badge = [
            'componentId' => 'comp-abc-123',
            'componentName' => 'UserProfile',
            'actionCount' => 5,
            'position' => ['top' => 100, 'left' => 50],
        ];

        expect($badge['componentId'])->toBe('comp-abc-123');
        expect($badge['actionCount'])->toBe(5);
        expect($badge['position'])->toHaveKey('top');
        expect($badge['position'])->toHaveKey('left');
    });

    it('updates badge action count', function () {
        $badge = ['actionCount' => 5];

        $badge['actionCount']++;

        expect($badge['actionCount'])->toBe(6);
    });

    it('removes badge when component destroyed', function () {
        $badges = [
            'comp-1' => ['componentId' => 'comp-1', 'actionCount' => 5],
            'comp-2' => ['componentId' => 'comp-2', 'actionCount' => 3],
        ];

        unset($badges['comp-1']);

        expect(count($badges))->toBe(1);
        expect($badges)->not->toHaveKey('comp-1');
        expect($badges)->toHaveKey('comp-2');
    });

    it('records performance metrics during recording', function () {
        $performanceRecording = [];

        // Simulate recording start
        $isRecording = true;

        if ($isRecording) {
            $performanceRecording[] = [
                'type' => 'action',
                'componentId' => 'comp-1',
                'actionName' => 'handleClick',
                'duration' => 25.5,
                'startTime' => 1000.0,
                'endTime' => 1025.5,
                'timestamp' => time()
            ];
        }

        expect(count($performanceRecording))->toBe(1);
        expect($performanceRecording[0]['type'])->toBe('action');
    });

    it('does not record performance when not recording', function () {
        $performanceRecording = [];

        // Simulate recording stopped
        $isRecording = false;

        if ($isRecording) {
            $performanceRecording[] = [
                'type' => 'action',
                'componentId' => 'comp-1',
                'actionName' => 'handleClick',
                'duration' => 25.5,
            ];
        }

        expect(count($performanceRecording))->toBe(0);
    });

    it('takes memory snapshots during recording', function () {
        $memorySnapshots = [];

        // Simulate memory snapshot
        $memorySnapshots[] = [
            'timestamp' => time(),
            'usedJSHeapSize' => 25000000,
            'totalJSHeapSize' => 50000000,
            'jsHeapSizeLimit' => 2000000000,
        ];

        expect(count($memorySnapshots))->toBe(1);
        expect($memorySnapshots[0])->toHaveKey('usedJSHeapSize');
        expect($memorySnapshots[0])->toHaveKey('totalJSHeapSize');
    });

    it('calculates memory delta correctly', function () {
        $initialMemory = 25000000;
        $currentMemory = 28000000;

        $delta = $currentMemory - $initialMemory;

        expect($delta)->toBe(3000000);
        expect($delta)->toBeGreaterThan(0); // Memory increased
    });

    it('aggregates action execution times', function () {
        $actionExecutionTimes = [];

        // Record multiple executions of same action
        $key = 'comp-1:handleClick';
        $actionExecutionTimes[$key] = [25.5, 30.2, 28.8, 32.1];

        $totalTime = array_sum($actionExecutionTimes[$key]);
        $avgTime = $totalTime / count($actionExecutionTimes[$key]);
        $count = count($actionExecutionTimes[$key]);

        expect($totalTime)->toBeGreaterThan(100);
        expect($avgTime)->toBeGreaterThan(25);
        expect($count)->toBe(4);
    });

    it('aggregates component render times', function () {
        $componentRenderTimes = [];

        $componentRenderTimes['comp-1'] = [45.5, 40.2, 42.8];

        $totalTime = array_sum($componentRenderTimes['comp-1']);
        $avgTime = $totalTime / count($componentRenderTimes['comp-1']);

        expect($totalTime)->toBeGreaterThan(120);
        expect($avgTime)->toBeGreaterThan(40);
    });

    it('limits performance recording to last 100 entries', function () {
        $performanceRecording = [];

        // Add 150 entries
        for ($i = 0; $i < 150; $i++) {
            $performanceRecording[] = [
                'type' => 'action',
                'componentId' => "comp-{$i}",
                'duration' => 25.5,
            ];

            // Keep only last 100
            if (count($performanceRecording) > 100) {
                array_shift($performanceRecording);
            }
        }

        expect(count($performanceRecording))->toBe(100);
    });

    it('limits memory snapshots to last 100', function () {
        $memorySnapshots = [];

        // Add 150 snapshots
        for ($i = 0; $i < 150; $i++) {
            $memorySnapshots[] = [
                'timestamp' => time() + $i,
                'usedJSHeapSize' => 25000000 + ($i * 10000),
            ];

            // Keep only last 100
            if (count($memorySnapshots) > 100) {
                array_shift($memorySnapshots);
            }
        }

        expect(count($memorySnapshots))->toBe(100);
    });

    it('formats bytes correctly', function () {
        $formatBytes = function (int $bytes): string {
            if ($bytes === 0) return '0 B';
            $k = 1024;
            $sizes = ['B', 'KB', 'MB', 'GB'];
            $i = (int) floor(log($bytes) / log($k));
            return round($bytes / pow($k, $i), 2) . ' ' . $sizes[$i];
        };

        expect($formatBytes(0))->toBe('0 B');
        expect($formatBytes(1024))->toBe('1 KB');
        expect($formatBytes(1048576))->toBe('1 MB');
        expect($formatBytes(1073741824))->toBe('1 GB');
        expect($formatBytes(2621440))->toBe('2.5 MB');
    });

    it('calculates performance summary correctly', function () {
        $performanceRecording = [
            ['type' => 'action', 'duration' => 25.5],
            ['type' => 'action', 'duration' => 30.2],
            ['type' => 'render', 'duration' => 45.5],
            ['type' => 'render', 'duration' => 40.2],
        ];

        $actionCount = count(array_filter($performanceRecording, fn($r) => $r['type'] === 'action'));
        $renderCount = count(array_filter($performanceRecording, fn($r) => $r['type'] === 'render'));
        $totalEvents = count($performanceRecording);

        $actionDurations = array_map(
            fn($r) => $r['duration'],
            array_filter($performanceRecording, fn($r) => $r['type'] === 'action')
        );
        $avgActionTime = $actionCount > 0 ? array_sum($actionDurations) / $actionCount : 0;

        $totalDuration = array_sum(array_column($performanceRecording, 'duration'));

        expect($totalEvents)->toBe(4);
        expect($actionCount)->toBe(2);
        expect($renderCount)->toBe(2);
        expect($avgActionTime)->toBeGreaterThan(27);
        expect($totalDuration)->toBeGreaterThan(140);
    });

    it('toggles devtools visibility with keyboard shortcut', function () {
        // Simulate Ctrl+Shift+D press
        $event = [
            'key' => 'D',
            'ctrlKey' => true,
            'shiftKey' => true,
            'altKey' => false,
        ];

        $shouldToggle = $event['key'] === 'D' && $event['ctrlKey'] && $event['shiftKey'];

        expect($shouldToggle)->toBeTrue();
    });

    it('switches between tabs correctly', function () {
        $tabs = ['components', 'actions', 'events', 'network', 'performance'];

        $activeTab = 'components';

        $activeTab = 'performance';

        expect($activeTab)->toBe('performance');
        expect(in_array($activeTab, $tabs))->toBeTrue();
    });

    it('exports network log correctly', function () {
        $networkLog = [
            [
                'componentId' => 'comp-1',
                'method' => 'POST',
                'url' => '/api/users',
                'status' => 200,
                'duration' => 145.5,
            ],
            [
                'componentId' => 'comp-2',
                'method' => 'GET',
                'url' => '/api/users/1',
                'status' => 200,
                'duration' => 85.2,
            ],
        ];

        $json = json_encode($networkLog, JSON_PRETTY_PRINT);

        expect($json)->toBeString();
        expect($json)->toContain('POST');
        expect($json)->toContain('GET');
        expect($json)->toContain('/api/users');
    });
});
