<?php

namespace XF\Api\Mvc;

use XF\Http\Request;
use XF\Mvc\ParameterBag;

use function intval, strlen;

class Router extends \XF\Mvc\Router
{
	protected $defaultAction = '';

	public function routePreProcessApiPrefix(\XF\Mvc\Router $router, $path, RouteMatch $match, ?Request $request = null)
	{
		if (preg_match('#^api(?:/|$)(.*)$#i', $path, $matches))
		{
			$path = $matches[1];
			$match->setPathRewrite($path);
		}
		return $match;
	}

	public function routePreProcessApiVersion(\XF\Mvc\Router $router, $path, RouteMatch $match, ?Request $request = null)
	{
		if (preg_match('#^v(\d+)(?:/|$)(.*)$#i', $path, $matches))
		{
			$version = intval($matches[1]);
			$path = $matches[2];

			$match->setVersion($version);
			$match->setPathRewrite($path);
		}
		return $match;
	}

	public function routePreProcessApiResponseType(\XF\Mvc\Router $router, $path, RouteMatch $match, ?Request $request = null)
	{
		$match->setResponseType('api');
		return $match;
	}

	public function routeToController($path, ?Request $request = null)
	{
		$match = parent::routeToController($path, $request);

		// By convention, the API will not allow "prefix/1/act/ion" to match the action as "act/ion" to prevent
		// inconsistent URLs. If this format is needed, define an explicit match for "prefix/1/act/" as needed (with
		// an action of "ion").
		$action = $match->getAction();
		if (strpos($action, '/') !== false)
		{
			$match->setController('');
			$match->setAction('');
		}
		else
		{
			// Prepend the request method to the action. This is done here instead of in dispatching as doing it there
			// makes it difficult to reroute to controllers (such as for errors).

			if ($request)
			{
				$requestMethod = preg_replace('#[^a-z]#', '', strtolower($request->getRequestMethod()));
				if (!$requestMethod)
				{
					$requestMethod = 'get';
				}
			}
			else
			{
				$requestMethod = 'get';
			}

			$match->setAction($requestMethod . $match->getAction());
			if ($match instanceof RouteMatch)
			{
				$match->setRequestMethod($requestMethod);
			}
		}

		return $match;
	}

	protected function manipulateLinkPathInternal($prefix, &$path, &$data, array &$parameters)
	{
		// This applies the API route convention that a "-" path identifies automatic switching to routes that take
		// a param without us having to explicitly call "xyz/-" when building a link.
		// This also assumes that the route is define properly, making the parameter part of it required in the matching
		// process.

		$prefixRoutes = $this->routes[$prefix];
		if (isset($prefixRoutes['-']) && $data && (!strlen($path) || $path[0] != '-'))
		{
			$path = '-/' . $path;
		}
	}

	/**
	 * @param string $controller
	 * @param string $action
	 * @param array|ParameterBag $params
	 * @param string $responseType
	 *
	 * @return RouteMatch
	 */
	public function getNewRouteMatch($controller = '', $action = '', $params = [], $responseType = 'html')
	{
		return new RouteMatch($controller, $action, $params, $responseType);
	}
}
