<?php

namespace Nest\Http\Router;

use DOMDocument;
use Nest\Application;
use Nest\Http\ContentType;
use Nest\Http\Header;
use Nest\Http\Renderer\Renderer;
use Nest\Http\StatusCode;
use Nyholm\Psr7\Response;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\StreamInterface;
use Stringable;

/**
 * An abstract HTTP route controller.
 */
abstract class Controller
{
	/**
	 * @param Application $app The application.
	 */
	public function __construct(protected Application $app)
	{}

	/**
	 * Get current request.
	 * @return ServerRequestInterface The request instance.
	 */
	public function getRequest(): ServerRequestInterface
	{
		return $this->app->http()->getRequest();
	}

	/**
	 * Create a new response with the given status code and data.
	 * @param StatusCode $statusCode The status code to respond.
	 * @param string|null $contentType The content type of the response.
	 * @param string|resource|StreamInterface|null $body The body to respond.
	 * @return ResponseInterface The new response.
	 */
	public function newResponse(StatusCode $statusCode = StatusCode::OK, ?string $contentType = null, mixed $body = null): ResponseInterface
	{
		// Initialize headers.
		$headers = [];
		if (!empty($contentType)) $headers[Header::ContentType->value] = $contentType;

		return new Response(
			// Pass the status code.
			status: $statusCode->value,
			// Pass headers.
			headers: $headers,
			// Pass the raw body.
			body: $body,
		);
	}

	/**
	 * Build a text response.
	 * @param string $value The text value to pass.
	 * @param StatusCode $statusCode The status code to send back. OK by default.
	 * @return ResponseInterface Built response.
	 */
	public function text(string $value, StatusCode $statusCode = StatusCode::OK): ResponseInterface
	{
		return $this->newResponse($statusCode, ContentType::Text->value, $value);
	}

	/**
	 * Build a JSON response.
	 * @param mixed $value The value to serialize.
	 * @param StatusCode $statusCode The status code to send back. OK by default.
	 * @return ResponseInterface Built response.
	 */
	public function json(mixed $value, StatusCode $statusCode = StatusCode::OK): ResponseInterface
	{
		return $this->newResponse($statusCode, ContentType::Json->value, json_encode($value));
	}

	/**
	 * Build an XML response.
	 * @param DOMDocument $value The XML value to serialize.
	 * @param StatusCode $statusCode The status code to send back. OK by default.
	 * @return ResponseInterface Built response.
	 */
	public function xml(DOMDocument $value, StatusCode $statusCode = StatusCode::OK): ResponseInterface
	{
		return $this->newResponse($statusCode, ContentType::Json->value, $value->saveXML());
	}

	/**
	 * Build a rendered response.
	 * @param Renderer $renderer The renderer instance to use.
	 * @param object $arguments Arguments to use when rendering.
	 * @param StatusCode $statusCode The status code to send back. OK by default.
	 * @param string|null $contentType The content type of the response.
	 * @return ResponseInterface Built response.
	 */
	public function render(Renderer $renderer, object $arguments = new \stdClass(), StatusCode $statusCode = StatusCode::OK, ?string $contentType = null): ResponseInterface
	{
		return $this->newResponse($statusCode, $contentType ?? null, $renderer->render($arguments));
	}
}