<?php

namespace Slovocast\Controller;

use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Views\Twig;
use Slim\Routing\RouteContext;

abstract class Controller
{
    protected Request $request;
    protected Response $response;
    protected array $args;
    protected RouteContext $routeContext;
    protected Twig $view;

    /**
     * Make the Class invokable and pass it into a route as its handler.
     *
     * @param Request $request
     * @param Response $response
     * @param array $args List of possible URL arguments
     * @return Response
     */
    public function __invoke(
        Request $request,
        Response $response,
        array $args = []
    ): Response {
        $this->request = $request;
        $this->response = $response;
        $this->args = $args;
        $this->routeContext = RouteContext::fromRequest($this->request);
        $this->view = Twig::fromRequest($request);

        return $this->handle($request, $response);
    }

    /**
     * Implement this method for handling the request.
     *
     * @return Response
     */
    abstract public function handle(): Response;

    /**
     * Render the given Template. 
     *
     * @param string $templateName The name of the template
     * @param array $data The data for the template
     * @return Response
     */
    public function render(string $templateName, array $data = []): Response
    {
        return $this->view
                    ->render($this->response, $templateName, $data)
                    ->withHeader('Content-Type', 'text/html');
    }

    /**
     * Redirect helper for sending the client elsewhere.
     *
     * @param string $route The route to redirect doe
     * @param int $code Default `301`, the code to respond with
     */
    public function redirect(string $route, int $code = 301): Response
    {
        return $this->response
                    ->withHeader('Location', $route)
                    ->withStatus($code);
    }

    /**
     * @param string $html The raw Twig HTML to render
     * @param array $data The data to injext into the HTML
     * @return Response
     */
    public function renderInline(string $html, array $data = []): Response
    {
        $renderedTemplate = $this->view->fetchFromString($html, $data);
        $this->response->getBody()->write($renderedTemplate);
        return $this->response->withHeader('Content-Type', 'text/html');
    }

    /**
     * @param array|object $data The data to encode as JSON
     * @return Response
     */
    public function json(array|object $data): Response
    {
        $encodedData = json_encode($data);
        $this->response->getBody()->write($encodedData);
        return $this->response->withHeader('Content-Type', 'application/json');
    }
}