<?php
/**
 * Class PromoService
 *
 * @package Automattic\WCShipping
 */

namespace Automattic\WCShipping\Promo;

use Automattic\WCShipping\Connect\WC_Connect_Options;
use Automattic\WCShipping\Connect\WC_Connect_Service_Schemas_Store;
use Automattic\WCShipping\Connect\WC_Connect_Service_Settings_Store;
use Automattic\WCShipping\Tracks;

/**
 * Handles all promotion logics.
 */
class PromoService {
	/**
	 * Schema Store instance
	 *
	 * @var WC_Connect_Service_Schemas_Store
	 */
	protected $schemas_store;

	/**
	 * Settings Store instance
	 *
	 * @var WC_Connect_Service_Settings_Store
	 */
	protected $settings_store;

	/**
	 * Class constructor.
	 *
	 * @param WC_Connect_Service_Schemas_Store  $schemas_store   Schema store instance.
	 * @param WC_Connect_Service_Settings_Store $settings_store  Settings store instance.
	 */
	public function __construct( WC_Connect_Service_Schemas_Store $schemas_store, WC_Connect_Service_Settings_Store $settings_store ) {
		$this->schemas_store  = $schemas_store;
		$this->settings_store = $settings_store;

		add_action( 'admin_notices', array( $this, 'maybe_show_promotion_notice' ) );
		add_action( 'admin_init', array( $this, 'handle_promotion_notice_dismiss' ) );
	}

	/**
	 * Get promotion from service schemas.
	 *
	 * @return object|null Promotion object or null if none exists
	 */
	public function get_promotion() {
		$schemas = $this->schemas_store->get_service_schemas();
		$promo   = $schemas->promotion ?? null;

		if ( ! isset( $promo->id ) || $promo->remaining <= 0 ) {
			return null;
		}

		// phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
		if ( isset( $promo->endDate ) && strtotime( $promo->endDate ) < time() ) {
			return null;
		}

		// Unset dismissed parts of the promotion.
		if ( $this->is_promotion_dismissed( 'notice', $promo->id ) ) {
			unset( $promo->notice );
		}

		if ( $this->is_promotion_dismissed( 'banner', $promo->id ) ) {
			unset( $promo->banner );
		}

		// Sanitize HTML properties.
		foreach ( array( 'notice', 'banner', 'tooltip' ) as $property ) {
			if ( isset( $promo->{$property} ) ) {
				$promo->{$property} = wp_kses_post( $promo->{$property} );
			}
			if ( empty( $promo->{$property} ) ) {
				unset( $promo->{$property} );
			}
		}

		return $promo;
	}

	/**
	 * Check if a promotion is dismissed by type and ID.
	 *
	 * @param string $type The type of promotion ('notice' | 'banner').
	 * @param string $id   The ID of the promotion.
	 *
	 * @return bool True if the promotion is dismissed, false otherwise.
	 */
	public function is_promotion_dismissed( string $type, string $id ): bool {
		return (bool) get_user_meta( get_current_user_id(), "wcc-promo-{$type}-{$id}-dismissed", true );
	}

	/**
	 * Dismiss a promotion by type and ID.
	 *
	 * @param string $type The type of promotion ('notice' | 'banner').
	 * @param string $id   The ID of the promotion.
	 */
	public function dismiss_promotion( string $type, string $id ) {
		update_user_meta( get_current_user_id(), "wcc-promo-{$type}-{$id}-dismissed", true );
		Tracks::promo_dismissed( $type, $id );
	}

	/**
	 * Maybe show promotion on the WooCommerce Orders page.
	 */
	public function maybe_show_promotion_notice() {
		$promo = $this->get_promotion();

		if (
			// Promotion notice available.
			! isset( $promo->notice )
			// WooCommerce Orders page.
			|| get_current_screen()->base !== 'woocommerce_page_wc-orders'
			// WooCommerce Order detail page.
			|| isset( $_GET['id'] ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended
		) {
			return;
		}

		$dismiss_url = add_query_arg(
			array(
				'wcc-dismiss-promo-notice' => $promo->id,
				'_wpnonce'                 => wp_create_nonce( 'wcc_dismiss_promo_notice_nonce' ),
			),
		);

		?>
		<div class="notice notice-info is-dismissible" style="position: relative;">
			<a href="<?php echo esc_url( $dismiss_url ); ?>"
				style="text-decoration: none;"
				class="notice-dismiss"
				title="<?php esc_attr_e( 'Dismiss this notice', 'woocommerce-shipping' ); ?>"></a>
			<p><?php echo wp_kses_post( $promo->notice ); ?></p>
		</div>
		<?php

		Tracks::promo_notice_viewed( $promo->id );
	}

	/**
	 * Handle the dismissal of the Orders page notice.
	 */
	public function handle_promotion_notice_dismiss() {
		$promo = $this->get_promotion();

		if ( ! isset( $promo->id ) ) {
			return;
		}

		if ( ! isset( $_GET['wcc-dismiss-promo-notice'] )
			|| $_GET['wcc-dismiss-promo-notice'] !== $promo->id
			|| ! isset( $_GET['_wpnonce'] )
			|| ! check_admin_referer( 'wcc_dismiss_promo_notice_nonce' ) ) {
				return;
		}

		$this->dismiss_promotion( 'notice', $promo->id );
	}

	/**
	 * Maybe decrement promotion remaining count when a label is purchased.
	 *
	 * @param int    $order_id       The order ID.
	 * @param object $new_label_data The new label data.
	 */
	public function maybe_decrement_promotion_remaining( int $order_id, object $new_label_data ) {
		if ( 'PURCHASED' !== $new_label_data->status ) {
			return;
		}

		$promo = $this->get_promotion();

		if ( ! $promo || $new_label_data->promo_id !== $promo->id || $promo->remaining <= 0 ) {
			return;
		}

		$label_data = current(
			array_filter(
				$this->settings_store->get_label_order_meta_data( $order_id ),
				fn( $data ) => $data['label_id'] === $new_label_data->label_id
			)
		);

		if ( $label_data && 'PURCHASED' === $label_data['status'] ) {
			return;
		}

		--$promo->remaining;

		$this->update_promotion_schema( $promo );
	}

	/**
	 * Update the promotion schema in the service schemas store.
	 *
	 * @param object $promotion The updated promotion object.
	 */
	protected function update_promotion_schema( object $promotion ) {
		$services            = $this->schemas_store->get_service_schemas();
		$services->promotion = $promotion;
		WC_Connect_Options::update_option( 'services', $services );
	}
}
