<?php
/**
 * Central monthly reservation quota client.
 *
 * @package FoodOrd
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Class Foodord_Reservation_Quota
 */
final class Foodord_Reservation_Quota {
	private const ENDPOINT_BASE    = 'https://www.foodord.com/wp-json/foodord-cloud/v1/reservation-quota/';
	private const STATUS_CACHE_KEY = 'foodord_reservation_quota_status';
	private const STATUS_CACHE_TTL = 30;
	private const REQUEST_TIMEOUT  = 15;

	/**
	 * Get a non-consuming entitlement snapshot for early UI decisions.
	 *
	 * @param bool $force Bypass the short status cache.
	 * @return array{known:bool,allowed:bool,licensed:bool,limit:int,used:int,remaining:int|null,month:string}
	 */
	public static function get_status( bool $force = false ): array {
		if ( ! $force ) {
			$cached = get_transient( self::STATUS_CACHE_KEY );
			if ( is_array( $cached ) ) {
				return self::normalize_status( $cached );
			}
		}

		$response = self::request( 'status' );
		if ( is_wp_error( $response ) ) {
			return self::unknown_status();
		}

		$status = self::normalize_status(
			array(
				'known'     => true,
				'allowed'   => ! empty( $response['allowed'] ),
				'licensed'  => ! empty( $response['licensed'] ),
				'limit'     => absint( $response['limit'] ?? 0 ),
				'used'      => absint( $response['used'] ?? 0 ),
				'remaining' => isset( $response['remaining'] ) ? absint( $response['remaining'] ) : null,
				'month'     => sanitize_text_field( (string) ( $response['month'] ?? '' ) ),
			)
		);
		set_transient( self::STATUS_CACHE_KEY, $status, self::STATUS_CACHE_TTL );

		return $status;
	}

	/**
	 * Reserve one central permit for a reservation insert attempt.
	 *
	 * @param string $operation_id Idempotent operation identifier.
	 * @return array{permit_token:string,licensed:bool,expires_at:string}|WP_Error
	 */
	public static function authorize( string $operation_id ) {
		$operation_id = strtolower( trim( $operation_id ) );
		if ( ! preg_match( '/^[a-f0-9]{64}$/', $operation_id ) ) {
			return new WP_Error( 'foodord_reservation_quota_operation_invalid', __( 'Unable to authorize this reservation. Please try again.', 'foodord' ) );
		}

		$response = self::request(
			'authorize',
			array(
				'operationId' => $operation_id,
			)
		);
		if ( is_wp_error( $response ) ) {
			return $response;
		}

		$permit_token = isset( $response['permitToken'] ) ? trim( (string) $response['permitToken'] ) : '';
		if ( '' === $permit_token ) {
			return new WP_Error( 'foodord_reservation_quota_invalid_response', __( 'Online reservations are temporarily unavailable. Please try again later.', 'foodord' ) );
		}

		return array(
			'permit_token' => $permit_token,
			'licensed'     => ! empty( $response['licensed'] ),
			'expires_at'   => sanitize_text_field( (string) ( $response['expiresAt'] ?? '' ) ),
		);
	}

	/**
	 * Commit a permit after the local reservation row exists.
	 *
	 * @param string $permit_token  Signed permit token.
	 * @param int    $reservation_id Local reservation ID.
	 * @return true|WP_Error
	 */
	public static function commit( string $permit_token, int $reservation_id ) {
		$permit_token   = trim( $permit_token );
		$reservation_id = absint( $reservation_id );
		if ( '' === $permit_token || $reservation_id <= 0 ) {
			return new WP_Error( 'foodord_reservation_quota_commit_invalid', __( 'Unable to finalize this reservation. Please try again.', 'foodord' ) );
		}

		$response = self::request(
			'commit',
			array(
				'permitToken'          => $permit_token,
				'reservationReference' => self::get_site_reference() . ':' . $reservation_id,
			)
		);
		if ( is_wp_error( $response ) ) {
			return $response;
		}

		if ( empty( $response['success'] ) || 'committed' !== sanitize_key( (string) ( $response['status'] ?? '' ) ) ) {
			return new WP_Error( 'foodord_reservation_quota_commit_failed', __( 'Unable to finalize this reservation. Please try again.', 'foodord' ) );
		}

		self::clear_status_cache();

		return true;
	}

	/**
	 * Release an unused payment-pending permit.
	 *
	 * @param string $permit_token Signed permit token.
	 * @return bool
	 */
	public static function release( string $permit_token ): bool {
		$permit_token = trim( $permit_token );
		if ( '' === $permit_token ) {
			return false;
		}

		$response = self::request(
			'release',
			array(
				'permitToken' => $permit_token,
			)
		);
		if ( is_wp_error( $response ) ) {
			return false;
		}

		self::clear_status_cache();
		$status = sanitize_key( (string) ( $response['status'] ?? '' ) );

		return ! empty( $response['success'] ) && in_array( $status, array( 'released', 'expired' ), true );
	}

	/**
	 * Clear the short early-check cache after state changes.
	 */
	public static function clear_status_cache(): void {
		delete_transient( self::STATUS_CACHE_KEY );
	}

