<?php
namespace Pay4All\Pro;

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

/**
 * Main bootstrap. Dispatches to a companion-plugin mode :
 *
 *   - « Form mode »        : when pay4all-form is loaded. Wires every Pro
 *                            subsystem that extends the free plugin
 *                            (categories, brands, tickets, filter widget,
 *                            abandoned-cart recovery, etc.).
 *   - « WooCommerce mode » : reserved for a future release. When
 *                            \WooCommerce is present without pay4all-form,
 *                            the Pro subsystems will graft themselves onto
 *                            the WooCommerce order flow instead.
 *   - « Standalone »       : neither companion detected. Only the License
 *                            layer runs so the merchant still sees the
 *                            Licences page and receives plugin updates.
 *
 * Each feature class ALSO guards itself against
 * LicenseManager::is_feature_allowed() at runtime, so on an expired
 * trial the UI keeps rendering but persistence and public output stay
 * locked (matches the WooCommerce-Pro convention of a soft gate).
 */
final class Plugin {

	/** @var Plugin|null */
	private static $instance = null;

	/** @var bool */
	private $booted = false;

	public static function instance() : Plugin {
		if ( self::$instance === null ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	public function boot() : void {
		if ( $this->booted ) { return; }
		$this->booted = true;

		I18n::register();

		// License layer runs on every request regardless of the companion :
		// registers its own admin menu (« Pay4All PRO »), its own cron, and
		// the Updater filter. Keep it wired FIRST so the merchant still sees
		// the Licences page and receives plugin updates even without a
		// companion plugin installed.
		License\Bootstrap::run();

		// Dispatch on the detected companion. Only ONE mode boots per
		// request — we don't try to be everything at once.
		if ( class_exists( '\\Pay4All\\Form\\Plugin' ) ) {
			$this->boot_form_mode();
			return;
		}

		if ( class_exists( '\\WooCommerce' ) ) {
			// WooCommerce is present without pay4all-form. Reserved for a
			// future release for the bulk of Pro features, but we still
			// wire the Age integration so a merchant running WC + pay4all-
			// pro + pay4all-age gets the WooCommerce KYC surfaces.
			if ( class_exists( '\\Pay4All\\Age\\Services\\Kyc' ) ) {
				Integrations\Age\WooBridge::register();
				Integrations\Age\Checkout::register();
				Integrations\Age\Registration::register();
				if ( is_admin() ) {
					Integrations\Age\OrderMetabox::register();
					Integrations\Age\ProductFields::register();
					Integrations\Age\WooMenu::register();
				}
			}
			// TWINT WooCommerce integration — booted when the free companion
			// pay4all-for-twint is installed (the class check is enough since
			// this branch already guarantees WC is loaded).
			if ( class_exists( '\\Twnt4All\\Bootstrap' ) ) {
				Integrations\Twint\Bootstrap::register();
			}
			// Nudge the merchant toward the free companion : Pay4All Pro
			// gets 90 % of its value from pay4all-form, so we surface a
			// dedicated onboarding page under the Pay4All PRO menu.
			if ( is_admin() ) {
				Admin\PromoteShop::register();
			}
			return;
		}

		// Standalone : neither companion detected. Nothing else to boot,
		// the License layer above is enough to keep the plugin useful.
		// Still promote pay4all-form so the merchant sees where the value
		// lives even from a fresh Pro install.
		if ( is_admin() ) {
			Admin\PromoteShop::register();
		}
	}

	/**
	 * Wires every subsystem that extends the free pay4all-form plugin.
	 * Called by `boot()` when \Pay4All\Form\Plugin is loaded.
	 */
	private function boot_form_mode() : void {
		PostTypes::register();

		// Ticket QR endpoint : /p4all-ticket/{token} public page consumed by
		// gatekeepers scanning printed tickets at the venue door.
		Frontend\TicketEndpoint::register();

		// Issues one wp_p4all_tickets row per seat sold — hooks the free
		// plugin's `p4all_after_order_created` action so the merchant's
		// Billets admin list picks up new orders automatically.
		Frontend\TicketIssuer::register();

		// Server-side backstop for the category / brand quantity rules the
		// JS validator enforces client-side. Rejects tampered payloads that
		// bypass the JS check (curl script, JS-off browser, etc.).
		Frontend\CartRuleValidator::register();

		// Enriches the free plugin's customer + admin order email context
		// with the tickets attached to the order (title, short description,
		// QR image URL) so customer-new-order.php can render the QR codes.
		Frontend\TicketMailContext::register();

		// Product filter widget rendered above the products step on forms
		// that opt in via Réglages du formulaire › Affichage › « Afficher
		// les filtres ». Hooks the free plugin's `p4all_form_filter_widget`
		// action + the `p4all_form_product_card_attrs` filter that
		// annotates each card with its categories / brands / price so the
		// client-side JS in form.js can hide-show without a server round.
		Frontend\FilterWidget::register();

		// Abandoned-cart feature : AJAX capture on the last-but-one form step,
		// recovery-URL handler on `init`, cron-driven mailer + purge. The
		// mailer lives in Pro\Services\Mailer with self-contained templates.
		Frontend\AbandonedCarts::register();

		// Newsletter spooler + public unsubscribe endpoint. Kept outside
		// the is_admin() branch below because the spooler tick fires from
		// wp-cron (may run without an admin session) and the unsubscribe
		// URL is a public front-end request. The tracking endpoints
		// (open pixel + click redirect) also live on the public side
		// since recipients hit them from their inbox, not the admin.
		Services\NewsletterSpooler::register();
		Frontend\Newsletter\UnsubscribeEndpoint::register();
		Frontend\Newsletter\TrackingEndpoint::register();

		// Ensure the cron event is scheduled on every request (idempotent —
		// wp_schedule_event skips if already queued). Covers merchants who
		// upgrade Pro via file-copy rather than the WP updater, which would
		// otherwise skip the activation hook.
		Activator::schedule_cron();
		// Same reason for the schema migration : file-copy upgrades never
		// re-fire the activation hook, but they still need the new
		// newsletter-stats columns. maybe_migrate_schema() is a no-op once
		// the flag matches the latest version.
		Activator::maybe_migrate_schema();

		if ( is_admin() ) {
			Admin\Categories\RulesField::register();
			Admin\Brands\RulesField::register();
			Admin\TermReorder::register();
			Admin\Tickets\Page::register();
			Admin\AbandonedCarts\Page::register();
			// Newsletter admin : list page, per-post metaboxes (kind +
			// content), and the shortcut buttons injected on the product /
			// promo edit screens via the free plugin's action hooks.
			// StatsPage registers the admin-post handler used by the
			// « Export CSV » button on the per-campaign stats screen.
			Admin\Newsletter\Page::register();
			Admin\Newsletter\MetaBoxes::register();
			Admin\Newsletter\QuickCreateButton::register();
			Admin\Newsletter\StatsPage::register();
		}

		// Age verification : if pay4all-age is installed AND WooCommerce is
		// active, wire the WooCommerce KYC integration. pay4all-age itself
		// handles the pay4all-form side + the shared WP user-profile screen ;
		// we plug the WC checkout / registration / product / order surfaces
		// on top.
		if ( class_exists( '\\Pay4All\\Age\\Services\\Kyc' ) && class_exists( '\\WooCommerce' ) ) {
			Integrations\Age\WooBridge::register();
			Integrations\Age\Checkout::register();
			Integrations\Age\Registration::register();
			if ( is_admin() ) {
				Integrations\Age\OrderMetabox::register();
				Integrations\Age\ProductFields::register();
				Integrations\Age\WooMenu::register();
			}
		}

		// TWINT : if pay4all-for-twint is installed AND WooCommerce is
		// active, wire the WooCommerce gateway + blocks + monitor jobs.
		// pay4all-for-twint itself handles the pay4all-form side (form-based
		// TWINT checkout) + the shared TWINT admin wizard.
		if ( class_exists( '\\Twnt4All\\Bootstrap' ) && class_exists( '\\WooCommerce' ) ) {
			Integrations\Twint\Bootstrap::register();
		}

		// Integration with the free Pay4All Shop plugin — filters that unlock
		// colours / multilingual / filter widget + a menu-swap that turns the
		// Pro-teaser sidebar entries into real destinations.
		Integrations\Pay4AllForm::register();
	}
}
