<?php

// phpcs:disable WordPress.WP.I18n.TextDomainMismatch -- Bridge to pay4all-for-twint :
// this WooCommerce integration re-emits the TWINT gateway strings the free
// plugin already ships and translates. Reusing its text domain avoids
// duplicating dozens of msgids × 4 locales in pay4all-pro's .po files.
namespace Pay4All\Pro\Integrations\Twint;

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

/**
 * Bootstraps the WooCommerce side of the TWINT integration.
 *
 * The free companion plugin `pay4all-for-twint` (WP.org, no license) ships the
 * TWINT SOAP engine, admin wizard and Pay4All Shop integration but explicitly
 * contains NO WooCommerce runtime code. All WooCommerce-facing classes
 * (payment gateway, blocks registration, checkout AJAX, background monitors,
 * order metabox transactions template) live here in `pay4all-pro` and are
 * booted at runtime once we know both companions are present.
 *
 * Wiring: called from `Pay4All\Pro\Plugin::boot()` (form-mode branch AND
 * WC-only branch) when both `\Twnt4All\Bootstrap` AND `\WooCommerce` are
 * detected.
 *
 * The moved classes still declare the `Twnt4All\...` namespace (they cross-
 * reference many helpers that live back in pay4all-for-twint : Engine,
 * Support\WpBridge, Rest\RestRouter, Update\LicenseManager, Vendor\*, etc.).
 * Rather than rewrite every namespace, this bootstrap simply requires the
 * physical files stored here so the free plugin's PSR-4 autoloader keeps
 * working — the class names it looks up (Twnt4All\Checkout\TwintGateway,
 * Twnt4All\Blocks\BlocksRegistration, etc.) are declared inside these files.
 */
final class Bootstrap {

	/** @var bool */
	private static $registered = false;