	/**
	 * Customer-safe message used when the central monthly allowance is exhausted.
	 */
	public static function get_customer_limit_message(): string {
		return __( 'Online reservations are currently unavailable. Please contact the restaurant directly.', 'foodord' );
	}

	/**
	 * Send one authenticated quota request to the central license service.
	 *
	 * @param string              $action  Endpoint action.
	 * @param array<string,mixed> $payload Additional request data.
	 * @return array<string,mixed>|WP_Error
	 */
	private static function request( string $action, array $payload = array() ) {
		$endpoint = self::get_endpoint( $action );
		if ( '' === $endpoint ) {
			return new WP_Error( 'foodord_reservation_quota_not_configured', __( 'Online reservations are temporarily unavailable. Please try again later.', 'foodord' ) );
		}

		$config = function_exists( 'foodord_get_license_service_config_values' )
			? foodord_get_license_service_config_values()
			: array( 'api_key' => '' );
		$body   = array_merge(
			array(
				'domain'        => self::get_site_domain(),
				'siteUrl'       => home_url(),
				'purchaseCode'  => trim( (string) get_option( 'foodord_license_key', '' ) ),
				'pluginVersion' => defined( 'FOODORD_VERSION' ) ? FOODORD_VERSION : '',
			),
			$payload
		);
		$args   = array(
			'timeout' => self::REQUEST_TIMEOUT,
			'headers' => array(
				'content-type' => 'application/json',
				'accept'       => 'application/json',
			),
			'body'    => wp_json_encode( $body ),
			'method'  => 'POST',
		);

		$api_key = isset( $config['api_key'] ) ? trim( (string) $config['api_key'] ) : '';
		if ( '' !== $api_key ) {
			$args['headers']['x-foodord-license-key'] = $api_key;
		}

		$response = wp_remote_request( $endpoint, $args );
		if ( is_wp_error( $response ) ) {
			return new WP_Error( 'foodord_reservation_quota_unreachable', __( 'Online reservations are temporarily unavailable. Please try again later.', 'foodord' ) );
		}

		$status_code = wp_remote_retrieve_response_code( $response );
		$data        = json_decode( wp_remote_retrieve_body( $response ), true );
		if ( ! is_array( $data ) ) {
			return new WP_Error( 'foodord_reservation_quota_invalid_response', __( 'Online reservations are temporarily unavailable. Please try again later.', 'foodord' ) );
		}

		if ( $status_code >= 400 || isset( $data['code'] ) ) {
			$code = sanitize_key( (string) ( $data['code'] ?? 'reservation_quota_failed' ) );
			if ( 'monthly_reservation_limit_reached' === $code ) {
				return new WP_Error( 'foodord_reservation_monthly_limit_reached', self::get_customer_limit_message(), array( 'status' => 403 ) );
			}

			return new WP_Error(
				'foodord_reservation_quota_service_error',
				__( 'Online reservations are temporarily unavailable. Please try again later.', 'foodord' ),
				array( 'status' => $status_code )
			);
		}

		return $data;
	}

	/**
	 * Build an official central quota endpoint.
	 *
	 * @param string $action Endpoint action.
	 */
	private static function get_endpoint( string $action ): string {
		$action = sanitize_key( $action );
		if ( ! in_array( $action, array( 'status', 'authorize', 'commit', 'release' ), true ) ) {
			return '';
		}

		return esc_url_raw( self::ENDPOINT_BASE . $action );
	}

	/**
	 * Get the normalized domain used by the central service.
	 */
	private static function get_site_domain(): string {
		$host = (string) wp_parse_url( home_url(), PHP_URL_HOST );

		return function_exists( 'foodord_normalize_license_domain' )
			? foodord_normalize_license_domain( $host )
			: strtolower( trim( $host ) );
	}

	/**
	 * Build a non-sensitive local reservation reference prefix.
	 */
	private static function get_site_reference(): string {
		return substr( hash( 'sha256', self::get_site_domain() ), 0, 16 );
	}

	/**
	 * Normalize a cached status payload.
	 *
	 * @param array<string,mixed> $status Raw status.
	 * @return array{known:bool,allowed:bool,licensed:bool,limit:int,used:int,remaining:int|null,month:string}
	 */
	private static function normalize_status( array $status ): array {
		return array(
			'known'     => ! empty( $status['known'] ),
			'allowed'   => ! empty( $status['allowed'] ),
			'licensed'  => ! empty( $status['licensed'] ),
			'limit'     => absint( $status['limit'] ?? 0 ),
			'used'      => absint( $status['used'] ?? 0 ),
			'remaining' => isset( $status['remaining'] ) ? absint( $status['remaining'] ) : null,
			'month'     => sanitize_text_field( (string) ( $status['month'] ?? '' ) ),
		);
	}

	/**
	 * Return a fail-open early-check state. Final insert authorization still fails closed.
	 *
	 * @return array{known:bool,allowed:bool,licensed:bool,limit:int,used:int,remaining:int|null,month:string}
	 */
	private static function unknown_status(): array {
		return array(
			'known'     => false,
			'allowed'   => true,
			'licensed'  => false,
			'limit'     => 0,
			'used'      => 0,
			'remaining' => null,
			'month'     => '',
		);
	}
}
