<?php
/**
 * Abstract REST Controller Base Class
 *
 * Provides base functionality for all REST API controllers.
 * Implements common patterns for route registration, permission
 * checking, and response formatting.
 *
 * @package     WPUltimateSecurity\UltimateSecurity\Presentation\API
 * @since       2.0.0
 * @author      Ultimate Security Team
 * @license     GPL-2.0-or-later
 */

declare(strict_types=1);

namespace WPUltimateSecurity\UltimateSecurity\Presentation\API;

use WP_Error;
use WP_REST_Request;
use WP_REST_Response;
use WPUltimateSecurity\UltimateSecurity\Infrastructure\Security\Capabilities;

/**
 * Abstract base class for REST API controllers.
 *
 * Provides common functionality including:
 * - Standardized route registration
 * - Permission checking helpers
 * - Response formatting
 * - Error handling
 *
 * Example usage:
 * ```php
 * class SettingsController extends AbstractRestController {
 *     protected string $resource = 'settings';
 *
 *     public function registerRoutes(): void {
 *         $this->registerRoute('/', [
 *             'methods' => 'GET',
 *             'callback' => [$this, 'getSettings'],
 *             'permission_callback' => [$this, 'canViewSettings'],
 *         ]);
 *     }
 *
 *     public function getSettings(WP_REST_Request $request): WP_REST_Response {
 *         return $this->success(['settings' => $this->loadSettings()]);
 *     }
 * }
 * ```
 *
 * @since 2.0.0
 */
abstract class AbstractRestController
{
    /**
     * The REST API namespace.
     *
     * @var string
     */
    protected string $namespace = 'ultimate-security/v1';

    /**
     * The resource name for this controller.
     *
     * Child classes should override this.
     *
     * @var string
     */
    protected string $resource = '';

    /**
     * Register the controller's routes.
     *
     * Child classes must implement this to register their routes.
     *
     * @since 2.0.0
     *
     * @return void
     */
    abstract public function registerRoutes(): void;

    /**
     * Register a single route.
     *
     * Helper method to simplify route registration.
     *
     * @since 2.0.0
     *
     * @param string $route Route path (appended to resource).
     * @param array  $args  Route arguments.
     *
     * @return bool True on success.
     */
    protected function registerRoute(string $route, array $args): bool
    {
        $path = $this->resource;
        if ($route !== '/' && $route !== '') {
            $path .= '/' . ltrim($route, '/');
        }

        // Ensure permission callback exists
        if (!isset($args['permission_callback'])) {
            $args['permission_callback'] = [$this, 'checkAdminPermission'];
        }

        return register_rest_route($this->namespace, $path, $args);
    }

    /**
     * Register multiple methods for a route.
     *
     * @since 2.0.0
     *
     * @param string $route   Route path.
     * @param array  $methods Array of method configurations.
     *
     * @return bool True on success.
     */
    protected function registerRouteWithMethods(string $route, array $methods): bool
    {
        $path = $this->resource;
        if ($route !== '/' && $route !== '') {
            $path .= '/' . ltrim($route, '/');
        }

        // Ensure permission callbacks exist
        foreach ($methods as &$method) {
            if (!isset($method['permission_callback'])) {
                $method['permission_callback'] = [$this, 'checkAdminPermission'];
            }
        }

        return register_rest_route($this->namespace, $path, $methods);
    }

    /**
     * Check if current user has admin permission.
     *
     * Default permission check requiring 'manage_options' capability.
     *
     * @since 2.0.0
     *
     * @param WP_REST_Request $request The request object.
     *
     * @return bool|WP_Error True if permitted, WP_Error otherwise.
     */
    public function checkAdminPermission(WP_REST_Request $request): bool|WP_Error
    {
        if (!current_user_can('manage_options')) {
            return $this->permissionDenied();
        }

        return true;
    }

    /**
     * Check if user has a specific capability.
     *
     * @since 2.0.0
     *
     * @param string $capability The capability to check.
     *
     * @return bool|WP_Error True if has capability, WP_Error otherwise.
     */
    protected function checkCapability(string $capability): bool|WP_Error
    {
        if (!current_user_can($capability)) {
            return $this->permissionDenied();
        }

        return true;
    }

    /**
     * Create a permission callback for a specific capability.
     *
     * @since 2.0.0
     *
     * @param string $capability The required capability.
     *
     * @return callable Permission callback function.
     */
    protected function requireCapability(string $capability): callable
    {
        return function (WP_REST_Request $request) use ($capability): bool|WP_Error {
            return $this->checkCapability($capability);
        };
    }

    /**
     * Return a success response.
     *
     * @since 2.0.0
     *
     * @param mixed  $data    Response data.
     * @param string $message Optional success message.
     * @param int    $status  HTTP status code.
     *
     * @return WP_REST_Response
     */
    protected function success(mixed $data = null, string $message = '', int $status = 200): WP_REST_Response
    {
        $response = [
            'success' => true,
        ];

        if ($data !== null) {
            $response['data'] = $data;
        }

        if ($message !== '') {
            $response['message'] = $message;
        }

        return new WP_REST_Response($response, $status);
    }

