<?php

namespace apexl\Io\services;

use apexl\Io\includes\HookManager;
use apexl\Io\interfaces\messageInterface;
use Psr\Http\Message\ServerRequestInterface;

/**
 * Class Messages
 * Class to provide a generalised store for communication - allows the addition to, and subtraction from the api response.
 * @package apexl\Io\includes
 * @Todo Remove singleton
 */
final class Output implements messageInterface
{
    public function __construct(
        private readonly HttpPaths $httpPaths,
        private readonly HookManager $hookManager,
        private array $response = [],
        private array $messages = [],
        private array $metadata = [],
        private array $systemErrors = [],
    ) {}

    public function addSystemError($e, $severity = "critical"): void
    {
        $message = [
            'status' => in_array($e->getCode(), [200, 301, 302, 400, 401, 403, 500]) ? $e->getCode() : 500,
            'message' => $e->getMessage(),
            'file' => $e->getFile(),
            'line' => $e->getLine(),
            'trace' => $e->getTrace(),
            'severity' => $severity,
        ];
        $message = $this->hookManager->processHook('addSystemError', $message, $e, $severity, $this);
        $this->systemErrors[] = $message;
    }

    public function addMessage(
        string $name,
        string $type,
        string $content,
        string $icon = '',
        ?int $timeout = null,
        ?string $id = null
    ): void {
        $message = [
            'content' => $content,
            'type' => $type,
            'id' => $id ?? uniqid(),
            'icon' => $icon,
            'timeout' => $timeout,
        ];
        $message = $this->hookManager->processHook('addMessage', $message, $name, $this);
        $this->messages[$name] = $message;
    }

    public function addRedirect(string $name, string $destination): void
    {
        $this->addMetadata($name, 'redirect', $destination);
    }

    public function addMetadata($name, $type, $contents): void
    {
        $metadata = [$type => $contents];
        $metadata = $this->hookManager->processHook('addMetadata', $metadata, $name, $this);
        $this->metadata[$name] = $metadata;
    }

    /**
     * @param ServerRequestInterface|null $request
     */
    public function addResponse(?ServerRequestInterface $request = null, array $response = [], bool $success = true): void
    {
        //Allows us to alter the response provided with a hook. We require the request to grab the current route.
        //allow us to inject data to the response object from elseware and maintain it (or override as appropriate)
        $response = array_merge(($this->getResponse() ?? []), $response);
        $response['success'] = $success;
        $this->response = $this->hookManager->processHook(
            'addResponse',
            $response,
            ($request !== null ? $this->httpPaths->getRouteName($request) : null),
        );
    }

    public function getResponse(): array
    {
        return $this->response;
    }

    public function getOutput()
    {
        $output = [];
        if ($this->getSystemErrors()) {
            $output += ['systemErrors' => $this->getSystemErrors()];
        }
        if ($this->getResponse()) {
            $output += $this->getResponse();
        }
        if ($this->getMetadata()) {
            foreach ($this->getMetadata() as $meta) {
                $output += $meta;
            }
        }
        if ($this->getMessages()) {
            $output['messages'] = $this->getMessages();
        }

        return $output ?? [];
    }

    public function getSystemErrors(): false|array
    {
        return $this->systemErrors ?? false;
    }

    public function getMetadata($name = null)
    {
        return $this->metadata[$name] ?? $this->metadata;
    }

    public function getMessages($name = null)
    {
        return $this->messages[$name] ?? $this->messages;
    }
}
