<?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\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('testflashmanagersessionidlong123');
    $this->session = new Session($this->sessionId, $this->clock, $this->csrfTokenGenerator);
    $this->session->fromArray([]);
});

describe('FlashManager Core Functionality', function () {
    test('can mark items for deletion', function () {
        $flashManager = $this->session->flashManager;

        $flashManager->mark('validation_errors', 'contact_form');
        $flashManager->mark('form_data', 'contact_form');

        expect($flashManager->isMarked('validation_errors', 'contact_form'))->toBeTrue();
        expect($flashManager->isMarked('form_data', 'contact_form'))->toBeTrue();
        expect($flashManager->isMarked('validation_errors', 'login_form'))->toBeFalse();
    });

    test('can unmark items', function () {
        $flashManager = $this->session->flashManager;

        $flashManager->mark('validation_errors', 'contact_form');
        expect($flashManager->isMarked('validation_errors', 'contact_form'))->toBeTrue();

        $flashManager->unmark('validation_errors', 'contact_form');
        expect($flashManager->isMarked('validation_errors', 'contact_form'))->toBeFalse();
    });

    test('can mark multiple items at once', function () {
        $flashManager = $this->session->flashManager;

        $flashManager->markMultiple('validation_errors', ['contact_form', 'login_form', 'register_form']);

        expect($flashManager->isMarked('validation_errors', 'contact_form'))->toBeTrue();
        expect($flashManager->isMarked('validation_errors', 'login_form'))->toBeTrue();
        expect($flashManager->isMarked('validation_errors', 'register_form'))->toBeTrue();
    });

    test('tracks marked items correctly', function () {
        $flashManager = $this->session->flashManager;

        $flashManager->mark('validation_errors', 'form1');
        $flashManager->mark('validation_errors', 'form2');
        $flashManager->mark('form_data', 'form1');

        expect($flashManager->hasMarkedItems('validation_errors'))->toBeTrue();
        expect($flashManager->hasMarkedItems('form_data'))->toBeTrue();
        expect($flashManager->hasMarkedItems('csrf'))->toBeFalse();

        $markedValidationKeys = $flashManager->getMarkedKeys('validation_errors');
        expect($markedValidationKeys)->toBe(['form1', 'form2']);

        $markedFormKeys = $flashManager->getMarkedKeys('form_data');
        expect($markedFormKeys)->toBe(['form1']);
    });
});

describe('FlashManager Session Data Filtering', function () {
    test('filters out marked validation errors', function () {
        // 1. Session mit Validation Errors füllen
        $this->session->validation->add('contact_form', ['email' => ['Invalid email']]);
        $this->session->validation->add('login_form', ['password' => ['Too short']]);

        // 2. Eine Form zum Löschen markieren
        $this->session->flashManager->mark(SessionKey::VALIDATION_ERRORS->value, 'contact_form');

        // 3. Gefilterte Session-Daten sollten nur unmarkierte Daten enthalten
        $filteredData = $this->session->all();

        expect($filteredData)->toHaveKey(SessionKey::VALIDATION_ERRORS->value);
        expect($filteredData[SessionKey::VALIDATION_ERRORS->value])->not->toHaveKey('contact_form');
        expect($filteredData[SessionKey::VALIDATION_ERRORS->value])->toHaveKey('login_form');

        // 4. Ungefilterte Daten sollten noch alle Daten enthalten
        $unfilteredData = $this->session->all(includeMarkedForDeletion: true);
        expect($unfilteredData[SessionKey::VALIDATION_ERRORS->value])->toHaveKey('contact_form');
        expect($unfilteredData[SessionKey::VALIDATION_ERRORS->value])->toHaveKey('login_form');
    });

    test('filters out marked form data', function () {
        // 1. Session mit Form Data füllen
        $this->session->form->store('contact_form', ['name' => 'John', 'email' => 'john@test.com']);
        $this->session->form->store('login_form', ['username' => 'john']);

        // 2. Eine Form zum Löschen markieren
        $this->session->flashManager->mark(SessionKey::FORM_DATA->value, 'contact_form');

        // 3. Gefilterte Daten sollten nur unmarkierte Daten enthalten
        $filteredData = $this->session->all();

        expect($filteredData[SessionKey::FORM_DATA->value])->not->toHaveKey('contact_form');
        expect($filteredData[SessionKey::FORM_DATA->value])->toHaveKey('login_form');
    });

    test('removes empty components after filtering', function () {
        // 1. Session mit nur einer Validation Error
        $this->session->validation->add('contact_form', ['email' => ['Invalid']]);

        // 2. Diese eine Error zum Löschen markieren
        $this->session->flashManager->mark(SessionKey::VALIDATION_ERRORS->value, 'contact_form');

        // 3. Nach dem Filtern sollte die ganze Komponente entfernt werden
        $filteredData = $this->session->all();

        expect($filteredData)->not->toHaveKey(SessionKey::VALIDATION_ERRORS->value);
    });

    test('filters multiple components simultaneously', function () {
        // 1. Session mit verschiedenen Daten füllen
        $this->session->validation->add('contact_form', ['email' => ['Invalid']]);
        $this->session->form->store('contact_form', ['name' => 'John']);
        $this->session->flash->add('success', 'Data saved');

        // 2. Validation und Form Data markieren
        $this->session->flashManager->mark(SessionKey::VALIDATION_ERRORS->value, 'contact_form');
        $this->session->flashManager->mark(SessionKey::FORM_DATA->value, 'contact_form');

        // 3. Beide sollten gefiltert werden, Flash sollte bleiben
        $filteredData = $this->session->all();

        expect($filteredData)->not->toHaveKey(SessionKey::VALIDATION_ERRORS->value);
        expect($filteredData)->not->toHaveKey(SessionKey::FORM_DATA->value);
        expect($filteredData)->toHaveKey(SessionKey::FLASH->value);
    });
});

