<?php

namespace Snaju\Inception\Http\Message;

use Closure;
use Snaju\Inception\Util\Loaders\Collection;

//Base functionality credited to: https://gist.github.com/BaylorRae/984276

/**
 * Handles setting and clearing session messages to use for error handling and other purposes.
 *
 * @package Snaju\Inception\Http\Message
 */
class FlashMessage
{
    private Collection $messages;
    private bool $now = false;
    private static ?FlashMessage $instance = null;

    /**
     * Allows for following the Singleton method and returns the same instance.
     * @return FlashMessage
     */
    public static function instance(): FlashMessage
    {
        if (self::$instance === null) {
            self::$instance = new FlashMessage();
        }

        return self::$instance;
    }

    /**
     * Cloning this class is not allowed.
     */
    private function __clone() {}

    /**
     * Allow simplified method for adding messages
     * @param $name
     * @param $args
     */
    public function __call($name, $args)
    {
        $this->message($name, $args[0]);
    }

    /**
     * FlashMessage constructor.
     */
    public function __construct()
    {
        // Save all the messages
        $this->messages = new Collection();

        if (isset($_SESSION['flash_messages'])) {
            if (gettype($_SESSION['flash_messages']) === 'array') {
                $this->messages->addFromArray($_SESSION['flash_messages']);
            }
        }

        // Reset all flash messages by creating creating new session
        $collection = new Collection();
        $_SESSION['flash_messages'] = $collection->toArray();
    }

    /**
     * Adds a flash message to the collection using a given name/key and message.
     *
     * @param string $name
     * @param string $message
     */
    public function message(string $name, string $message)
    {
        if ($this->now) {
            $this->messages->add($name, $message);
            $this->now = false;
        }
        else {
            $collection = new Collection();
            $collection->addFromArray($_SESSION['flash_messages']);
            $collection->add($name, $message);
            $_SESSION['flash_messages'] = $collection->toArray();
        }
    }

    /**
     * Prints all the flash messages either using the default HTML markup
     * or accents a closure to execute. If closure specified, printing will need
     * to be handled by the closure.
     *
     * @param Closure|null $closure
     */
    public function each (Closure $closure = null)
    {
        if ($closure === null) {
            $closure = function($name, $message) {
                echo $this->generateDOMString($name, $message);
            };
        }

        foreach ($this->messages->toArray() as $name => $message) {
            echo $closure($name, $message);
        }
    }

    /**
     * Allow messages to be displayed instantly instead of waiting for the
     * for the next page request.
     *
     * @return $this
     */
    public function now(): FlashMessage
    {
        $this->now = true;
        return $this;
    }

    /**
     * Retrieve the session/flash message. Empty string returned if it doesn't exist.
     *
     * @param string $name
     * @return mixed|string
     */
    public function get(string $name)
    {
        $message = '';

        if ($this->messages->keyExists($name)) {
            $message = $this->messages->get($name);
        }

        return $message;
    }

    /**
     * Show a DOM representation of the message if it exists.
     *
     * @see FlashMessage->generateDOMString()
     * @param $name
     */
    public function showIfExists($name)
    {
        $message = $this->get($name);
        echo $this->generateDOMString($name, $message);
    }

    /**
     * Returns all messages as an array.
     * @return array
     */
    public function toArray(): array
    {
        return $this->messages->toArray();
    }

    /**
     * Generates a DOM representation of the message as a `div` element with
     * the class of "snj-flash-message $type."
     *
     * @param string $type
     * @param string $message
     * @return string
     */
    public function generateDOMString(string $type, string $message): string
    {
        return '<div class="snj-flash-message $type">'. $message .'</div>';
    }
}