<?php
/**
 * Plugin Loader — Singleton bootstrap for CapiFlow.
 *
 * All Core features load unconditionally — no license gates.
 * Pro features hook in via the 17 defined hook points (see Hook Map in plan).
 *
 * @package CapiFlow
 */

namespace CapiFlow\Core;

defined( 'ABSPATH' ) || exit;

class Plugin_Loader {

	/**
	 * Singleton instance.
	 *
	 * @var Plugin_Loader|null
	 */
	private static $instance = null;

	/**
	 * Queue Manager instance.
	 *
	 * @var Queue_Manager|null
	 */
	public $queue = null;

	/**
	 * Get singleton instance.
	 *
	 * @return Plugin_Loader
	 */
	public static function instance(): self {
		if ( null === self::$instance ) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Private constructor (singleton).
	 */
	private function __construct() {}

	/**
	 * Initialize the plugin — all Core features load unconditionally.
	 */
	public function init(): void {
		// Always require core dependencies first.
		$this->require_core_files();

		// DB migration — admin only (never fires on frontend page loads).
		if ( is_admin() ) {
			DB_Manager::maybe_migrate();
		}

		// Ensure installation timestamp exists (self-healing for existing installs / file updates).
		if ( false === get_option( 'capiflow_installed_at' ) ) {
			add_option( 'capiflow_installed_at', time() );
		}

		// ── Load Core Free features — UNCONDITIONAL ──────────────────────────
		$this->load_free_core();

		$includes = CAPIFLOW_PLUGIN_DIR . 'includes/';
		require_once $includes . 'class-queue-manager.php';
		require_once $includes . 'class-custom-order-statuses.php';
		require_once $includes . 'class-purchase-controller.php';
		require_once $includes . 'class-context-capture.php';
		require_once $includes . 'class-analytics-tracker.php';
		require_once $includes . 'class-cart-monitor.php';
		require_once $includes . 'class-dataset-quality.php';

		$this->queue = new Queue_Manager();
		$this->queue->register_hooks();

		// Custom order statuses MUST register before Purchase_Controller.
		( new Custom_Order_Statuses() )->register_hooks();

		$purchase_ctrl = new Purchase_Controller();
		$purchase_ctrl->register_hooks();

		( new Context_Capture() )->register_hooks();
		( new Cart_Monitor() )->register_hooks();

		// Debug console is always available in Core.
		add_filter( 'capiflow_debug_console_enabled', '__return_true' );

		// Fraud module (calculation = Free, action = Pro).
		$this->load_module( 'fraud' );

		// Invoice module (Free).
		$this->load_module( 'invoice' );

		// Scripts module — custom header/footer/checkout code injection (Free).
		$this->load_module( 'scripts' );

		// ── Admin UI ──────────────────────────────────────────────────────────
		if ( is_admin() ) {
			$this->load_admin();
			add_action( 'admin_init', array( $this, 'register_privacy_policy' ) );
		}

		// ── Plugin action links ───────────────────────────────────────────────
		add_filter(
			'plugin_action_links_' . CAPIFLOW_PLUGIN_BASENAME,
			array( $this, 'add_action_links' )
		);

		// ── Register Action Scheduler handlers (unconditional) ──────────────
		// Handlers MUST be registered on every request so AS can fire them.
		// Scheduling (as_schedule_recurring_action) is deferred to action_scheduler_pre_init.
		$this->register_as_handlers();

		// ── Schedule recurring tasks ──────────────────────────────────────────
		// Deferred to 'init' priority 99 — Action Scheduler data store must be
		// fully initialized before calling as_has_scheduled_action() or
		// as_schedule_recurring_action(). AS initializes on 'init' priority 1-10,
		// so priority 99 guarantees the data store is ready.
		add_action( 'init', array( $this, 'schedule_tasks' ), 99 );

		// ── Pro hook point — MUST be last ─────────────────────────────────────
		do_action( 'capiflow_core_loaded' );
	}

	/**
	 * Require core dependency files (no instantiation).
	 */
	private function require_core_files(): void {
		$includes = CAPIFLOW_PLUGIN_DIR . 'includes/';

		require_once $includes . 'class-license-manager.php';
		require_once $includes . 'class-db-manager.php';
		require_once $includes . 'class-encryption.php';
		require_once $includes . 'class-logger.php';
		require_once $includes . 'class-event-id-generator.php';
		require_once $includes . 'class-data-normalizer.php';
		require_once $includes . 'class-cookie-manager.php';
	}

	/**
	 * Load free core tracking features.
	 */
	private function load_free_core(): void {
		$includes = CAPIFLOW_PLUGIN_DIR . 'includes/';

		require_once $includes . 'class-capi-sender.php';
		require_once $includes . 'class-event-builder.php';
		require_once $includes . 'class-pixel-injector.php';
		require_once $includes . 'class-proxy-endpoint.php';
		require_once $includes . 'class-conflict-detector.php';

		// Register REST proxy endpoint.
		( new Proxy_Endpoint() )->register_hooks();

		// Register pixel injector (frontend JS).
		( new Pixel_Injector() )->register_hooks();

		// Conflict detector.
		( new Conflict_Detector() )->register_hooks();
	}

	/**
	 * Load a module directory (all class-*.php files inside).
	 *
	 * @param string $module Module directory name (e.g., 'fraud', 'invoice').
	 */
	private function load_module( string $module ): void {
		$path = CAPIFLOW_PLUGIN_DIR . 'modules/' . $module . '/';

		if ( ! is_dir( $path ) ) {
			return;
		}

		$files = glob( $path . 'class-*.php' );
		if ( empty( $files ) ) {
			return;
		}

		foreach ( $files as $file ) {
			require_once $file;
		}

		do_action( 'capiflow_module_loaded_' . $module );
	}

	/**
	 * Load admin-only classes.
	 */
	private function load_admin(): void {
		$admin = CAPIFLOW_PLUGIN_DIR . 'admin/';

		require_once $admin . 'class-admin-settings.php';
		require_once $admin . 'class-order-columns.php';
		require_once $admin . 'class-order-metabox.php';
		require_once $admin . 'class-dashboard-widget.php';

		( new Admin_Settings() )->register_hooks();
		( new Order_Columns() )->register_hooks();
		( new Order_Metabox() )->register_hooks();
		( new Dashboard_Widget() )->register_hooks();
	}

	/**
	 * Schedule recurring Action Scheduler tasks.
	 *
	 * IMPORTANT: This method is hooked on 'action_scheduler_pre_init' (not plugins_loaded).
	 * AS data store must be initialized before calling as_has_scheduled_action() or
	 * as_schedule_recurring_action() — otherwise WP fires an incorrect-usage notice.
	 *
	 * Hook order: plugins_loaded (20) → action_scheduler_pre_init → this method.
	 */
	public function schedule_tasks(): void {
		if ( ! function_exists( 'as_has_scheduled_action' ) ) {
			return;
		}

		try {
			// Queue processing (every minute).
			if ( ! as_has_scheduled_action( 'capiflow_process_queue' ) ) {
				$interval = (int) get_option( 'capiflow_queue_interval', 1 ) * MINUTE_IN_SECONDS;
				as_schedule_recurring_action( time() + 60, max( $interval, MINUTE_IN_SECONDS ), 'capiflow_process_queue', array(), 'capiflow' );
			}

			// Daily maintenance (analytics cleanup, log cleanup).
			if ( ! as_has_scheduled_action( 'capiflow_daily_maintenance' ) ) {
				as_schedule_recurring_action( time() + HOUR_IN_SECONDS, DAY_IN_SECONDS, 'capiflow_daily_maintenance', array(), 'capiflow' );
			}

			// Cart monitor — hourly scan of WC session table.
			if ( ! as_has_scheduled_action( 'capiflow_scan_abandoned_carts' ) ) {
				as_schedule_recurring_action( time() + 1800, HOUR_IN_SECONDS, 'capiflow_scan_abandoned_carts', array(), 'capiflow' );
			}

			// Dataset Quality API — fetch from Meta once per day.
			if ( ! as_has_scheduled_action( 'capiflow_fetch_dataset_quality' ) ) {
				as_schedule_recurring_action( time() + 7200, DAY_IN_SECONDS, 'capiflow_fetch_dataset_quality', array(), 'capiflow' );
			}
		} catch ( \Exception $e ) {
			Logger::error( 'Scheduler', 'Failed to schedule tasks: ' . $e->getMessage() );
		}
	}

	/**
	 * Register Action Scheduler action handlers.
	 *
	 * IMPORTANT: Called from init() — NOT from schedule_tasks().
	 * Handlers MUST be registered unconditionally on every request so that
	 * when AS fires an action (via Async Request or WP-Cron), the callback
	 * is already attached. Previously these were inside schedule_tasks()
	 * which only runs on action_scheduler_pre_init — if that hook failed,
	 * handlers were never registered and AS reported "no callbacks".
	 */
	public function register_as_handlers(): void {
		// Queue processing — handler is in Queue_Manager::register_hooks() (line 72).
		// Daily maintenance.
		add_action( 'capiflow_daily_maintenance', array( $this, 'run_daily_maintenance' ) );
		// Cart monitor.
		add_action( 'capiflow_scan_abandoned_carts', array( Cart_Monitor::class, 'scan' ) );
		// Dataset Quality API.
		add_action( 'capiflow_fetch_dataset_quality', array( Dataset_Quality::class, 'fetch_from_meta' ) );
		// Deferred Purchase analytics (Audit C3 — race condition fix).
		add_action( 'capiflow_record_purchase_analytics', array( $this, 'handle_deferred_purchase_analytics' ) );
	}

	/**
	 * Daily maintenance cron handler.
	 *
	 * R12-H1 Fix: Method body was referenced but never defined.
	 */
	public function run_daily_maintenance(): void {
		// Purge old analytics rows (respects capiflow_analytics_retention_days filter).
		DB_Manager::cleanup_old_analytics();

		// Logger cleanup (old log entries).
		Logger::cleanup_old_logs();

		// Pro hook — Pro_License::maybe_refresh() registers its own add_action here.
		// No explicit call needed — Pro plugin hooks into capiflow_daily_maintenance.
	}

	/**
	 * Deferred Purchase analytics handler (Audit C3 — race condition fix).
	 *
	 * Runs 5 minutes after purchase so Pixel_Injector has time to set
	 * _capiflow_pixel_fired on the thank-you page.
	 *
	 * @param int $order_id WooCommerce order ID.
	 */
	public function handle_deferred_purchase_analytics( int $order_id ): void {
		$order = wc_get_order( $order_id );
		if ( ! $order ) {
			return;
		}

		// By now Pixel_Injector has had 5 minutes to set _capiflow_pixel_fired.
		$pixel_fired = $order->get_meta( '_capiflow_pixel_fired', true );
		$source      = ! empty( $pixel_fired ) ? 'both' : 'server';
		$revenue     = (float) $order->get_total();
		$recovered   = ( 'server' === $source ) ? $revenue : 0.0;
		$currency    = $order->get_currency();

		Analytics_Tracker::record( 'Purchase', $source, $revenue, $recovered, $currency );
	}

	/**
	 * Register privacy policy content (WP.org Rule 7 — REQUIRED).
	 */
	public function register_privacy_policy(): void {
		if ( ! function_exists( 'wp_add_privacy_policy_content' ) ) {
			return;
		}
		wp_add_privacy_policy_content(
			'CapiFlow', // R12-M2 Fix: Use short brand name (WooCommerce/Jetpack/Yoast pattern).
			wp_kses_post(
				'<p>' . __( 'CapiFlow sends the following customer data to the Meta Conversions API when tracking events:', 'capiflow' ) . '</p>' .
				'<ul>' .
				'<li>' . __( 'Hashed email address (SHA-256 — not readable by Meta)', 'capiflow' ) . '</li>' .
				'<li>' . __( 'Hashed phone number (SHA-256)', 'capiflow' ) . '</li>' .
				'<li>' . __( 'Hashed first and last name (SHA-256)', 'capiflow' ) . '</li>' .
				'<li>' . __( 'Browser IP address (sent server-to-server)', 'capiflow' ) . '</li>' .
				'<li>' . __( 'Browser user agent string', 'capiflow' ) . '</li>' .
				'<li>' . __( 'Meta Click ID cookie (_fbc) and Browser ID cookie (_fbp)', 'capiflow' ) . '</li>' .
				'<li>' . __( 'Order value and currency (non-personal financial data)', 'capiflow' ) . '</li>' .
				'</ul>' .
				'<p>' .
				sprintf(
					/* translators: %s: URL to Meta Privacy Policy */
					__( 'All personal data sent to Meta is hashed (one-way encrypted) before transmission. Meta\'s privacy policy governs how this data is processed: %s', 'capiflow' ),
					'<a href="https://www.facebook.com/privacy/policy" target="_blank" rel="noopener">https://www.facebook.com/privacy/policy</a>'
				) .
				'</p>' .
				'<p>' . __( 'This data is only transmitted when the store owner has configured a Meta Pixel ID and Access Token in the plugin settings.', 'capiflow' ) . '</p>' .
				'<h3>' . __( 'Consent Controls', 'capiflow' ) . '</h3>' .
				'<p>' . __( 'CapiFlow provides consent enforcement controls for store owners. When enabled, tracking events are only processed after consent has been granted through the site\'s cookie consent mechanism. If no consent mechanism is configured, tracking events continue normally. Store owners are responsible for configuring a lawful consent solution where required.', 'capiflow' ) . '</p>'
			)
		);
	}

	/**
	 * Add plugin action links (📊 Dashboard, ⚙️ Settings).
	 *
	 * @param array $links Existing links.
	 * @return array Modified links.
	 */
	public function add_action_links( array $links ): array {
		$plugin_links = array(
			'<a href="' . esc_url( admin_url( 'admin.php?page=capiflow-dashboard' ) ) . '">'
				. esc_html__( 'Dashboard', 'capiflow' ) . '</a>',
			'<a href="' . esc_url( admin_url( 'admin.php?page=capiflow-settings' ) ) . '">'
				. esc_html__( 'Settings', 'capiflow' ) . '</a>',
		);
		return array_merge( $plugin_links, $links );
	}
}