    /**
     * Return an error response.
     *
     * @since 2.0.0
     *
     * @param string $code    Error code.
     * @param string $message Error message.
     * @param int    $status  HTTP status code.
     * @param array  $data    Additional error data.
     *
     * @return WP_Error
     */
    protected function error(
        string $code,
        string $message,
        int $status = 400,
        array $data = []
    ): WP_Error {
        $errorData = array_merge(['status' => $status], $data);
        return new WP_Error($code, $message, $errorData);
    }

    /**
     * Return a permission denied error.
     *
     * @since 2.0.0
     *
     * @param string $message Optional custom message.
     *
     * @return WP_Error
     */
    protected function permissionDenied(string $message = ''): WP_Error
    {
        if ($message === '') {
            $message = __('You do not have permission to perform this action.', 'ultimate-security');
        }

        return $this->error(
            'ultimate_security_permission_denied',
            $message,
            403
        );
    }

    /**
     * Return a not found error.
     *
     * @since 2.0.0
     *
     * @param string $message Optional custom message.
     *
     * @return WP_Error
     */
    protected function notFound(string $message = ''): WP_Error
    {
        if ($message === '') {
            $message = __('The requested resource was not found.', 'ultimate-security');
        }

        return $this->error(
            'ultimate_security_not_found',
            $message,
            404
        );
    }

    /**
     * Return a validation error.
     *
     * @since 2.0.0
     *
     * @param string $message Error message.
     * @param array  $errors  Field-level errors.
     *
     * @return WP_Error
     */
    protected function validationError(string $message, array $errors = []): WP_Error
    {
        return $this->error(
            'ultimate_security_validation_error',
            $message,
            422,
            ['errors' => $errors]
        );
    }

    /**
     * Return a Pro required error.
     *
     * @since 2.0.0
     *
     * @param string $feature The feature requiring Pro.
     *
     * @return WP_Error
     */
    protected function proRequired(string $feature = ''): WP_Error
    {
        $message = $feature !== ''
            ? sprintf(
                /* translators: %s: feature name */
                __('%s requires Ultimate Security Pro.', 'ultimate-security'),
                $feature
            )
            : __('This feature requires Ultimate Security Pro.', 'ultimate-security');

        return $this->error(
            'ultimate_security_pro_required',
            $message,
            403,
            ['feature' => $feature]
        );
    }

    /**
     * Sanitize a string parameter.
     *
     * @since 2.0.0
     *
     * @param WP_REST_Request $request The request object.
     * @param string          $key     Parameter key.
     * @param string          $default Default value.
     *
     * @return string Sanitized value.
     */
    protected function getString(WP_REST_Request $request, string $key, string $default = ''): string
    {
        $value = $request->get_param($key);
        return is_string($value) ? sanitize_text_field($value) : $default;
    }

    /**
     * Get an integer parameter.
     *
     * @since 2.0.0
     *
     * @param WP_REST_Request $request The request object.
     * @param string          $key     Parameter key.
     * @param int             $default Default value.
     *
     * @return int The integer value.
     */
    protected function getInt(WP_REST_Request $request, string $key, int $default = 0): int
    {
        $value = $request->get_param($key);
        return is_numeric($value) ? (int) $value : $default;
    }

    /**
     * Get a boolean parameter.
     *
     * @since 2.0.0
     *
     * @param WP_REST_Request $request The request object.
     * @param string          $key     Parameter key.
     * @param bool            $default Default value.
     *
     * @return bool The boolean value.
     */
    protected function getBool(WP_REST_Request $request, string $key, bool $default = false): bool
    {
        $value = $request->get_param($key);

        if ($value === null) {
            return $default;
        }

        return filter_var($value, FILTER_VALIDATE_BOOLEAN);
    }

    /**
     * Get an array parameter.
     *
     * @since 2.0.0
     *
     * @param WP_REST_Request $request The request object.
     * @param string          $key     Parameter key.
     * @param array           $default Default value.
     *
     * @return array The array value.
     */
    protected function getArray(WP_REST_Request $request, string $key, array $default = []): array
    {
        $value = $request->get_param($key);
        return is_array($value) ? $value : $default;
    }

    /**
     * Get pagination parameters.
     *
     * @since 2.0.0
     *
     * @param WP_REST_Request $request The request object.
     *
     * @return array{page: int, per_page: int, offset: int}
     */
    protected function getPagination(WP_REST_Request $request): array
    {
        $page = max(1, $this->getInt($request, 'page', 1));
        $perPage = min(100, max(1, $this->getInt($request, 'per_page', 20)));
        $offset = ($page - 1) * $perPage;

        return [
            'page' => $page,
            'per_page' => $perPage,
            'offset' => $offset,
        ];
    }

    /**
     * Add pagination headers to response.
     *
     * @since 2.0.0
     *
     * @param WP_REST_Response $response   The response object.
     * @param int              $total      Total items.
     * @param int              $page       Current page.
     * @param int              $perPage    Items per page.
     *
     * @return WP_REST_Response
     */
    protected function withPaginationHeaders(
        WP_REST_Response $response,
        int $total,
        int $page,
        int $perPage
    ): WP_REST_Response {
        $totalPages = (int) ceil($total / $perPage);

        $response->header('X-WP-Total', $total);
        $response->header('X-WP-TotalPages', $totalPages);
        $response->header('X-WP-Page', $page);
        $response->header('X-WP-PerPage', $perPage);

        return $response;
    }
}
