<?php
/**
 * UI Preferences REST Controller.
 *
 * Persists per-user UI toggles in user_meta. Currently exposes the F17
 * provider-cost chip's optional USD estimate flag, used by ProviderCostChip.
 *
 * @package AIPT\Api\UiPrefs
 * @since   2.6.1
 */

declare(strict_types=1);

namespace AIPT\Api\UiPrefs;

defined( 'ABSPATH' ) || exit;

/**
 * Thin REST controller for per-user UI preference toggles.
 *
 * @since 2.6.1
 */
final class UiPrefsController {

	private const NAMESPACE = 'aipt/v1';
	private const ROUTE     = '/ui-prefs/show-provider-cost-usd';
	private const META_KEY  = 'aipt_show_provider_cost_usd';

	/**
	 * WordPress function callbacks (injection point for tests).
	 *
	 * @since 2.6.1
	 * @var array<string, callable>
	 */
	private $callbacks;

	/**
	 * Constructor.
	 *
	 * @since 2.6.1
	 *
	 * @param array<string, callable> $callbacks Optional WordPress function overrides.
	 */
	public function __construct( array $callbacks = array() ) {
		$this->callbacks = array_replace(
			array(
				'get_user_meta'         => 'get_user_meta',
				'update_user_meta'      => 'update_user_meta',
				'current_user_can'      => 'current_user_can',
				'rest_sanitize_boolean' => 'rest_sanitize_boolean',
				'get_current_user_id'   => 'get_current_user_id',
				'wp_verify_nonce'       => 'wp_verify_nonce',
			),
			$callbacks
		);
	}

	/**
	 * Register REST API routes.
	 *
	 * @since 2.6.1
	 *
	 * @return void
	 */
	public function register_routes(): void {
		\register_rest_route(
			self::NAMESPACE,
			self::ROUTE,
			array(
				array(
					'methods'             => \WP_REST_Server::READABLE,
					'callback'            => array( $this, 'get_show_provider_cost_usd' ),
					'permission_callback' => array( $this, 'check_permissions' ),
				),
				array(
					'methods'             => \WP_REST_Server::CREATABLE,
					'callback'            => array( $this, 'post_show_provider_cost_usd' ),
					'permission_callback' => array( $this, 'check_permissions_write' ),
				),
			)
		);
	}

	/**
	 * Handle POST aipt/v1/ui-prefs/show-provider-cost-usd.
	 *
	 * @since 2.6.1
	 *
	 * @param \WP_REST_Request $request Incoming REST request.
	 * @return \WP_REST_Response|\WP_Error Response or error.
	 */
	public function post_show_provider_cost_usd( \WP_REST_Request $request ) {
		$raw = $request->get_param( 'enabled' );

		if ( null === $raw ) {
			return new \WP_Error(
				'missing_enabled',
				\__( 'Missing required parameter: enabled.', 'ai-product-tools' ),
				array( 'status' => 400 )
			);
		}

		$enabled = (bool) call_user_func( $this->callbacks['rest_sanitize_boolean'], $raw );
		$user_id = (int) call_user_func( $this->callbacks['get_current_user_id'] );

		call_user_func(
			$this->callbacks['update_user_meta'],
			$user_id,
			self::META_KEY,
			$enabled ? '1' : '0'
		);

		return new \WP_REST_Response(
			array( 'enabled' => $enabled ),
			200
		);
	}

	/**
	 * Handle GET aipt/v1/ui-prefs/show-provider-cost-usd.
	 *
	 * @since 2.6.1
	 *
	 * @param \WP_REST_Request $request Incoming REST request.
	 * @return \WP_REST_Response|\WP_Error Response or error.
	 */
	public function get_show_provider_cost_usd( \WP_REST_Request $request ) {
		unset( $request );

		$user_id = (int) call_user_func( $this->callbacks['get_current_user_id'] );
		$value   = call_user_func(
			$this->callbacks['get_user_meta'],
			$user_id,
			self::META_KEY,
			true
		);

		$enabled = ( '1' === (string) $value );

		return new \WP_REST_Response(
			array( 'enabled' => $enabled ),
			200
		);
	}

	/**
	 * Permission callback: edit_products + REST nonce.
	 *
	 * @since 2.6.1
	 *
	 * @param \WP_REST_Request $request Incoming REST request.
	 * @return bool|\WP_Error True when both `edit_products` capability and nonce pass.
	 *                       \WP_Error with status 403 when the capability check fails
	 *                       or {@see self::verify_nonce()} rejects an invalid/missing
	 *                       X-WP-Nonce.
	 */
	public function check_permissions( \WP_REST_Request $request ) {
		if ( ! call_user_func( $this->callbacks['current_user_can'], 'edit_products' ) ) {
			return new \WP_Error(
				'rest_forbidden',
				\__( 'You do not have permission to access this endpoint.', 'ai-product-tools' ),
				array( 'status' => 403 )
			);
		}

		if ( ! $this->verify_nonce( $request ) ) {
			return new \WP_Error(
				'rest_forbidden',
				\__( 'Invalid or missing REST nonce.', 'ai-product-tools' ),
				array( 'status' => 403 )
			);
		}

		return true;
	}

	/**
	 * Write-capable permission callback.
	 *
	 * @since 2.6.1
	 *
	 * @param \WP_REST_Request $request Incoming REST request.
	 * @return bool|\WP_Error True when both `edit_products` capability and nonce pass.
	 */
	public function check_permissions_write( \WP_REST_Request $request ) {
		return $this->check_permissions( $request );
	}

	/**
	 * Verify REST nonce.
	 *
	 * @since 2.6.1
	 *
	 * @param \WP_REST_Request $request Incoming REST request.
	 * @return bool True when the nonce is valid.
	 */
	private function verify_nonce( \WP_REST_Request $request ): bool {
		$nonce = $request->get_header( 'X-WP-Nonce' );

		if ( empty( $nonce ) ) {
			$nonce = $request->get_param( '_wpnonce' );
		}

		return (bool) call_user_func( $this->callbacks['wp_verify_nonce'], $nonce, 'wp_rest' );
	}
}
