<?php

declare(strict_types=1);

use App\Framework\DateTime\FrozenClock;
use App\Framework\Http\Session\Session;
use App\Framework\Http\Session\SessionId;
use App\Framework\Random\TestableRandomGenerator;
use App\Framework\Security\CsrfTokenGenerator;

beforeEach(function () {
    $this->clock = new FrozenClock();
    $this->randomGenerator = new TestableRandomGenerator();
    $this->csrfTokenGenerator = new CsrfTokenGenerator($this->randomGenerator);
    $this->sessionId = SessionId::fromString('testsessionid1234567890abcdefgh12');
    $this->session = new Session($this->sessionId, $this->clock, $this->csrfTokenGenerator);
});

describe('Session Basic Operations', function () {
    test('session starts correctly', function () {
        expect($this->session->isStarted())->toBeTrue();
    });

    test('session has correct id', function () {
        expect($this->session->id)->toBe($this->sessionId);
    });

    test('session data operations work correctly', function () {
        // Set data
        $this->session->set('key1', 'value1');
        $this->session->set('key2', 42);

        // Get data
        expect($this->session->get('key1'))->toBe('value1');
        expect($this->session->get('key2'))->toBe(42);
        expect($this->session->get('nonexistent'))->toBeNull();
        expect($this->session->get('nonexistent', 'default'))->toBe('default');
    });

    test('session has() method works correctly', function () {
        $this->session->set('existing_key', 'value');

        expect($this->session->has('existing_key'))->toBeTrue();
        expect($this->session->has('nonexistent_key'))->toBeFalse();
    });

    test('session remove() method works correctly', function () {
        $this->session->set('key_to_remove', 'value');
        expect($this->session->has('key_to_remove'))->toBeTrue();

        $this->session->remove('key_to_remove');
        expect($this->session->has('key_to_remove'))->toBeFalse();
    });

    test('session all() method returns all data', function () {
        $this->session->set('key1', 'value1');
        $this->session->set('key2', 'value2');

        $allData = $this->session->all();

        // Die Komponenten fügen ihre eigenen Keys hinzu
        expect($allData)->toHaveKey('key1');
        expect($allData)->toHaveKey('key2');
        expect($allData['key1'])->toBe('value1');
        expect($allData['key2'])->toBe('value2');

        // Komponenten-Keys sollten auch existieren
        expect($allData)->toHaveKey('__flash');
        expect($allData)->toHaveKey('__validation_errors');
        expect($allData)->toHaveKey('__form_data');
        expect($allData)->toHaveKey('__csrf');
    });

    test('session clear() method removes all data', function () {
        $this->session->set('key1', 'value1');
        $this->session->set('key2', 'value2');

        $this->session->clear();
        expect($this->session->all())->toBe([]);
    });
});

describe('Session fromArray functionality', function () {
    test('fromArray() sets session data correctly', function () {
        $data = [
            'user_id' => 123,
            'username' => 'testuser',
            'preferences' => ['theme' => 'dark'],
        ];

        $this->session->fromArray($data);

        expect($this->session->get('user_id'))->toBe(123);
        expect($this->session->get('username'))->toBe('testuser');
        expect($this->session->get('preferences'))->toBe(['theme' => 'dark']);
    });

    test('fromArray() initializes components after setting data', function () {
        // Komponenten sollten nach fromArray() verfügbar sein
        $this->session->fromArray(['some' => 'data']);

        expect($this->session->flash)->toBeInstanceOf(\App\Framework\Http\Session\FlashBag::class);
        expect($this->session->validation)->toBeInstanceOf(\App\Framework\Http\Session\ValidationErrorBag::class);
        expect($this->session->form)->toBeInstanceOf(\App\Framework\Http\Session\FormDataStorage::class);
        expect($this->session->security)->toBeInstanceOf(\App\Framework\Http\Session\SecurityManager::class);
        expect($this->session->csrf)->toBeInstanceOf(\App\Framework\Http\Session\CsrfProtection::class);
    });

    test('fromArray() preserves existing data structure', function () {
        // Teste ob komplexe Datenstrukturen korrekt gespeichert werden
        $complexData = [
            'user' => [
                'id' => 123,
                'profile' => [
                    'name' => 'Test User',
                    'settings' => [
                        'notifications' => true,
                        'theme' => 'dark',
                    ],
                ],
            ],
            'session_data' => [
                'csrf_token' => 'token123',
                'flash_messages' => [
                    'success' => ['Message saved!'],
                    'error' => ['Validation failed!'],
                ],
            ],
        ];

        $this->session->fromArray($complexData);

        expect($this->session->get('user'))->toBe($complexData['user']);
        expect($this->session->get('session_data'))->toBe($complexData['session_data']);

        // Teste nested access
        $user = $this->session->get('user');
        expect($user['profile']['name'])->toBe('Test User');
    });
});

