<?php

namespace WordpressModels\DependencyInjection\HookAttributes;

use Symfony\Component\HttpFoundation\Exception\BadRequestException;
use Symfony\Component\HttpFoundation\Request;

/**
 * Invokable class that serves as a wrapper for an ajax action used at container compile time.
 *
 * This class is used to wrap the handler of an ajax action to add nonce verification and error handling.
 * The handler is called with a {@see Request} as the first parameter.
 * The handler must return any json serializable value, as responses are sent using {@see wp_send_json_success}
 * and {@see wp_send_json_error}.
 */
class AjaxActionWrapper
{

    private $handler;

    /**
     * @param callable $handler -- the handler to call, must accept a {@see Request} as the first parameter and return any json serializable value.
     * @param string|null $nonceId -- the nonce id to verify, if null, no nonce verification is done.
     * @param string $nonceKey -- the nonce key to verify, defaults to 'nonce'.
     */
    public function __construct(
        callable                 $handler,
        private readonly ?string $nonceId,
        private readonly string  $nonceKey,
    )
    {
        $this->handler = $handler;
    }

    public function __invoke()
    {
        // create a request from the globals
        $request = Request::createFromGlobals();

        $bag = $request->isMethod('GET') ? $request->query : $request->request;

        // check if the nonce is configured and is valid
        if ($this->nonceId && (!$bag->has($this->nonceKey) || !wp_verify_nonce($bag->get($this->nonceKey), $this->nonceId))) {
            // send a 403
            wp_send_json_error('Invalid nonce', \WP_Http::FORBIDDEN);
        }

        try {
            // call the handler
            $response = call_user_func($this->handler, $request);

            // check if response is not void
            if ($response === null) {
                return;
            }

            // send the response
            wp_send_json_success($response);
        } catch (BadRequestException $e) {
            // send a 400
            wp_send_json_error($e->getMessage(), \WP_Http::BAD_REQUEST);
        } catch (\HttpException $e) {
            // check whether the exception has a valid http status code
            $code = $e->getCode();
            if ($code < 100 || $code >= 600) {
                // send a 500
                $code = \WP_Http::INTERNAL_SERVER_ERROR;
            }

            // send the specified status code
            wp_send_json_error($e->getMessage(), $code);
        } catch (\Throwable $e) {
            // send a 500
            wp_send_json_error($e->getMessage(), \WP_Http::INTERNAL_SERVER_ERROR);
        }
    }

}