<?php

declare(strict_types=1);

namespace App\Framework\Http\Session;

use App\Framework\Http\Session\ValueObjects\ValidationError;
use App\Framework\Validation\ValidationResult;

/**
 * A bag for managing validation errors associated with forms.
 *
 * This class provides methods to store, retrieve, check, and clear validation errors
 * for specific forms or fields within a session. Supports both single error messages
 * and multiple error messages per field.
 */
final readonly class ValidationErrorBag
{
    public function __construct(
        private Session $session
    ) {
    }

    /**
     * Fügt Validierungsfehler für ein Formular hinzu
     *
     * @param string $formId Formular-ID
     * @param array $errors Fehler-Array im Format ['field' => ['error1', 'error2']] oder ['field' => 'error']
     */
    public function add(string $formId, array $errors): void
    {
        $validationErrors = [];

        // Convert array errors to ValidationError objects
        foreach ($errors as $field => $fieldErrors) {
            $messages = is_array($fieldErrors) ? $fieldErrors : [$fieldErrors];
            foreach ($messages as $message) {
                $validationErrors[] = ValidationError::create($field, $message, [
                    'form_id' => $formId,
                ]);
            }
        }

        // Update session using ValidationErrorCollection
        $this->session->updateSessionData(function ($sessionData) use ($formId, $validationErrors) {
            $collection = $sessionData->validationErrors;
            foreach ($validationErrors as $error) {
                $collection = $collection->withError($formId, $error);
            }

            return $sessionData->withValidationErrors($collection);
        });
    }

    /**
     * Fügt Validierungsfehler aus einem ValidationResult hinzu
     */
    public function addFromValidationResult(string $formId, ValidationResult $validationResult): void
    {
        $this->add($formId, $validationResult->getAll());
    }

    /**
     * Gibt alle Fehler für ein Formular zurück OHNE sie zu löschen
     *
     * @param string $formId Formular-ID
     * @return array<string, string[]> Array von Fehlern gruppiert nach Feldern
     */
    public function get(string $formId): array
    {
        $sessionData = $this->session->getSessionData();
        $formErrors = $sessionData->validationErrors->getFormErrors($formId);

        // Convert ValidationError objects back to legacy array format
        $result = [];
        foreach ($formErrors as $field => $validationErrors) {
            $result[$field] = array_map(fn ($error) => $error->message, $validationErrors);
        }

        return $result;
    }

    /**
     * Gibt alle Fehler für ein Formular zurück UND markiert sie zum Löschen (Flash-Verhalten)
     *
     * @param string $formId Formular-ID
     * @return array<string, string[]> Array von Fehlern gruppiert nach Feldern
     */
    public function getAndFlash(string $formId): array
    {
        $sessionData = $this->session->getSessionData(true); // Include marked for deletion
        $formErrors = $sessionData->validationErrors->getFormErrors($formId);

        // Convert ValidationError objects back to legacy array format
        $result = [];
        foreach ($formErrors as $field => $validationErrors) {
            $result[$field] = array_map(fn ($error) => $error->message, $validationErrors);
        }

        // Mark for deletion (deferred flash behavior)
        if (! empty($result)) {
            $this->session->flashManager->mark(SessionKey::VALIDATION_ERRORS->value, $formId);
        }

        return $result;
    }

    /**
     * Prüft, ob Fehler für ein Formular oder Feld vorhanden sind
     */
    public function has(string $formId, ?string $field = null): bool
    {
        $sessionData = $this->session->getSessionData();

        if ($field === null) {
            return $sessionData->validationErrors->hasFormErrors($formId);
        }

        return $sessionData->validationErrors->hasFieldErrors($formId, $field);
    }

    /**
     * Gibt alle Fehlermeldungen für ein bestimmtes Feld zurück
     *
     * @param string $formId Formular-ID
     * @param string $field Feldname
     * @return array<string> Array von Fehlermeldungen für das Feld
     */
    public function getFieldErrors(string $formId, string $field): array
    {
        return $this->session->getSessionData()->validationErrors->getFieldErrorMessages($formId, $field);
    }

    /**
     * Gibt alle Fehlermeldungen für ein bestimmtes Feld zurück UND markiert das Formular zum Löschen
     *
     * @param string $formId Formular-ID
     * @param string $field Feldname
     * @return array<string> Array von Fehlermeldungen für das Feld
     */
    public function getFieldErrorsAndFlash(string $formId, string $field): array
    {
        $errors = $this->getAndFlash($formId);

        return $errors[$field] ?? [];
    }

    /**
     * Gibt die erste Fehlermeldung für ein Feld zurück (Rückwärtskompatibilität)
     *
     * @param string $formId Formular-ID
     * @param string $field Feldname
     * @return string|null Erste Fehlermeldung oder null
     */
    public function getField(string $formId, string $field): ?string
    {
        $fieldErrors = $this->getFieldErrors($formId, $field);

        return $fieldErrors[0] ?? null;
    }

    /**
     * Gibt alle Fehlermeldungen für ein Feld als String zurück
     *
     * @param string $formId Formular-ID
     * @param string $field Feldname
     * @param string $separator Trennzeichen zwischen Fehlermeldungen
     * @return string Alle Fehlermeldungen als String
     */
    public function getFieldErrorsAsString(string $formId, string $field, string $separator = ', '): string
    {
        $fieldErrors = $this->getFieldErrors($formId, $field);

        return implode($separator, $fieldErrors);
    }

    /**
     * Gibt alle Fehlermeldungen als flache Liste zurück
     *
     * @param string $formId Formular-ID
     * @return array<string> Liste aller Fehlermeldungen
     */
    public function getAllErrorMessages(string $formId): array
    {
        $errors = $this->get($formId);
        $messages = [];

        foreach ($errors as $fieldErrors) {
            foreach ($fieldErrors as $error) {
                $messages[] = $error;
            }
        }

        return $messages;
    }

    /**
     * Fügt eine einzelne Fehlermeldung zu einem Feld hinzu
     */
    public function addFieldError(string $formId, string $field, string $error): void
    {
        $validationData = $this->session->get(SessionKey::VALIDATION_ERRORS->value, []);

        if (! isset($validationData[$formId])) {
            $validationData[$formId] = [];
        }

        if (! isset($validationData[$formId][$field])) {
            $validationData[$formId][$field] = [];
        }

        $validationData[$formId][$field][] = $error;
        $this->session->set(SessionKey::VALIDATION_ERRORS->value, $validationData);
    }

    /**
     * Löscht alle Fehler für ein Formular
     */
    public function clear(string $formId): void
    {
        $this->session->updateSessionData(function ($sessionData) use ($formId) {
            return $sessionData->withValidationErrors(
                $sessionData->validationErrors->withoutForm($formId)
            );
        });
    }

    /**
     * Löscht Fehler für ein bestimmtes Feld
     */
    public function clearField(string $formId, string $field): void
    {
        $this->session->updateSessionData(function ($sessionData) use ($formId, $field) {
            return $sessionData->withValidationErrors(
                $sessionData->validationErrors->withoutField($formId, $field)
            );
        });
    }

    /**
     * Löscht alle Validierungsfehler
     */
    public function clearAll(): void
    {
        $this->session->updateSessionData(function ($sessionData) {
            return $sessionData->withValidationErrors(
                new \App\Framework\Http\Session\ValueObjects\ValidationErrorCollection([])
            );
        });
    }

    public function __debugInfo(): array
    {
        return $this->session->get(SessionKey::VALIDATION_ERRORS->value, []);
    }
}