	public static function register() : void {
		if ( self::$registered ) { return; }
		if ( ! class_exists( '\\WooCommerce' ) ) { return; }
		// Two-step guard against a « pay4all-for-twint active but dormant »
		// state — happens when the merchant deactivates pay4all-form (Pay4All
		// Shop) : pay4all-for-twint's on_plugins_loaded() bails without
		// registering its PSR-4 autoloader, so the top-level
		// `Twnt4All\Bootstrap` class (instantiated directly from the plugin's
		// main file) still exists but every other `Twnt4All\...` class /
		// interface fails to autoload. Requiring our own `WooOrderLock.php`
		// (which `implements Twnt4All\Engine\Contract\LockInterface`) would
		// then trigger a fatal.
		//
		// We test the actual interface required by the moved files instead of
		// the top-level class : if the autoloader is up, the interface
		// resolves ; if it isn't, we stay silent and let pay4all-for-twint
		// show its own « needs Pay4All Shop » admin notice.
		if ( ! interface_exists( '\\Twnt4All\\Engine\\Contract\\LockInterface' ) ) { return; }
		self::$registered = true;

		// Physically load the migrated files. They declare classes in the
		// Twnt4All namespace so nothing else needs to change.
		self::require_moved_files();

		// -----------------------------------------------------------------
		// Payment gateway registration
		// -----------------------------------------------------------------
		add_filter( 'woocommerce_payment_gateways', [ __CLASS__, 'add_twint_gateway' ] );
		add_filter( 'woocommerce_available_payment_gateways', [ __CLASS__, 'conditionally_show_payment_gateway' ], 10, 1 );

		// Localized gateway title / description (WPML + Polylang aware).
		add_filter( 'woocommerce_gateway_title', [ __CLASS__, 'localize_gateway_title' ], 10, 2 );
		add_filter( 'woocommerce_gateway_description', [ __CLASS__, 'localize_gateway_description' ], 10, 2 );

		// WC "Manage" button on the gateway list — send admin to our wizard.
		add_action( 'load-woocommerce_page_wc-settings', [ __CLASS__, 'redirect_wc_gateway_manage_to_wizard' ] );

		// Checkout Blocks integration is registered at pay4all-pro file-parse
		// time (see pay4all-pro.php) because `woocommerce_blocks_loaded` fires
		// at plugins_loaded:0 — well before our Plugin::boot() runs at
		// plugins_loaded:11. Hooking it from here would be too late and TWINT
		// would silently disappear from the wp:woocommerce/checkout block.

		// Refund mirroring when admin flips status to "refunded" manually.
		add_action( 'woocommerce_order_status_refunded', [ __CLASS__, 'auto_refund_twint_on_status_change' ], 10, 2 );

		// -----------------------------------------------------------------
		// TWINT-direct webhook endpoints (integration: direct).
		// -----------------------------------------------------------------
		add_action( 'woocommerce_api_' . \T4A_PREFIX . '_webhook_oun', [ '\\Twnt4All\\Checkout\\TwintGateway', 'handle_webhook_order_update_notification' ] );
		add_action( 'woocommerce_api_' . \T4A_PREFIX . '_webhook_redirect', [ '\\Twnt4All\\Checkout\\TwintGateway', 'handle_webhook_redirect' ] );
		add_action( 'woocommerce_api_' . \T4A_PREFIX . '_webhook_cancel', [ '\\Twnt4All\\Checkout\\TwintGateway', 'handle_webhook_cancel' ] );

		// TWNT (pay4all.net proxy) endpoints — kept for backwards compat.
		add_action( 'woocommerce_api_' . \T4A_PREFIX . '_transaction_redirect', [ '\\Twnt4All\\Checkout\\TwintGateway', 'handle_transaction_redirect' ] );
		add_action( 'woocommerce_api_' . \T4A_PREFIX . '_transaction_cancel', [ '\\Twnt4All\\Checkout\\TwintGateway', 'handle_transaction_cancel' ] );
		add_action( 'woocommerce_api_' . \T4A_PREFIX . '_transaction_webhook', [ '\\Twnt4All\\Checkout\\TwintGateway', 'handle_transaction_webhook' ] );

		// -----------------------------------------------------------------
		// Background monitor + reconciliation crons.
		// -----------------------------------------------------------------
		add_action( 'wp_async_t4a_twint_monitor_order', [ '\\Twnt4All\\Checkout\\TwintGateway', 'monitor_order_background_task' ], 10, 3 );
		add_action( 'wp_async_t4a_twint_monitor_order_immediate', [ '\\Twnt4All\\Checkout\\TwintGateway', 'monitor_order_background_task' ], 10, 3 );

		add_action( \T4A_PREFIX . '_check_order_statuses', [ '\\Twnt4All\\Backend\\AdminAjax', 'check_successful_order_statuses' ] );
		add_action( \T4A_PREFIX . '_bg_cron_task_single', [ '\\Twnt4All\\Checkout\\TwintGateway', 'run_scheduled_background_task' ], 10, 1 );

		add_filter( 'cron_schedules', [ __CLASS__, 'add_cron_schedules' ] );

		// -----------------------------------------------------------------
		// Init the SOAP engine's WC-facing wiring on plugins_loaded so we're
		// safely after WooCommerce.
		// -----------------------------------------------------------------
		add_action( 'plugins_loaded', [ __CLASS__, 'boot_wc_runtime' ], 20 );

		// Template resolver override — payment-page.php and metabox templates
		// now live inside pay4all-pro. The free plugin's helper is called from
		// AdminAjax::twnt_check_transaction_status() etc.
		add_filter( 'pay4all_for_twint_locate_template', [ __CLASS__, 'locate_template_override' ], 10, 4 );

		// WooCommerce sub-menu shortcut to the TWINT wizard.
		if ( is_admin() ) {
			WooMenu::register();
		}
	}