describe('FlashManager with ValidationErrorBag Integration', function () {
    test('getAndFlash marks validation errors for deletion', function () {
        // 1. Validation Errors hinzufügen
        $this->session->validation->add('contact_form', [
            'email' => ['Invalid email format'],
            'name' => ['Name is required'],
        ]);

        // 2. Mit getAndFlash abrufen
        $errors = $this->session->validation->getAndFlash('contact_form');

        // 3. Errors sollten zurückgegeben werden
        expect($errors['email'])->toBe(['Invalid email format']);
        expect($errors['name'])->toBe(['Name is required']);

        // 4. Sollten zum Löschen markiert sein
        expect($this->session->flashManager->isMarked(SessionKey::VALIDATION_ERRORS->value, 'contact_form'))->toBeTrue();

        // 5. Nach Session-Save sollten sie nicht mehr da sein
        $filteredData = $this->session->all();
        expect($filteredData)->not->toHaveKey(SessionKey::VALIDATION_ERRORS->value);
    });

    test('get without flash does not mark for deletion', function () {
        // 1. Validation Errors hinzufügen
        $this->session->validation->add('contact_form', ['email' => ['Invalid']]);

        // 2. Mit normalem get() abrufen
        $errors = $this->session->validation->get('contact_form');

        // 3. Errors sollten zurückgegeben werden
        expect($errors['email'])->toBe(['Invalid']);

        // 4. Sollten NICHT zum Löschen markiert sein
        expect($this->session->flashManager->isMarked(SessionKey::VALIDATION_ERRORS->value, 'contact_form'))->toBeFalse();

        // 5. Sollten nach Session-Save noch da sein
        $filteredData = $this->session->all();
        expect($filteredData[SessionKey::VALIDATION_ERRORS->value])->toHaveKey('contact_form');
    });
});

describe('FlashManager with FormDataStorage Integration', function () {
    test('getAndFlash marks form data for deletion', function () {
        // 1. Form Data hinzufügen
        $formData = ['name' => 'John Doe', 'email' => 'john@example.com'];
        $this->session->form->store('contact_form', $formData);

        // 2. Mit getAndFlash abrufen
        $retrievedData = $this->session->form->getAndFlash('contact_form');

        // 3. Daten sollten zurückgegeben werden
        expect($retrievedData)->toBe($formData);

        // 4. Sollten zum Löschen markiert sein
        expect($this->session->flashManager->isMarked(SessionKey::FORM_DATA->value, 'contact_form'))->toBeTrue();

        // 5. Nach Session-Save sollten sie nicht mehr da sein
        $filteredData = $this->session->all();
        expect($filteredData)->not->toHaveKey(SessionKey::FORM_DATA->value);
    });

    test('getFieldAndFlash marks form data for deletion', function () {
        // 1. Form Data hinzufügen
        $this->session->form->store('contact_form', ['name' => 'John', 'email' => 'john@test.com']);

        // 2. Einzelnes Feld mit Flash abrufen
        $name = $this->session->form->getFieldAndFlash('contact_form', 'name');

        // 3. Feldwert sollte zurückgegeben werden
        expect($name)->toBe('John');

        // 4. Ganze Form sollte zum Löschen markiert sein
        expect($this->session->flashManager->isMarked(SessionKey::FORM_DATA->value, 'contact_form'))->toBeTrue();
    });
});

describe('FlashManager Edge Cases', function () {
    test('marking empty data does nothing', function () {
        // 1. Leere Validation Errors abrufen mit Flash
        $errors = $this->session->validation->getAndFlash('nonexistent_form');

        // 2. Sollte leeres Array zurückgeben
        expect($errors)->toBe([]);

        // 3. Sollte NICHT markiert werden
        expect($this->session->flashManager->isMarked(SessionKey::VALIDATION_ERRORS->value, 'nonexistent_form'))->toBeFalse();
    });

    test('clearAllMarkings removes all markings', function () {
        // 1. Mehrere Markierungen setzen
        $this->session->flashManager->mark('validation_errors', 'form1');
        $this->session->flashManager->mark('form_data', 'form2');

        // 2. Alle Markierungen löschen
        $this->session->flashManager->clearAllMarkings();

        // 3. Keine Markierungen sollten mehr existieren
        expect($this->session->flashManager->isMarked('validation_errors', 'form1'))->toBeFalse();
        expect($this->session->flashManager->isMarked('form_data', 'form2'))->toBeFalse();
        expect($this->session->flashManager->getMarkedItems())->toBe([]);
    });

    test('filtering preserves non-marked data structure', function () {
        // 1. Komplexe Session-Struktur erstellen
        $this->session->set('user_id', 123);
        $this->session->validation->add('form1', ['error1' => ['msg1']]);
        $this->session->validation->add('form2', ['error2' => ['msg2']]);
        $this->session->form->store('form1', ['data1' => 'value1']);
        $this->session->flash->add('info', 'Information');

        // 2. Nur form1 markieren
        $this->session->flashManager->mark(SessionKey::VALIDATION_ERRORS->value, 'form1');

        // 3. Gefilterte Daten sollten korrekte Struktur haben
        $filteredData = $this->session->all();

        expect($filteredData['user_id'])->toBe(123);
        expect($filteredData[SessionKey::VALIDATION_ERRORS->value])->toHaveKey('form2');
        expect($filteredData[SessionKey::VALIDATION_ERRORS->value])->not->toHaveKey('form1');
        expect($filteredData[SessionKey::FORM_DATA->value])->toHaveKey('form1'); // Nicht markiert
        expect($filteredData[SessionKey::FLASH->value])->toHaveKey('info');
    });
});
