<?php
declare( strict_types=1 );

namespace Automattic\WooCommerce\GoogleListingsAndAds\Shipping;

use Automattic\WooCommerce\GoogleListingsAndAds\API\Google\Settings as GoogleSettings;
use Automattic\WooCommerce\GoogleListingsAndAds\Infrastructure\Registerable;
use Automattic\WooCommerce\GoogleListingsAndAds\Infrastructure\Service;
use Automattic\WooCommerce\GoogleListingsAndAds\Jobs\JobRepository;
use Automattic\WooCommerce\GoogleListingsAndAds\Jobs\UpdateShippingSettings;
use Automattic\WooCommerce\GoogleListingsAndAds\MerchantCenter\MerchantCenterService;

defined( 'ABSPATH' ) || exit;

/**
 * Class SyncerHooks
 *
 * Hooks to various WooCommerce and WordPress actions to automatically sync shipping settings.
 *
 * @package Automattic\WooCommerce\GoogleListingsAndAds\Shipping
 *
 * @since 2.1.0
 */
class SyncerHooks implements Service, Registerable {

	/**
	 * This property is used to avoid scheduling duplicate jobs in the same request.
	 *
	 * @var bool
	 */
	protected $already_scheduled = false;

	/**
	 * @var GoogleSettings
	 */
	protected $google_settings;

	/**
	 * @var MerchantCenterService
	 */
	protected $merchant_center;

	/**
	 * @var JobRepository
	 */
	protected $job_repository;

	/**
	 * SyncerHooks constructor.
	 *
	 * @param MerchantCenterService $merchant_center
	 * @param GoogleSettings        $google_settings
	 * @param JobRepository         $job_repository
	 */
	public function __construct( MerchantCenterService $merchant_center, GoogleSettings $google_settings, JobRepository $job_repository ) {
		$this->google_settings = $google_settings;
		$this->merchant_center = $merchant_center;
		$this->job_repository  = $job_repository;
	}

	/**
	 * Register the service.
	 */
	public function register(): void {
		// only register the hooks if Merchant Center account is connected and the user has chosen for the shipping rates to be synced from WooCommerce settings.
		if ( ! $this->merchant_center->is_connected() || ! $this->google_settings->should_get_shipping_rates_from_woocommerce() ) {
			return;
		}

		$update_settings = function () {
			$this->handle_update_shipping_settings();
		};

		// After a shipping zone object is saved to database.
		add_action( 'woocommerce_after_shipping_zone_object_save', $update_settings, 90 );

		// After a shipping zone is deleted.
		add_action( 'woocommerce_delete_shipping_zone', $update_settings, 90 );

		// After a shipping method is added to or deleted from a shipping zone.
		add_action( 'woocommerce_shipping_zone_method_added', $update_settings, 90 );
		add_action( 'woocommerce_shipping_zone_method_deleted', $update_settings, 90 );

		// After a shipping method is enabled or disabled.
		add_action( 'woocommerce_shipping_zone_method_status_toggled', $update_settings, 90 );

		// After a shipping class is updated/deleted.
		add_action( 'woocommerce_shipping_classes_save_class', $update_settings, 90 );
		add_action( 'saved_product_shipping_class', $update_settings, 90 );
		add_action( 'delete_product_shipping_class', $update_settings, 90 );

		// After free_shipping and flat_rate method options are updated.
		add_action( 'woocommerce_update_options_shipping_free_shipping', $update_settings, 90 );
		add_action( 'woocommerce_update_options_shipping_flat_rate', $update_settings, 90 );

		// The shipping options can also be updated using other methods (e.g. by calling WC_Shipping_Method::process_admin_options).
		// Those methods may not fire any hooks, so we need to watch the base WordPress hooks for when those options are updated.
		$on_option_change = function ( $option ) {
			/**
			 * This Regex checks for the shipping options key generated by the `WC_Shipping_Method::get_instance_option_key` method.
			 * We check for the shipping method IDs supported by GLA (flat_rate or free_shipping), and an integer instance_id.
			 *
			 * @see \WC_Shipping_Method::get_instance_option_key for more information about this key.
			 */
			if ( preg_match( '/^woocommerce_(flat_rate|free_shipping)_\d+_settings$/', $option ) ) {
				$this->handle_update_shipping_settings();
			}
		};
		add_action(
			'updated_option',
			$on_option_change,
			90
		);
		add_action(
			'added_option',
			$on_option_change,
			90
		);
	}

	/**
	 * Handle updating of Merchant Center shipping settings.
	 *
	 * @return void
	 */
	protected function handle_update_shipping_settings() {
		if ( $this->already_scheduled ) {
			return;
		}

		$this->job_repository->get( UpdateShippingSettings::class )->schedule();
		$this->already_scheduled = true;
	}
}