	/**
	 * Physically loads the moved-here TWINT WooCommerce classes so the free
	 * plugin's PSR-4 autoloader (which points at pay4all-for-twint/src/)
	 * doesn't need to find them.
	 *
	 * Every load is guarded by `class_exists( …, false )` (autoload-off) because
	 * older pay4all-for-twint releases still ship the same class files under
	 * their PSR-4 root, and requiring both physical paths triggers a
	 * "Cannot redeclare class" fatal. We let whichever plugin loaded first win.
	 */
	private static function require_moved_files() : void {
		$base = __DIR__;
		self::maybe_require( '\\Twnt4All\\Support\\WooBridge',              $base . '/Support/WooBridge.php' );
		self::maybe_require( '\\Twnt4All\\Support\\WooOrderLock',           $base . '/Support/WooOrderLock.php' );
		self::maybe_require( '\\Twnt4All\\Wallet\\WalletPaymentHandler',    $base . '/Wallet/WalletPaymentHandler.php' );
		self::maybe_require( '\\Twnt4All\\Wallet\\TwintToken',              $base . '/Wallet/TwintToken.php' );
		// Woo/frontend.php is a procedural file (no class). Guard against a
		// legacy pay4all-for-twint install still shipping the same file, which
		// would raise "Cannot redeclare function" when we require both copies.
		if ( ! function_exists( 'pay4all_for_twint_get_account_saved_payment_methods_list_item' ) ) {
			require_once $base . '/Woo/frontend.php';
		}
		self::maybe_require( '\\Twnt4All\\Blocks\\BlocksRegistration',      $base . '/Blocks/BlocksRegistration.php' );
		self::maybe_require( '\\Twnt4All\\Checkout\\MonitorJob',            $base . '/Checkout/MonitorJob.php' );
		self::maybe_require( '\\Twnt4All\\Checkout\\ImmediateMonitorJob',   $base . '/Checkout/ImmediateMonitorJob.php' );
		self::maybe_require( '\\Twnt4All\\Checkout\\CheckoutAjax',          $base . '/Checkout/CheckoutAjax.php' );
		self::maybe_require( '\\Twnt4All\\Checkout\\TwintGateway',          $base . '/Checkout/TwintGateway.php' );
		// OrderDataRepository is intentionally kept in pay4all-for-twint
		// (used by both WC and Pay4All Form flows). The free plugin's PSR-4
		// autoloader resolves it — no manual require needed here.
	}

	/**
	 * `require_once` guarded by a no-autoload class_exists check.
	 *
	 * Both pay4all-for-twint and pay4all-pro physically ship every
	 * `Twnt4All\...` class file that lives under this folder — the two paths
	 * differ so `require_once` won't dedupe them. This helper skips the
	 * include if the class is already declared, no matter which plugin got
	 * there first.
	 */
	private static function maybe_require( string $fqcn, string $path ) : void {
		if ( class_exists( $fqcn, false ) ) { return; }
		if ( is_file( $path ) ) {
			require_once $path;
		}
	}

	public static function boot_wc_runtime() : void {
		// The moved files reference \Twnt4All\Wallet\WalletPaymentHandler (which
		// stays in the free plugin) — its polylang fix has to be installed
		// once WC is up.
		if ( class_exists( '\\Twnt4All\\Wallet\\WalletPaymentHandler' ) ) {
			\Twnt4All\Wallet\WalletPaymentHandler::add_polylang_fix();
		}

		\Twnt4All\Checkout\CheckoutAjax::init();

		// Async task instances.
		new \Twnt4All\Checkout\MonitorJob();
		new \Twnt4All\Checkout\ImmediateMonitorJob();

		// Schedules.
		if ( ! wp_next_scheduled( \T4A_PREFIX . '_automatically_renew_certificate' ) ) {
			wp_schedule_event( time(), 'weekly', \T4A_PREFIX . '_automatically_renew_certificate' );
		}
		if ( ! wp_next_scheduled( \T4A_PREFIX . '_check_certificate_status' ) ) {
			wp_schedule_event( time(), 'weekly', \T4A_PREFIX . '_check_certificate_status' );
		}
		if ( defined( 'T4A_ORDER_CHECK_INTERVAL' ) && \T4A_ORDER_CHECK_INTERVAL > 0 ) {
			if ( ! wp_next_scheduled( \T4A_PREFIX . '_check_order_statuses' ) ) {
				wp_schedule_event( time(), \T4A_PREFIX . '_order_check', \T4A_PREFIX . '_check_order_statuses' );
			}
		}
	}

