<?php
/**
 * Plugin Loader.
 *
 * @package modern-cart
 * @since 0.0.1
 */

namespace ModernCart;

use ModernCart\Admin_Core\Admin_Menu;
use ModernCart\Inc\Abilities\Register_Abilities;
use ModernCart\Inc\Admin_Notices;
use ModernCart\Inc\Floating;
use ModernCart\Inc\Floating_Ajax;
use ModernCart\Inc\Helper;
use ModernCart\Inc\Scripts;
use ModernCart\Inc\Order_Tracking;
use ModernCart\Inc\Slide_Out;
use ModernCart\Inc\Slide_Out_Ajax;

/**
 * Plugin_Loader
 *
 * @since 0.0.1
 */
class Plugin_Loader {
	/**
	 * Instance
	 *
	 * @access private
	 * @var object Class Instance.
	 * @since 0.0.1
	 */
	private static $instance;

	/**
	 * Constructor
	 *
	 * @since 0.0.1
	 */
	public function __construct() {
		spl_autoload_register( [ $this, 'autoload' ] );

		register_activation_hook( __DIR__ . '/modern-cart.php', [ $this, 'activate' ] );
		add_action( 'plugins_loaded', [ $this, 'load_classes' ] );
		add_action( 'init', [ $this, 'save_version_info' ] );
		add_action( 'init', [ $this, 'register_bsf_analytics_entity' ], 20 );
		add_action( 'admin_init', [ $this, 'redirect_to_onboarding' ] );
		if ( defined( 'MODERNCART_BASE' ) ) {
			add_filter( 'plugin_action_links_' . MODERNCART_BASE, [ $this, 'action_links' ] );
		}

		do_action( 'moderncart_loaded' );
	}

	/**
	 * Load plugin text domain.
	 *
	 * @since 1.0.9
	 * @return void
	 */
	public function load_textdomain(): void {
		$site_locale = apply_filters( 'plugin_locale', get_locale(), 'modern-cart' );

		// Global translations directory takes priority (Loco Translate / manual uploads).
		$mofile = WP_LANG_DIR . '/plugins/modern-cart-' . $site_locale . '.mo';

		if ( ! file_exists( $mofile ) ) {
			// Fall back to bundled translations shipped with the plugin.
			$mofile = MODERNCART_DIR . 'languages/modern-cart-' . $site_locale . '.mo';
		}

		if ( ! file_exists( $mofile ) ) {
			return;
		}

		// Load translations for the site locale.
		load_textdomain( 'modern-cart', $mofile, $site_locale );

		// In AJAX context (admin-ajax.php), WP_Translation_Controller calls
		// determine_locale() at translate-time, which returns the logged-in user's
		// profile language rather than the site language. Loading the site translations
		// under the request locale too ensures the controller finds them regardless
		// of which locale it looks up — without modifying any global WordPress filters.
		$request_locale = determine_locale();
		if ( $request_locale !== $site_locale ) {
			load_textdomain( 'modern-cart', $mofile, $request_locale );
		}
	}

	/**
	 * Activate plugin and set onboarding redirect.
	 */
	public function activate(): void {
		// Record install timestamp for days_since_install analytics metric.
		if ( ! get_option( 'mcw_usage_installed_time', false ) ) {
			update_option( 'mcw_usage_installed_time', time(), false );
		}

		$is_onboarding_complete = get_option( 'moderncart_is_onboarding_complete', 'no' );

		if ( 'yes' === $is_onboarding_complete ) {
			return;
		}

		// Short-lived flag: the onboarding redirect is only relevant on the page
		// load immediately following activation. Expiring it after 30 seconds
		// prevents a stale flag from hijacking an unrelated admin navigation later
		// (e.g. when Modern Cart is activated alongside another plugin).
		set_transient( 'moderncart_redirect_to_onboarding', 'yes', 30 );
	}

	/**
	 * Redirect to onboarding page.
	 */
	public function redirect_to_onboarding(): void {
		if ( ! get_transient( 'moderncart_redirect_to_onboarding' ) ) {
			return;
		}

		// Never redirect during AJAX, cron, or network-admin requests.
		if ( wp_doing_ajax() || wp_doing_cron() || is_network_admin() ) {
			return;
		}

		global $pagenow;

		// Only auto-open onboarding from the Plugins screen — the page WordPress
		// lands on right after a single activation. This stops the flag from
		// hijacking an unrelated navigation (e.g. Power Coupons "Add coupon",
		// which loads post-new.php) when the redirect did not fire on activation,
		// such as when Modern Cart is activated alongside another plugin. Also skip
		// bulk activation, where the user expects to stay on the plugins list.
		// phpcs:ignore WordPress.Security.NonceVerification.Recommended
		if ( 'plugins.php' !== $pagenow || isset( $_GET['activate-multi'] ) ) {
			return;
		}

		$url = add_query_arg(
			[
				'page'       => 'moderncart_settings',
				'nonce'      => wp_create_nonce( 'moderncart_onboarding_nonce' ),
				'onboarding' => 1,
			],
			admin_url( 'admin.php' )
		);

		delete_transient( 'moderncart_redirect_to_onboarding' );

		wp_safe_redirect( $url );
		exit;
	}

