<?php

declare(strict_types=1);

use App\Framework\DateTime\FrozenClock;
use App\Framework\Http\Session\InMemorySessionStorage;
use App\Framework\Http\Session\Session;
use App\Framework\Http\Session\SessionId;
use App\Framework\Http\Session\SessionKey;
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('testlazyinitsessionid1234567890ab');
});

describe('Session Component Lazy Initialization', function () {
    test('only used component keys appear in session data', function () {
        // 1. Session erstellen
        $session = new Session($this->sessionId, $this->clock, $this->csrfTokenGenerator);
        $session->fromArray([]);

        // 2. Nur einige Komponenten verwenden
        $session->flash->add('info', 'Test message');
        $session->csrf->generateToken('test_form');
        // Bewusst NICHT verwenden: validation und form

        // 3. Session-Daten prüfen
        $sessionData = $session->all();

        // 4. Nur verwendete Keys sollten existieren
        expect($sessionData)->toHaveKey(SessionKey::FLASH->value);
        expect($sessionData)->toHaveKey(SessionKey::CSRF->value);

        // 5. Nicht verwendete Keys sollten NICHT existieren
        expect($sessionData)->not->toHaveKey(SessionKey::VALIDATION_ERRORS->value);
        expect($sessionData)->not->toHaveKey(SessionKey::FORM_DATA->value);

        // 6. Inhalt der verwendeten Keys prüfen
        expect($sessionData[SessionKey::FLASH->value])->toHaveKey('info');
        expect($sessionData[SessionKey::CSRF->value])->toHaveKey('test_form');
    });

    test('accessing component creates its key even if empty', function () {
        // 1. Session erstellen
        $session = new Session($this->sessionId, $this->clock, $this->csrfTokenGenerator);
        $session->fromArray([]);

        // 2. Komponente zugreifen ohne Daten hinzuzufügen
        $errors = $session->validation->get('nonexistent_form'); // Sollte [] zurückgeben
        expect($errors)->toBe([]);

        // 3. Jetzt sollte der Key existieren (da Komponente initialisiert wurde)
        $sessionData = $session->all();
        expect($sessionData)->toHaveKey(SessionKey::VALIDATION_ERRORS->value);
        expect($sessionData[SessionKey::VALIDATION_ERRORS->value])->toBe([]);
    });

    test('component keys persist after session reload', function () {
        $storage = new InMemorySessionStorage();

        // 1. Session mit gemischter Nutzung erstellen
        $session1 = new Session($this->sessionId, $this->clock, $this->csrfTokenGenerator);
        $session1->fromArray([]);

        $session1->flash->add('success', 'Saved!');
        $session1->csrf->generateToken('edit_form');
        // validation und form werden NICHT verwendet

        // 2. Session speichern
        $storage->write($this->sessionId, $session1->all());

        // 3. Session neu laden
        $loadedData = $storage->read($this->sessionId);
        $session2 = new Session($this->sessionId, $this->clock, $this->csrfTokenGenerator);
        $session2->fromArray($loadedData);

        // 4. Nur die Keys die vorher verwendet wurden sollten existieren
        $reloadedData = $session2->all();
        expect($reloadedData)->toHaveKey(SessionKey::FLASH->value);
        expect($reloadedData)->toHaveKey(SessionKey::CSRF->value);
        expect($reloadedData)->not->toHaveKey(SessionKey::VALIDATION_ERRORS->value);
        expect($reloadedData)->not->toHaveKey(SessionKey::FORM_DATA->value);

        // 5. Daten sollten korrekt geladen werden
        expect($session2->flash->get('success'))->toBe(['Saved!']);
    });

    test('unused components can be accessed after session reload', function () {
        $storage = new InMemorySessionStorage();

        // 1. Session mit teilweiser Nutzung
        $session1 = new Session($this->sessionId, $this->clock, $this->csrfTokenGenerator);
        $session1->fromArray([]);
        $session1->flash->add('info', 'Test');
        $storage->write($this->sessionId, $session1->all());

        // 2. Session neu laden
        $loadedData = $storage->read($this->sessionId);
        $session2 = new Session($this->sessionId, $this->clock, $this->csrfTokenGenerator);
        $session2->fromArray($loadedData);

        // 3. Bisher unverwendete Komponenten sollten funktionieren
        $session2->validation->add('new_form', ['field' => ['New error']]);
        $session2->form->store('new_form', ['data' => 'New data']);

        // 4. Nach Verwendung sollten die Keys existieren
        $finalData = $session2->all();
        expect($finalData)->toHaveKey(SessionKey::VALIDATION_ERRORS->value);
        expect($finalData)->toHaveKey(SessionKey::FORM_DATA->value);

        // 5. Ursprüngliche Daten sollten erhalten bleiben
        expect($finalData)->toHaveKey(SessionKey::FLASH->value);
    });

    test('simulates live system behavior from your example', function () {
        // 1. Simuliere eine typische Web-App Session
        $session = new Session($this->sessionId, $this->clock, $this->csrfTokenGenerator);
        $session->fromArray([]);

        // 2. Simuliere Security Tracking (automatisch durch Framework)
        $session->security->updateActivity();

        // 3. Simuliere CSRF Token Generierung für verschiedene Formulare
        $session->csrf->generateToken('form_bf9d2e47868b');
        $session->csrf->generateToken('form_386bb8ff6647');

        // 4. Flash wird initialisiert aber ist leer (typisch nach Redirect)
        // Simuliere: Flash wurde bereits abgerufen und ist jetzt leer
        $session->flash->get('any_type'); // Initialisiert das Array

        // 5. validation und form werden NICHT verwendet (kein Fehler, kein Form-Data)

        // 6. Prüfe Session-Daten (sollte dem Live-System ähneln)
        $sessionData = $session->all();

        expect($sessionData)->toHaveKey('__security');
        expect($sessionData)->toHaveKey('__csrf');
        expect($sessionData)->toHaveKey('__flash');
        expect($sessionData['__flash'])->toBe([]); // Leer, wie im Live-System

        // Diese Keys fehlen - das ist korrekt!
        expect($sessionData)->not->toHaveKey('__validation_errors');
        expect($sessionData)->not->toHaveKey('__form_data');

        // 7. CSRF sollte Tokens für verschiedene Formulare enthalten
        expect($sessionData['__csrf'])->toHaveKey('form_bf9d2e47868b');
        expect($sessionData['__csrf'])->toHaveKey('form_386bb8ff6647');
    });

    test('components become available when first needed', function () {
        $storage = new InMemorySessionStorage();

        // 1. Session wie im Live-System
        $existingData = [
            '__security' => [
                'user_agent' => 'Mozilla/5.0 (Test Browser)',
                'ip_address' => '127.0.0.1',
                'last_activity' => time(),
            ],
            '__csrf' => [
                'test_form' => [
                    ['token' => 'abc123', 'created_at' => time(), 'used_at' => null],
                ],
            ],
            '__flash' => [],
        ];

        $session = new Session($this->sessionId, $this->clock, $this->csrfTokenGenerator);
        $session->fromArray($existingData);

        // 2. Bisher fehlende Komponenten sind trotzdem verfügbar
        expect($session->validation->has('any_form'))->toBeFalse();
        expect($session->form->has('any_form'))->toBeFalse();

        // 3. Wenn sie verwendet werden, funktionieren sie
        $session->validation->add('contact_form', ['email' => ['Required']]);
        $session->form->store('contact_form', ['name' => 'John']);

        // 4. Jetzt sollten die Keys existieren
        $finalData = $session->all();
        expect($finalData)->toHaveKey('__validation_errors');
        expect($finalData)->toHaveKey('__form_data');

        // 5. Bestehende Daten bleiben erhalten
        expect($finalData['__security']['user_agent'])->toBe('Mozilla/5.0 (Test Browser)');
        expect($finalData['__csrf'])->toHaveKey('test_form');
        expect($finalData['__flash'])->toBe([]);
    });
});
