<?php declare(strict_types=1);
/**
 * @package messenger
 * @author kputyr
 * @date 20.04.2026
 */

namespace IMATHUZH\Messenger\Rest;

/**
 * A class for processing REST requests and routing them to the appropriate handlers.
 */
class RestProcessor
{
    protected string $baseUrl;

    public function __construct(
        string $baseUrl,
        protected array $actions
    ) {
        $this->baseUrl = preg_replace('/\/+/', '/', "/$baseUrl/");
    }

    /**
     * Finds the appropriate handler for the given request by matching the request path
     * against the registered actions. The best match is the longest slug that is a prefix
     * of the request path and has a handler for the request method.
     * 
     * @param array $actions
     * @param RequestInterface $request
     * @return string
     */
    protected function findHandler(array $actions, ServerRequestInterface $request): string
    {
        $path = substr($request->getPath(), \strlen($this->baseUrl)-1);
        $method = $request->getMethod();
        if ($method === 'HEAD') $method = 'GET';

        // Find the best handler: the longest matching slug
        $match = '';
        foreach($actions as $slug => $action) {
            if (\strlen($slug) > \strlen($match) && \str_starts_with($path, $slug)
             && \is_array($action) && isset($action[$method])) {
                $match = $slug;
            }
        }
        return $match;
    }

    /**
     * Outputs a JSON response with the given HTTP status code and response body.
     * 
     * @param int $httpCode
     * @param array $response
     * @return void
     */
    protected function output(int $httpCode, array $response): void
    {
        http_response_code($httpCode);
        header('Content-Type: application/json');
        echo json_encode($response);
    }

    protected function handleException(\Throwable $e): void
    {
        $this->output(500, [ 'success' => false, 'reason' => $e->getMessage() ]);
    }

    /**
     * The main method for processing a request. It finds the appropriate handler for the request
     * and calls it, then outputs the response. If any error occurs during processing, it outputs
     * an error response with the appropriate HTTP status code and error message.
     * 
     * @param ServerRequestInterface $request
     * @return void
     */
    public function process(ServerRequestInterface $request): void
    {
        try {
            $path = $request->getPath();
            if (!str_starts_with($path, $this->baseUrl)) {
                $this->output(405, [ 'success' => false, 'reason' => 'invalid endpoint' ]);
                return;
            }
            
            $slug = $this->findHandler($this->actions, $request);
            if (!$slug) {
                $this->output(404, [ 'success' => false, 'reason' => 'not found' ]);
                return;
            }

            $method = $request->getMethod();
            if ($method === 'HEAD') $method = 'GET';
            $handler = $this->actions[$slug][$method];
            if (!is_callable($handler)) {
                $this->output(405, [ 'success' => false, 'reason' => 'unsupported request method' ]);
                return;
            }

            // Extract arguments from the path
            // - keep only the part after the baseUrl and matched slug
            // - skip the trailing slash
            $request->injectParams(explode("/", substr(
                $path,
                \strlen($this->baseUrl) + \strlen($slug) - 1,
                -1
            )));
            
            // Call the handler and output the response
            $this->output(200, [
                'success' => true,
                'data' => $handler($request)
            ]);

        } catch(\RuntimeException $e) {
            // Invalid request
            $this->output($e->getCode(), [ 'success' => false, 'reason' => $e->getMessage() ]);
        } catch(\Throwable $e) {
            // The request has failed due to server error
            $this->handleException($e);
        }
    }
}