	/**
	 * Save version information.
	 */
	public function save_version_info(): void {
		$version = get_option( 'moderncart_version' );

		if ( is_array( $version ) && isset( $version['current'] ) && MODERNCART_VER === $version['current'] ) {
			// Already updated.
			return;
		}

		// Backfill install time for sites that activated before this tracking was added.
		if ( ! get_option( 'mcw_usage_installed_time', false ) ) {
			update_option( 'mcw_usage_installed_time', time(), false );
		}

		$version = [
			'current'  => MODERNCART_VER,
			'previous' => ( is_array( $version ) && isset( $version['current'] ) ) ? $version['current'] : '',
		];

		update_option( 'moderncart_version', $version );
	}

	/**
	 * Initiator
	 *
	 * @since 0.0.1
	 * @return object initialized object of class.
	 */
	public static function get_instance() {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Autoload classes.
	 *
	 * @param string $class class name.
	 */
	public function autoload( $class ): void {
		if ( 0 !== strpos( $class, __NAMESPACE__ ) ) {
			return;
		}

		$class_to_load = $class;

		$filename = preg_replace(
			[ '/^' . __NAMESPACE__ . '\\\/', '/([a-z])([A-Z])/', '/_/', '/\\\/' ],
			[ '', '$1-$2', '-', DIRECTORY_SEPARATOR ],
			$class_to_load
		);

		if ( is_string( $filename ) ) {
			$filename = strtolower( $filename );

			$file = MODERNCART_DIR . $filename . '.php';

			// if the file redable, include it.
			if ( is_readable( $file ) ) {
				require_once $file;
			}
		}
	}

	/**
	 *  Declare the woo HPOS compatibility.
	 *
	 * @since 1.0.1
	 * @return void
	 */
	public function declare_woo_hpos_compatibility() {
		if ( class_exists( '\Automattic\WooCommerce\Utilities\FeaturesUtil' ) ) {
			\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', MODERNCART_FILE, true );
		}
	}

	/**
	 * Loads plugin classes as per requirement.
	 *
	 * @return void
	 * @since 0.0.1
	 */
	public function load_classes(): void {
		$this->load_textdomain();
		$this->load_bsf_analytics_loader();

		if ( ! class_exists( 'woocommerce' ) ) {
			return;
		}

		// Let WooCommerce know, CartFlows is compatible with HPOS.
		add_action( 'before_woocommerce_init', array( $this, 'declare_woo_hpos_compatibility' ) );

		if ( is_admin() ) {
			Admin_Menu::get_instance();
			$this->load_astra_notices();
			Admin_Notices::get_instance();
		}

		$this->load_integration_classes();

		Scripts::get_instance();
		Floating::get_instance();
		Slide_Out::get_instance();
		Slide_Out_Ajax::get_instance();
		Floating_Ajax::get_instance();
		Register_Abilities::get_instance();
		Order_Tracking::get_instance();
	}

	/**
	 * Adds links in Plugins page.
	 *
	 * @param array<string> $links Existing links.
	 * @return array<string> Filtered links with settings added.
	 * @since 0.0.1
	 */
	public function action_links( $links ) {
		$plugin_links = apply_filters(
			'moderncart_cpsw_plugin_action_links',
			[
				'moderncart_settings' => '<a href="' . admin_url( 'admin.php?page=moderncart_settings' ) . '">' . __( 'Settings', 'modern-cart' ) . '</a>',
			]
		);

		return array_merge( $plugin_links, $links );
	}

	/**
	 * Load the Astra Notices library.
	 *
	 * @since 1.0.9
	 * @return void
	 */
	public function load_astra_notices() {
		if ( ! class_exists( 'Astra_Notices' ) ) {
			require_once MODERNCART_DIR . 'inc/libraries/astra-notices/class-astra-notices.php';
		}
	}

	/**
	 * Require the BSF Analytics Loader and trigger its constructor.
	 *
	 * Runs on plugins_loaded so the BSF_Analytics_Loader class is available
	 * for all plugins before init fires. Entity registration with translatable
	 * strings is intentionally deferred to register_bsf_analytics_entity()
	 * which runs on init after the textdomain has been loaded.
	 *
	 * @since 1.0.8
	 * @return void
	 */
	public function load_bsf_analytics_loader() {
		if ( ! class_exists( 'BSF_Analytics_Loader' ) ) {
			require_once MODERNCART_DIR . 'inc/libraries/bsf-analytics/class-bsf-analytics-loader.php';
		}
	}

	/**
	 * Register the Modern Cart entity with BSF Analytics.
	 *
	 * Runs on init:10, registered in the constructor so it queues before
	 * BSF_Analytics_Loader::load_analytics (registered during plugins_loaded).
	 * Translatable strings are safe here because the textdomain is loaded by init.
	 *
	 * Also performs a one-time migration of the legacy cf_analytics_optin option
	 * to mcw_usage_optin before the analytics class reads it.
	 *
	 * Backward compatibility matrix (keyed on MODERNCART_VER / MODERNCART_PRO_VER):
	 *  - Latest Free + Latest Pro  (Free ≥ 1.0.8, Pro ≥ 1.2.4): Free registers entity;
	 *    Pro defers and sends data via filter.  ← this method runs fully.
	 *  - Latest Free + Older Pro   (Free ≥ 1.0.8, Pro < 1.2.4): Old Pro registers its
	 *    own entity and does not defer.  Free must not register a duplicate.  ← early return.
	 *  - Older Free + Latest Pro   (Free < 1.0.8, Pro ≥ 1.2.4): Free does not have this
	 *    method; Pro handles analytics via its own fallback.  No conflict.
	 *  - Older Free + Older Pro    (Free < 1.0.8, Pro < 1.2.4): Neither plugin runs this
	 *    code; no change in behaviour.
	 *
	 * @since 1.0.8
	 * @return void
	 */
	public function register_bsf_analytics_entity() {
		if ( ! class_exists( 'BSF_Analytics_Loader' ) ) {
			return;
		}

		$this->maybe_migrate_analytics_optin();

		$plugin_slug   = 'modern-cart';
		$bsf_analytics = \BSF_Analytics_Loader::get_instance();

		$bsf_analytics->set_entity(
			array(
				'mcw' => array(
					'hide_optin_checkbox' => true,
					'product_name'        => 'Modern Cart',
					'usage_doc_link'      => 'https://my.cartflows.com/usage-tracking/',
					'path'                => MODERNCART_DIR . 'inc/libraries/bsf-analytics',
					'author'              => 'CartFlows',
					'deactivation_survey' => apply_filters(
						'moderncart_bsf_analytics_deactivation_survey_data',
						array(
							array(
								'id'                => 'deactivation-survey-' . $plugin_slug,
								'popup_logo'        => MODERNCART_URL . 'admin-core/assets/images/logo.svg',
								'plugin_slug'       => $plugin_slug,
								'plugin_version'    => MODERNCART_VER,
								'popup_title'       => __( 'Quick Feedback', 'modern-cart' ),
								'support_url'       => 'https://cartflows.com/contact/',
								'popup_description' => __( 'If you have a moment, please share why you are deactivating Modern Cart Starter:', 'modern-cart' ),
								'show_on_screens'   => array( 'plugins' ),
							),
						)
					),
				),
			)
		);

		// Ensure BSF_Analytics_Events is loaded before instantiating Modern_Cart_Analytics.
		// class-bsf-analytics-events.php is normally loaded inside BSF_Analytics::includes(),
		// which runs on init at priority 10 via BSF_Analytics_Loader::load_analytics(). Since
		// register_bsf_analytics_entity() also runs on init (priority 20), BSF_Analytics_Loader
		// should have already run — but we require it explicitly here as a safety guarantee so
		// detect_state_events() always finds BSF_Analytics_Events available.
		if ( ! class_exists( 'BSF_Analytics_Events' ) ) {
			require_once MODERNCART_DIR . 'inc/libraries/bsf-analytics/class-bsf-analytics-events.php';
		}

		require_once MODERNCART_DIR . 'inc/libraries/class-modern-cart-analytics.php';
	}

	/**
	 * Migrate legacy cf_analytics_optin to mcw_usage_optin.
	 *
	 * Sites that previously ran a CartFlows-era version of this plugin may
	 * have the analytics opt-in stored under cf_analytics_optin. This copies
	 * the value to the current key and removes the legacy option.
	 *
	 * Skips silently if cf_analytics_optin does not exist, or if
	 * mcw_usage_optin already has a value.
	 *
	 * @since 1.0.8
	 * @return void
	 */
	private function maybe_migrate_analytics_optin() {
		$legacy_value = get_option( 'cf_analytics_optin' );

		if ( false === $legacy_value ) {
			// Legacy option absent — nothing to migrate.
			return;
		}

		if ( false !== get_option( 'mcw_usage_optin' ) ) {
			// New option already set — just clean up the legacy key.
			return;
		}

		update_option( 'mcw_usage_optin', $legacy_value );
	}

	/**
	 * Loads integration classes like mcw-zipwp-helper.
	 *
	 * @since x.x.x
	 * @return void
	 */
	public function load_integration_classes() {
		require_once MODERNCART_DIR . 'inc/integrations/mcw-zipwp-helper.php';
	}
}

/**
 * Kicking this off by calling 'get_instance()' method
 */
Plugin_Loader::get_instance();
