<?php

namespace App\Core;

/**
 * FlashMessage — Session Flash Message Store
 *
 * Stores ephemeral messages in the session for display on the NEXT request.
 * Messages are read once and then cleared (pull semantics).
 *
 * Flash message lifecycle:
 *   POST request:   FlashMessage::set('success', 'Guest added.')
 *                   → stores in $_SESSION['flash']['success']
 *   Redirect (302): headers sent, PHP exits.
 *   GET request:    Layout calls FlashMessage::pull()
 *                   → reads $_SESSION['flash'], clears it, returns array.
 *   View layer:     Renders the flash banner, displays message, removes it.
 *
 * Message types (convention — not enforced):
 *   'success'  → green banner   (operation completed)
 *   'error'    → red banner     (operation failed)
 *   'warning'  → amber banner   (partial success or caution)
 *   'info'     → blue banner    (informational)
 *
 * Multiple message types can be set in the same request:
 *   FlashMessage::set('success', 'Saved.');
 *   FlashMessage::set('warning', 'Email delivery queued.');
 *   → pull() returns ['success' => 'Saved.', 'warning' => 'Email delivery queued.']
 *
 * Note: BaseController::flash() already writes to $_SESSION['flash'] directly.
 * FlashMessage provides the same semantics via a dedicated class, used by
 * Services and other non-controller code.
 *
 * @package Inveetaire
 * @ref     DBP v1.0 Ch.6, INIT-010
 */
class FlashMessage
{
    /**
     * Session key under which all flash messages are stored.
     * Must be consistent across set(), pull(), and has().
     */
    const SESSION_KEY = 'flash';

    // ─────────────────────────────────────────────────────────────────────────
    // WRITE
    // ─────────────────────────────────────────────────────────────────────────

    /**
     * Store a flash message for display on the next request.
     *
     * If the session is not active, the message is silently logged and dropped
     * rather than causing a PHP notice or fatal error.
     *
     * Calling set() twice with the same $type overwrites the previous message.
     *
     * @param string $type    Message type: 'success', 'error', 'warning', 'info'.
     * @param string $message Human-readable message text.
     * @return void
     */
    public static function set(string $type, string $message): void
    {
        if (!Session::isActive()) {
            Logger::warning('FlashMessage::set() called before session is active.', [
                'type'    => $type,
                'message' => $message,
            ]);
            return;
        }

        // Read existing flash array (may contain other types), add this type.
        $flash = Session::get(self::SESSION_KEY, []);

        if (!is_array($flash)) {
            $flash = [];
        }

        $flash[$type] = $message;

        Session::set(self::SESSION_KEY, $flash);
    }

    // ─────────────────────────────────────────────────────────────────────────
    // READ & CLEAR
    // ─────────────────────────────────────────────────────────────────────────

    /**
     * Retrieve all flash messages and immediately clear them from the session.
     *
     * Called once per request, typically by the layout file during rendering.
     * Returns an empty array if no messages are set or session is inactive.
     *
     * @return array<string, string>  Type → message pairs.
     *                                Example: ['success' => 'Guest added.']
     */
    public static function pull(): array
    {
        if (!Session::isActive()) {
            return [];
        }

        $flash = Session::get(self::SESSION_KEY, []);
        Session::remove(self::SESSION_KEY);

        return is_array($flash) ? $flash : [];
    }

    // ─────────────────────────────────────────────────────────────────────────
    // INSPECTION
    // ─────────────────────────────────────────────────────────────────────────

    /**
     * Return true if one or more flash messages are waiting in the session.
     *
     * @return bool
     */
    public static function has(): bool
    {
        return Session::isActive() && Session::has(self::SESSION_KEY);
    }

    /**
     * Clear all flash messages without reading them.
     * Used in test teardown — not for production request paths.
     *
     * @return void
     */
    public static function clear(): void
    {
        Session::remove(self::SESSION_KEY);
    }
}
