<?php

declare(strict_types=1);

namespace App\Framework\Http\Session;

use App\Framework\Http\Session\ValueObjects\FlashMessage;
use App\Framework\Http\Session\ValueObjects\FlashMessageType;

/**
 * The FlashBag class provides a mechanism to manage flash messages in a session,
 * typically used for temporary notifications such as success messages, error alerts,
 * or other transient data between requests.
 */
final class FlashBag
{
    public function __construct(
        private Session $session
    ) {
    }

    public function add(string $type, string $message): void
    {
        $flashMessageType = FlashMessageType::tryFrom($type) ?? FlashMessageType::INFO;
        $flashMessage = new FlashMessage($flashMessageType, $message, \App\Framework\Core\ValueObjects\Timestamp::fromFloat(microtime(true)));

        $this->session->updateSessionData(function ($sessionData) use ($flashMessage) {
            return $sessionData->withFlashMessages(
                $sessionData->flashMessages->add($flashMessage)
            );
        });
    }

    public function get(string $type): array
    {
        $flashMessageType = FlashMessageType::tryFrom($type) ?? FlashMessageType::INFO;
        $sessionData = $this->session->getSessionData(true); // Include marked for deletion

        // Get messages of the specified type
        $messages = $sessionData->flashMessages->getMessagesByType($flashMessageType);

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

        return $messages;
    }

    public function has(string $type): bool
    {
        $flashMessageType = FlashMessageType::tryFrom($type) ?? FlashMessageType::INFO;

        return $this->session->getSessionData()->flashMessages->hasType($flashMessageType);
    }

    public function all(): array
    {
        $sessionData = $this->session->getSessionData(true); // Include marked for deletion
        $allMessages = $sessionData->flashMessages->toArray();

        // Mark all flash message types for deletion (deferred flash behavior)
        if (! empty($allMessages)) {
            foreach (array_keys($allMessages) as $type) {
                $this->session->flashManager->mark(SessionKey::FLASH->value, $type);
            }
        }

        return $allMessages;
    }

    /**
     * Add convenience methods for common flash message types
     */
    public function addSuccess(string $message): void
    {
        $this->add('success', $message);
    }

    public function addError(string $message): void
    {
        $this->add('error', $message);
    }

    public function addWarning(string $message): void
    {
        $this->add('warning', $message);
    }

    public function addInfo(string $message): void
    {
        $this->add('info', $message);
    }

    /**
     * Get convenience methods for common flash message types
     */
    public function getSuccess(): array
    {
        return $this->get('success');
    }

    public function getError(): array
    {
        return $this->get('error');
    }

    public function getWarning(): array
    {
        return $this->get('warning');
    }

    public function getInfo(): array
    {
        return $this->get('info');
    }

    /**
     * Apply deferred deletion - remove all marked flash data
     * This method is called at the end of the request cycle
     */
    public function age(): void
    {
        // Apply the FlashManager filtering to remove marked items
        $filteredSessionData = $this->session->flashManager->filterSessionDataObject(
            $this->session->getSessionData(true) // Include marked for deletion
        );

        // Update the session with the filtered data (removing marked items)
        $this->session->setSessionData($filteredSessionData);

        // Clear all markings since they've been processed
        $this->session->flashManager->clearAllMarkings();
    }
}