	// ---------------------------------------------------------------------
	// Filter / action callbacks (previously on Twnt4All\Bootstrap).
	// ---------------------------------------------------------------------

	public static function add_twint_gateway( $methods ) {
		$methods[ 't4a_twint' ] = 'Twnt4All\\Checkout\\TwintGateway';
		return $methods;
	}

	public static function conditionally_show_payment_gateway( $available_gateways ) {
		$check_for_currency = get_option( 't4a_twint_settings_chf_only' );
		if ( ! is_admin() && $check_for_currency === 'yes' && strtolower( get_woocommerce_currency() ) !== 'chf' ) {
			unset( $available_gateways[ 't4a_twint' ] );
		}
		return $available_gateways;
	}

	public static function localize_gateway_title( $title, $id ) {
		if ( $id !== 't4a_twint' ) { return $title; }
		$lang = \Twnt4All\Backend\ConfigPage::current_supported_lang();
		$localized = get_option( 't4a_twint_title_' . $lang, '' );
		return $localized !== '' ? $localized : $title;
	}

	public static function localize_gateway_description( $description, $id ) {
		if ( $id !== 't4a_twint' ) { return $description; }
		$lang = \Twnt4All\Backend\ConfigPage::current_supported_lang();
		$localized = get_option( 't4a_twint_description_' . $lang, '' );
		return $localized !== '' ? $localized : $description;
	}

	public static function redirect_wc_gateway_manage_to_wizard() : void {
		// phpcs:disable WordPress.Security.NonceVerification.Recommended -- read-only routing check.
		$tab     = isset( $_GET[ 'tab' ] ) ? sanitize_key( wp_unslash( $_GET[ 'tab' ] ) ) : '';
		$section = isset( $_GET[ 'section' ] ) ? sanitize_key( wp_unslash( $_GET[ 'section' ] ) ) : '';
		// phpcs:enable WordPress.Security.NonceVerification.Recommended
		if ( $tab !== 'checkout' || $section !== 't4a_twint' ) { return; }
		// The "Manage" button on the WC payment methods list lands here. Send
		// merchants to the Pay4All Pro TWINT settings screen (payment texts +
		// order status configuration). The TWINT credentials (Store UUID,
		// certificate) still live in the free wizard under Pay4All TWINT.
		wp_safe_redirect( add_query_arg( [ 'page' => Backend\WcSettings::SLUG ], admin_url( 'admin.php' ) ) );
		exit;
	}

	/**
	 * Registers the TWINT payment method for the WooCommerce Checkout Block.
	 *
	 * Called directly from pay4all-pro.php at file-parse time, wrapped in an
	 * `add_action( 'woocommerce_blocks_loaded', ... )` listener that fires
	 * regardless of whether `Plugin::boot()` has run yet. This is the ONLY
	 * reliable timing : plugins_loaded:11 (our `boot()`) is too late relative
	 * to plugins_loaded:0 (WC's dispatch of `woocommerce_blocks_loaded`).
	 *
	 * Self-guarding : the WC Blocks classes and the moved BlocksRegistration
	 * file are only touched once we're sure both companions exist. If the free
	 * pay4all-for-twint plugin is missing, we bail before pulling the file in.
	 */
	public static function register_blocks_payment_method() : void {
		if ( ! class_exists( 'Automattic\\WooCommerce\\Blocks\\Payments\\Integrations\\AbstractPaymentMethodType' ) ) { return; }
		if ( ! class_exists( '\\Twnt4All\\Bootstrap' ) ) { return; }

		// The moved BlocksRegistration file is normally loaded from
		// require_moved_files() (called by Bootstrap::register()). When the
		// Blocks hook fires before our register() runs, load it manually — but
		// go through maybe_require() so we don't collide with a copy still
		// shipped by pay4all-for-twint.
		self::maybe_require( '\\Twnt4All\\Blocks\\BlocksRegistration', __DIR__ . '/Blocks/BlocksRegistration.php' );
		// Bail if neither plugin managed to declare the class (misinstall).
		if ( ! class_exists( '\\Twnt4All\\Blocks\\BlocksRegistration', false ) ) { return; }

		add_action(
			'woocommerce_blocks_payment_method_type_registration',
			function ( \Automattic\WooCommerce\Blocks\Payments\PaymentMethodRegistry $registry ) : void {
				$container = \Automattic\WooCommerce\Blocks\Package::container();
				$container->register(
					\Twnt4All\Blocks\BlocksRegistration::class,
					function () {
						return new \Twnt4All\Blocks\BlocksRegistration();
					}
				);
				$registry->register( $container->get( \Twnt4All\Blocks\BlocksRegistration::class ) );
			},
			5
		);
	}

