<?php
/**
 * Central monthly allowance client for browser-based Live Orders.
 *
 * @package FoodOrd
 */

defined( 'ABSPATH' ) || exit;

/**
 * Class Foodord_Live_Orders_Quota
 */
final class Foodord_Live_Orders_Quota {
	private const ENDPOINT_BASE    = 'https://www.foodord.com/wp-json/foodord-cloud/v1/feature-quota/';
	private const FEATURE_KEY      = 'live_orders';
	private const STATUS_CACHE_KEY = 'foodord_live_orders_quota_status';
	private const STATUS_CACHE_TTL = 30;
	private const REQUEST_TIMEOUT  = 15;
	private const MAX_REFERENCES   = 51;

	/**
	 * Register cache invalidation hooks for qualifying order transitions.
	 */
	public static function init(): void {
		add_action( 'woocommerce_order_status_processing', array( __CLASS__, 'clear_status_cache' ) );
		add_action( 'woocommerce_order_status_completed', array( __CLASS__, 'clear_status_cache' ) );
	}

	/**
	 * Get the current centrally enforced Live Orders entitlement.
	 *
	 * @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(
			'sync',
			array(
				'feature'    => self::FEATURE_KEY,
				'references' => self::get_current_month_order_references(),
			)
		);
		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;
	}

	/**
	 * Clear the short status cache after an order enters the live workflow.
	 *
	 * @param int $order_id Order identifier supplied by WooCommerce.
	 */
	public static function clear_status_cache( $order_id = 0 ): void {
		unset( $order_id );
		delete_transient( self::STATUS_CACHE_KEY );
	}

	/**
	 * Get the operator-facing message shown after the free allowance is used.
	 */
	public static function get_limit_message(): string {
		return __( 'Your monthly Live Orders allowance has been used. Activate a FoodOrd license to continue managing orders in the browser.', 'foodord' );
	}

	/**
	 * Format the remaining monthly allowance for operator-facing screens.
	 *
	 * @param int $remaining Remaining order allowance.
	 */
	public static function get_remaining_message( int $remaining ): string {
		$remaining = max( 0, $remaining );

		return sprintf(
			/* translators: %s: Number of Live Orders remaining this month. */
			_n(
				'%s Live Order remaining this month',
				'%s Live Orders remaining this month',
				$remaining,
				'foodord'
			),
			number_format_i18n( $remaining )
		);
	}

	/**
	 * Keep existing Live Orders cards available after the monthly allowance ends.
	 *
	 * @param WC_Order[]               $orders Current Live Orders feed candidates.
	 * @param array<string,mixed>|null $status Current central quota status.
	 * @return WC_Order[]
	 */
	public static function filter_feed_orders( array $orders, ?array $status = null ): array {
		$status = is_array( $status ) ? $status : self::get_status();
		if ( empty( $status['known'] ) || ! empty( $status['licensed'] ) || ! empty( $status['allowed'] ) ) {
			return $orders;
		}

		$limit = absint( $status['limit'] ?? 0 );
		if ( $limit < 1 ) {
			return array();
		}

		$allowed_orders = self::get_current_month_qualifying_orders( $limit );
		$allowed_ids    = array();
		foreach ( $allowed_orders as $allowed_order ) {
			$allowed_ids[ $allowed_order->get_id() ] = true;
		}

		$month_start = strtotime( gmdate( 'Y-m-01 00:00:00' ) . ' UTC' );
		$filtered    = array();
		foreach ( $orders as $order ) {
			if ( ! $order instanceof WC_Order ) {
				continue;
			}

			$date_created = $order->get_date_created();
			if (
				( $date_created && $date_created->getTimestamp() < $month_start )
				|| isset( $allowed_ids[ $order->get_id() ] )
			) {
				$filtered[] = $order;
			}
		}

		return $filtered;
	}

	/**
	 * Build stable, non-sensitive references for qualifying orders this month.
	 *
	 * The central service retains at most the threshold plus one reference per
	 * site and month, so the quota store cannot grow with unlimited order volume.
	 *
	 * @return string[]
	 */
	private static function get_current_month_order_references(): array {
		$orders     = self::get_current_month_qualifying_orders( self::MAX_REFERENCES );
		$references = array();
		$site_hash  = hash( 'sha256', self::get_site_domain() );

		foreach ( $orders as $order ) {
			$references[] = hash( 'sha256', $site_hash . '|order|' . $order->get_id() );
		}

		return $references;
	}

	/**
	 * Get the earliest qualifying orders created in the current UTC month.
	 *
	 * @param int $limit Maximum qualifying orders to return.
	 * @return WC_Order[]
	 */
	private static function get_current_month_qualifying_orders( int $limit ): array {
		if ( ! function_exists( 'wc_get_orders' ) || $limit < 1 ) {
			return array();
		}

		$month_start = strtotime( gmdate( 'Y-m-01 00:00:00' ) . ' UTC' );
		$month_end   = strtotime( '+1 month', $month_start ) - 1;
		$page        = 1;
		$orders      = array();

		do {
			$result = wc_get_orders(
				array(
					'date_created' => $month_start . '...' . $month_end,
					'limit'        => 100,
					'page'         => $page,
					'paginate'     => true,
					'order'        => 'ASC',
					'orderby'      => 'date',
					'status'       => array( 'processing', 'completed' ),
				)
			);

			$page_orders = is_object( $result ) && isset( $result->orders ) && is_array( $result->orders )
				? $result->orders
				: array();

			foreach ( $page_orders as $order ) {
				if (
					! $order instanceof WC_Order
					|| ( function_exists( 'foodord_is_excluded_order' ) && foodord_is_excluded_order( $order ) )
					|| (
						class_exists( 'Foodord_Table_Reservation_Deposits' )
						&& Foodord_Table_Reservation_Deposits::is_reservation_payment_order( $order )
					)
				) {
					continue;
				}

				$orders[] = $order;
				if ( count( $orders ) >= $limit ) {
					break 2;
				}
			}

			$max_pages = is_object( $result ) && isset( $result->max_num_pages )
				? absint( $result->max_num_pages )
				: 0;
			++$page;
		} while ( $page <= $max_pages && ! empty( $page_orders ) );

		return $orders;
	}

	/**
	 * Send one authenticated request to the central feature quota 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() ) {
		if ( 'sync' !== $action ) {
			return new WP_Error( 'foodord_live_orders_quota_action_invalid' );
		}

		$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(
			'body'    => wp_json_encode( $body ),
			'headers' => array(
				'accept'       => 'application/json',
				'content-type' => 'application/json',
			),
			'method'  => 'POST',
			'timeout' => self::REQUEST_TIMEOUT,
		);

		$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( esc_url_raw( self::ENDPOINT_BASE . $action ), $args );
		if ( is_wp_error( $response ) ) {
			return new WP_Error( 'foodord_live_orders_quota_unreachable' );
		}

		$status_code = wp_remote_retrieve_response_code( $response );
		$data        = json_decode( wp_remote_retrieve_body( $response ), true );
		if ( ! is_array( $data ) || $status_code >= 400 || isset( $data['code'] ) ) {
			return new WP_Error( 'foodord_live_orders_quota_invalid_response' );
		}

		return $data;
	}

	/**
	 * 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 ) );
	}

	/**
	 * Normalize a status response or cached snapshot.
	 *
	 * @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'] ?? '' ) ),
		);
	}

	/**
	 * Keep order operations available during a temporary central outage.
	 *
	 * @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'     => '',
		);
	}
}
