<?php

/**
 * LolaCore — Remote Execution Endpoint
 *
 * @package LolaCore
 * @license GPL-2.0-or-later
 */

declare(strict_types=1);

namespace LolaCore\Hub;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use WP_Error;
use WP_REST_Request;
use WP_REST_Response;

/**
 * REST channel for LolaHub master remote execution (sprint C1).
 *
 * Route: POST /wp-json/lolacore/v1/hub/execute
 * Auth:  WordPress Application Password of the paired master (DEC-1).
 *
 * Payload: { ability, params, request_id, dry_run?, expected_fingerprint? }
 */
final class RemoteExecutionEndpoint
{
    public function __construct(
        private ?PairingRegistrar $pairing = null,
        private ?RemoteExecutionService $service = null,
    ) {
        $this->pairing = $pairing ?? new PairingRegistrar();
        $this->service = $service ?? new RemoteExecutionService($this->pairing);
    }

    public function register(): void
    {
        \add_action('rest_api_init', [$this, 'registerRoute']);
    }

    public function registerRoute(): void
    {
        \register_rest_route('lolacore/v1', '/hub/execute', [
            'methods'             => \WP_REST_Server::CREATABLE,
            'callback'            => [$this, 'handle'],
            'permission_callback' => [$this, 'validatePermission'],
            'args'                => [
                'ability' => [
                    'required'          => true,
                    'type'              => 'string',
                    'sanitize_callback' => 'sanitize_text_field',
                ],
                'params' => [
                    'required' => false,
                    'type'     => 'object',
                    'default'  => [],
                ],
                'request_id' => [
                    'required'          => true,
                    'type'              => 'string',
                    'sanitize_callback' => 'sanitize_text_field',
                ],
                'dry_run' => [
                    'required' => false,
                    'type'     => 'boolean',
                    'default'  => false,
                ],
                'expected_fingerprint' => [
                    'required'          => false,
                    'type'              => 'string',
                    'sanitize_callback' => 'sanitize_text_field',
                ],
            ],
        ]);
    }

    public function validatePermission(WP_REST_Request $request): bool|WP_Error
    {
        unset($request);

        if (!function_exists('is_user_logged_in') || !\is_user_logged_in()) {
            return new WP_Error(
                'lolacore_hub_unauthorized',
                __('Authentication required. Use the paired LolaHub Application Password.', 'lolacore'),
                ['status' => 401]
            );
        }

        if (!$this->pairing->isAuthenticatedMaster()) {
            return new WP_Error(
                'lolacore_hub_forbidden',
                __('Only the paired LolaHub master may execute remote abilities on this site.', 'lolacore'),
                ['status' => 403]
            );
        }

        return true;
    }

    public function handle(WP_REST_Request $request): WP_REST_Response
    {
        $ability = (string) $request->get_param('ability');
        $params  = $request->get_param('params');
        if (!is_array($params)) {
            $params = [];
        }

        $requestId = (string) $request->get_param('request_id');
        $dryRun    = (bool) $request->get_param('dry_run');
        $expected  = $request->get_param('expected_fingerprint');
        $expected  = is_string($expected) && $expected !== '' ? $expected : null;

        $result = $this->service->execute($ability, $params, $requestId, $dryRun, $expected);

        if ($result instanceof WP_Error) {
            $status = (int) ($result->get_error_data()['status'] ?? 400);

            return new WP_REST_Response([
                'request_id'    => $requestId,
                'ability'       => $ability,
                'status'        => 'error',
                'error_message' => $result->get_error_message(),
                'reversible'    => false,
            ], $status);
        }

        $httpStatus = ($result['status'] ?? 'ok') === 'drifted' ? 409 : 200;

        return new WP_REST_Response($result, $httpStatus);
    }
}