	public static function add_cron_schedules( $schedules ) {
		if ( defined( 'T4A_ORDER_CHECK_INTERVAL' ) && \T4A_ORDER_CHECK_INTERVAL > 0 ) {
			$order_check_interval = \T4A_ORDER_CHECK_INTERVAL * 60;
			$schedules[ \T4A_PREFIX . '_order_check' ] = [
				'interval' => $order_check_interval,
				/* translators: %1$s dynamic value(s). */
				'display'  => sprintf( __( 'Order check: every %1$s minutes', 'pay4all-for-twint' ), \T4A_ORDER_CHECK_INTERVAL ),
			];
		}
		if ( ! isset( $schedules[ 'weekly' ] ) ) {
			$schedules[ 'weekly' ] = [
				'interval' => 604800,
				'display'  => __( 'Once weekly', 'pay4all-for-twint' ),
			];
		}
		return $schedules;
	}

	public static function auto_refund_twint_on_status_change( $order_id, $order = null ) : void {
		if ( ! $order instanceof \WC_Order ) {
			$order = wc_get_order( $order_id );
		}
		if ( ! $order || $order->get_payment_method() !== 't4a_twint' ) { return; }
		if ( count( $order->get_refunds() ) > 0 ) { return; }
		$amount = (float) $order->get_total();
		if ( $amount <= 0 ) { return; }

		$line_items = [];
		foreach ( $order->get_items( [ 'line_item', 'shipping', 'fee', 'tax' ] ) as $item_id => $item ) {
			$refund_total = (float) $order->get_line_total( $item, true, false );
			$line_items[ $item_id ] = [
				'qty'          => $item->get_quantity(),
				'refund_total' => $refund_total,
				'refund_tax'   => [],
			];
		}

		$result = wc_create_refund( [
			'amount'         => $amount,
			'reason'         => __( 'Statut commande changé manuellement sur « Remboursée ».', 'pay4all-for-twint' ),
			'order_id'       => $order->get_id(),
			'line_items'     => $line_items,
			'refund_payment' => true,
			'restock_items'  => false,
		] );

		if ( is_wp_error( $result ) ) {
			/* translators: %s: error message returned by wc_create_refund. */
			$order->add_order_note( sprintf( __( 'Tentative de remboursement automatique TWINT échouée : %s', 'pay4all-for-twint' ), $result->get_error_message() ) );
			return;
		}
		$order->add_order_note( __( 'Remboursement TWINT déclenché automatiquement suite au changement de statut « Remboursée ».', 'pay4all-for-twint' ) );
	}

	/**
	 * Redirects the free plugin's template resolver to the templates now
	 * bundled inside pay4all-pro.
	 */
	public static function locate_template_override( $template, $template_name, $template_path, $default_path ) {
		if ( is_string( $template ) && file_exists( $template ) ) {
			return $template;
		}
		$candidate = __DIR__ . '/templates/' . ltrim( (string) $template_name, '/' );
		if ( file_exists( $candidate ) ) {
			return $candidate;
		}
		return $template;
	}
}
