<?php
/**
 * UI Variant Health REST Controller.
 *
 * @package AIPT\Api\UiVariant
 * @since   2.6.1
 */

declare(strict_types=1);

namespace AIPT\Api\UiVariant;

defined( 'ABSPATH' ) || exit;

use AIPT\Core\Services\UiVariantService;
use AIPT\Core\Telemetry\VariantTelemetryReporter;

/**
 * Exposes a health payload for UI variant rollout state.
 *
 * @since 2.6.1
 */
final class UiVariantHealthController {

	private const NAMESPACE        = 'aipt/v1';
	private const ROUTE_HEALTH     = '/ui-variant/health';
	private const ROUTE_PREFERENCE = '/ui-variant/preference';

	private const VALID_VARIANTS     = array( 'legacy', 'next', 'auto' );
	private const VALID_CHANGE_KINDS = array( 'opt_in', 'switch_back' );

	/**
	 * UI variant service.
	 *
	 * @since 2.6.1
	 * @var UiVariantService
	 */
	private $service;

	/**
	 * Constructor.
	 *
	 * @since 2.6.1
	 *
	 * @param UiVariantService $service Injected variant service.
	 */
	public function __construct( UiVariantService $service ) {
		$this->service = $service;
	}

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

		\register_rest_route(
			self::NAMESPACE,
			self::ROUTE_PREFERENCE,
			array(
				'methods'             => \WP_REST_Server::CREATABLE,
				'callback'            => array( $this, 'post_preference' ),
				'permission_callback' => array( $this, 'check_permissions_write' ),
			)
		);
	}

	/**
	 * Handle POST aipt/v1/ui-variant/preference.
	 *
	 * Persists the per-user variant choice + increments the matching rollout
	 * metric (opt_in / switch_back) + touches the last-used timestamp.
	 *
	 * @since 2.6.1
	 *
	 * @param \WP_REST_Request $request Incoming REST request.
	 * @return \WP_REST_Response|\WP_Error Updated health payload, or error.
	 */
	public function post_preference( \WP_REST_Request $request ) {
		$variant       = $request->get_param( 'variant' );
		$change_kind   = $request->get_param( 'change_kind' );
		$dismiss_modal = (bool) $request->get_param( 'dismiss_modal' );

		$variant_supplied     = is_string( $variant );
		$change_kind_supplied = is_string( $change_kind );

		if ( $dismiss_modal && ! $variant_supplied && ! $change_kind_supplied ) {
			$user_id            = (int) \get_current_user_id();
			$snoozed_until      = $this->service->snoozeModal( $user_id );

			return new \WP_REST_Response(
				array(
					'user_variant'        => $this->service->getUserVariant( $user_id ),
					'site_variant'        => $this->service->getSiteVariant(),
					'resolved'            => $this->service->resolveForUser( $user_id ),
					'modal_snoozed_until' => $snoozed_until,
					'metrics'             => $this->service->getMetrics(),
				),
				200
			);
		}

		if ( ! $variant_supplied || ! in_array( $variant, self::VALID_VARIANTS, true ) ) {
			return new \WP_Error(
				'invalid_variant',
				\__( 'variant must be one of: legacy, next, auto.', 'ai-product-tools' ),
				array( 'status' => 400 )
			);
		}

		if ( ! $change_kind_supplied || ! in_array( $change_kind, self::VALID_CHANGE_KINDS, true ) ) {
			return new \WP_Error(
				'invalid_change_kind',
				\__( 'change_kind must be one of: opt_in, switch_back.', 'ai-product-tools' ),
				array( 'status' => 400 )
			);
		}

		$user_id = (int) \get_current_user_id();

		$this->service->setUserVariant( $user_id, $variant );

		if ( 'opt_in' === $change_kind ) {
			$this->service->incrementOptIn();
		} else {
			$this->service->incrementSwitchBack();

			$this->service->snoozeModal( $user_id );
		}

		$this->service->touchLastUsed();

		\add_action(
			'shutdown',
			static function () use ( $user_id ): void {
				VariantTelemetryReporter::reportVariantChanged( $user_id );
			},
			100
		);

		return new \WP_REST_Response(
			array(
				'user_variant' => $this->service->getUserVariant( $user_id ),
				'site_variant' => $this->service->getSiteVariant(),
				'resolved'     => $this->service->resolveForUser( $user_id ),
				'metrics'      => $this->service->getMetrics(),
			),
			200
		);
	}

	/**
	 * Write-capable permission for the preference toggle.
	 *
	 * Uses edit_products so shop_manager and editor roles that access the
	 * generator pages (manage_woocommerce-gated) can switch their variant
	 * without needing manage_options.
	 *
	 * @since 2.6.1
	 *
	 * @param \WP_REST_Request $request Incoming REST request.
	 * @return bool|\WP_Error True when capability + nonce both pass. \WP_Error with
	 *                       status 403 when `edit_products` capability is missing or
	 *                       {@see self::verify_nonce()} rejects an invalid/missing
	 *                       X-WP-Nonce.
	 */
	public function check_permissions_write( \WP_REST_Request $request ) {
		if ( ! \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;
	}

	/**
	 * Handle GET aipt/v1/ui-variant/health.
	 *
	 * @since 2.6.1
	 *
	 * @param \WP_REST_Request $request Incoming REST request.
	 * @return \WP_REST_Response Health payload.
	 */
	public function get_health( \WP_REST_Request $request ): \WP_REST_Response {
		unset( $request );

		$user_id = (int) \get_current_user_id();

		return new \WP_REST_Response(
			array(
				'user_variant' => $this->service->getUserVariant( $user_id ),
				'site_variant' => $this->service->getSiteVariant(),
				'resolved'     => $this->service->resolveForUser( $user_id ),
				'metrics'      => $this->service->getMetrics(),
			),
			200
		);
	}

	/**
	 * Administrator-only reads with nonce verification.
	 *
	 * @since 2.6.1
	 *
	 * @param \WP_REST_Request $request Incoming REST request.
	 * @return bool|\WP_Error True when both `manage_options` capability and the 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 ( ! \current_user_can( 'manage_options' ) ) {
			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;
	}

	/**
	 * 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) \wp_verify_nonce( $nonce, 'wp_rest' );
	}
}