describe('Session Component Integration', function () {
    test('components are properly initialized after fromArray()', function () {
        $this->session->fromArray([]);

        // Teste ob alle Komponenten korrekt initialisiert sind
        expect($this->session->flash)->toBeInstanceOf(\App\Framework\Http\Session\FlashBag::class);
        expect($this->session->validation)->toBeInstanceOf(\App\Framework\Http\Session\ValidationErrorBag::class);
        expect($this->session->form)->toBeInstanceOf(\App\Framework\Http\Session\FormDataStorage::class);
        expect($this->session->security)->toBeInstanceOf(\App\Framework\Http\Session\SecurityManager::class);
        expect($this->session->csrf)->toBeInstanceOf(\App\Framework\Http\Session\CsrfProtection::class);
    });

    test('components can access session data', function () {
        $this->session->fromArray([
            'flash' => ['success' => ['Test message']],
            'csrf_token' => 'test-token',
        ]);

        // Flash component sollte auf die Session-Daten zugreifen können
        expect($this->session->get('flash'))->toBe(['success' => ['Test message']]);
        expect($this->session->get('csrf_token'))->toBe('test-token');
    });

    test('double initialization is prevented', function () {
        $this->session->fromArray([]);
        $flash1 = $this->session->flash;

        // Zweite Initialisierung sollte die gleichen Instanzen zurückgeben
        $this->session->fromArray(['new' => 'data']);
        $flash2 = $this->session->flash;

        expect($flash1)->toBe($flash2);
    });
});

describe('Session Edge Cases', function () {
    test('handles null values correctly', function () {
        $this->session->set('null_value', null);

        expect($this->session->has('null_value'))->toBeTrue();
        expect($this->session->get('null_value'))->toBeNull();
    });

    test('handles empty string values correctly', function () {
        $this->session->set('empty_string', '');

        expect($this->session->has('empty_string'))->toBeTrue();
        expect($this->session->get('empty_string'))->toBe('');
    });

    test('handles boolean values correctly', function () {
        $this->session->set('true_value', true);
        $this->session->set('false_value', false);

        expect($this->session->get('true_value'))->toBeTrue();
        expect($this->session->get('false_value'))->toBeFalse();
    });

    test('handles array values correctly', function () {
        $arrayValue = ['nested' => ['deep' => 'value']];
        $this->session->set('array_value', $arrayValue);

        expect($this->session->get('array_value'))->toBe($arrayValue);
    });

    test('handles object serialization edge cases', function () {
        // Teste was passiert wenn Objekte in der Session gespeichert werden
        $stdClass = new stdClass();
        $stdClass->property = 'value';

        $this->session->set('object_value', $stdClass);
        $retrieved = $this->session->get('object_value');

        expect($retrieved)->toBeInstanceOf(stdClass::class);
        expect($retrieved->property)->toBe('value');
    });
});

describe('Session Debug Information', function () {
    test('__debugInfo() returns correct format', function () {
        $this->session->set('debug_key', 'debug_value');

        $debugInfo = $this->session->__debugInfo();

        expect($debugInfo[0])->toBe($this->sessionId->toString());
        expect($debugInfo['debug_key'])->toBe('debug_value');
    });
});
