<?php
/**
 * Dokan Kits - Main Plugin Class
 *
 * This file contains the main plugin class that initializes and bootstraps the
 * Dokan Kits plugin. It provides a centralized API for registering features,
 * managing features, and extending the plugin functionality.
 *
 * @since   3.0.0
 * @package Dokan_Kits
 */

use Automattic\WooCommerce\Utilities\FeaturesUtil;
use DokanKits\Core\Bootstrap;
use DokanKits\Core\Data\DataStore;
use DokanKits\Core\Data\Stores\SettingsDataStore;
use DokanKits\Core\DI\Container;
use DokanKits\Core\Interfaces\Hookable;
use DokanKits\Setup\SystemCheck;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Main Dokan Kits Plugin Class
 *
 * This class serves as the core of the Dokan Kits plugin, implementing the singleton pattern
 * to ensure only one instance is loaded. It manages plugin initialization, handling
 * dependencies, hooks registration, and provides an extensive API for developers to extend
 * and interact with the plugin.
 *
 * Features:
 * - Dependency Injection Container integration
 * - Settings management system
 * - Feature registration and management
 * - Extensible hooks architecture
 * - WooCommerce compatibility management
 *
 * @since   3.0.0
 * @package Dokan_Kits
 */
final class Dokan_Kits {

	/**
	 * Plugin version
	 *
	 * @var string
	 */
	public string $version = DOKAN_KITS_VERSION;

	/**
	 * Plugin slug
	 *
	 * @var string
	 */
	public string $slug = 'dokan-kits';

	/**
	 * Instance of this class.
	 *
	 * @var object
	 */
	private static $instance = null;

	/**
	 * Return an instance of this class.
	 *
	 * Implements the singleton pattern to ensure only one instance of the plugin exists
	 * at any time. This method is the preferred way to access the plugin functionality.
	 *
	 * @since  3.0.0
	 * @static
	 * @access public
	 *
	 * @return self A single instance of this class.
	 */
	public static function instance() {
		// If the single instance hasn't been set, set it now.
		if ( null === self::$instance ) {
			self::$instance = new self();
		}

		return self::$instance;
	}

	/**
	 * Initialize plugin hooks
	 *
	 * Sets up all the necessary WordPress hooks for the plugin lifecycle:
	 * - WooCommerce compatibility
	 * - Plugin bootstrap
	 * - Activation/deactivation hooks
	 *
	 * Provides 'before' and 'after' action hooks for third-party extensions.
	 *
	 * @since  3.0.0
	 * @access private
	 *
	 * @return void
	 */
	public function init(): void {
		// Register activation and deactivation hooks.
		register_activation_hook( DOKAN_KITS_FILE, array( $this, 'activate' ) );
		register_deactivation_hook( DOKAN_KITS_FILE, array( $this, 'deactivate' ) );

		// WooCommerce compatibility hooks.
		add_action( 'before_woocommerce_init', array( $this, 'declare_compatibility' ) );
		add_filter( 'woocommerce_data_stores', array( $this, 'load_data_stores' ) );

		// Bootstrap the plugin after all plugins are loaded.
		add_action( 'woocommerce_loaded', array( $this, 'load_hookable_services' ) );
		add_action( 'dokan_loaded', array( $this, 'boot' ) );

		/**
		 * Action hook that fires after initializing the plugin hooks
		 * Use this to add additional hooks to the plugin
		 *
		 * @since 3.0.0
		 *
		 * @param Dokan_Kits $this Plugin instance.
		 */
		do_action( 'dokan_kits_after_init_hooks', $this );
	}

	/**
	 * Get container instance
	 *
	 * Retrieves the dependency injection container used by the plugin.
	 * This container holds all registered services and can be extended
	 * by third-party developers using the 'dokan_kits_get_container' filter.
	 *
	 * @since  3.0.0
	 * @access public
	 *
	 * @return Container The dependency injection container instance.
	 */
	public function get_container(): Container {
		$container = dokan_kits_get_container();

		/**
		 * Filter the container instance
		 *
		 * Allows third-party developers to modify or extend the container
		 * before it's used by the plugin. This is useful for registering
		 * custom services or modifying existing ones.
		 *
		 * @since 3.0.0
		 *
		 * @param Container  $container The dependency injection container.
		 * @param Dokan_Kits $this      Plugin instance.
		 */
		return apply_filters( 'dokan_kits_get_container', $container, $this );
	}

	/**
	 * Load hookable services
	 *
	 * Loads the hookable services required for the plugin to function.
	 * This includes setting up the dependency injection container
	 * and registering all necessary services with WordPress hooks.
	 * Services must implement the Hookable interface to be registered.
	 *
	 * @since  3.0.0
	 * @access public
	 *
	 * @return void
	 */
	public function load_hookable_services(): void {
		/**
		 * Action hook that fires before loading hookable services
		 *
		 * Use this hook to register custom services to the container
		 * before the plugin loads its hookable services.
		 *
		 * @since 3.0.0
		 *
		 * @param Dokan_Kits $this Plugin instance.
		 */
		do_action( 'dokan_kits_before_load_hookable_services', $this );

		// Get all registered hookable services.
		$hooks = $this->get_container()->get( Hookable::class );

		/**
		 * Filter the hookable services
		 *
		 * Allows third-party developers to add or modify the hookable services
		 * that get registered with WordPress.
		 *
		 * @since 3.0.0
		 *
		 * @param array      $hooks Array of hookable service instances.
		 * @param Dokan_Kits $this  Plugin instance.
		 */
		$hooks = apply_filters( 'dokan_kits_hookable_services', $hooks, $this );

		// Register hooks for each service.
		foreach ( $hooks as $hook ) {
			$hook->register_hooks();
		}

		/**
		 * Action hook that fires after loading and registering hookable services
		 *
		 * @since 3.0.0
		 *
		 * @param array      $hooks Array of hookable service instances that were registered.
		 * @param Dokan_Kits $this  Plugin instance.
		 */
		do_action( 'dokan_kits_after_load_hookable_services', $hooks, $this );
	}

	/**
	 * Load data stores
	 *
	 * Loads the data stores required for the plugin to function.
	 * This includes setting up the dependency injection container
	 * and registering all necessary data stores.
	 *
	 * @since  3.0.0
	 * @access public
	 *
	 * @param array $stores Array of data stores to load.
	 *
	 * @return array Modified array of data stores.
	 */
	public function load_data_stores( array $stores ): array {
		$stores['dokan_kits_settings'] = SettingsDataStore::class;

		/**
		 * Filter the data stores to be loaded
		 *
		 * Allows third-party developers to register their own data stores
		 * for use with the Dokan Kits plugin.
		 *
		 * @since 3.0.0
		 *
		 * @param array      $stores The array of data stores.
		 * @param Dokan_Kits $this   Plugin instance.
		 */
		return apply_filters( 'dokan_kits_data_stores', $stores, $this );
	}

	/**
	 * Boot the plugin
	 *
	 * Main plugin initialization sequence:
	 * 1. Loads text domain for internationalization
	 * 2. Checks plugin dependencies
	 * 3. Bootstraps the plugin components
	 * 4. Fires plugin loaded actions
	 *
	 * Includes multiple action hooks for third-party extensions to tap into
	 * the plugin's lifecycle events.
	 *
	 * @since  3.0.0
	 * @access public
	 *
	 * @return void
	 */
	public function boot(): void {
		try {
			/**
			 * Action hook that fires before the plugin boots
			 *
			 * @since 3.0.0
			 *
			 * @param Dokan_Kits $this Plugin instance.
			 */
			do_action( 'dokan_kits_before_boot', $this );

			// Initialize database tables from each data store.
			$this->initialize_data_stores();

			// Check system requirements.
			$system_check = $this->get_container()->get( SystemCheck::class );
			if ( ! $system_check->check() ) {
				return;
			}

			// Load bootstrap.
			$bootstrap = $this->get_container()->get( Bootstrap::class );
			$bootstrap->boot();

			/**
			 * Fire an action when the plugin is fully loaded
			 *
			 * @since 3.0.0
			 *
			 * @param Dokan_Kits $this Plugin instance.
			 */
			do_action( 'dokan_kits_loaded', $this );
		} catch ( Exception $e ) {
			// Catch any exceptions during boot and display an admin notice.
			add_action(
				'admin_notices',
				static function () use ( $e ) {
					echo '<div class="notice notice-error"><p>';
					echo esc_html( sprintf( 'Dokan Kits encountered an error during initialization: %s', $e->getMessage() ) );
					echo '</p></div>';
				}
			);
		}
	}

	/**
	 * Initialize all registered data stores
	 *
	 * Sets up database tables and performs initial configuration for all
	 * registered data stores. This method:
	 * - Retrieves all data store services from the container
	 * - Creates database tables for each store
	 * - Updates database version tracking
	 * - Ensures data store consistency
	 *
	 * Called during plugin boot to ensure all data stores are properly
	 * initialized before the plugin becomes fully functional.
	 *
	 * @since 3.0.0
	 * @access private
	 *
	 * @return void
	 */
	private function initialize_data_stores(): void {
		// Get all data stores from the container.
		$data_stores = $this->get_container()->get( 'data-store-service' );

		// Initialize each data store that implements the DataStore interface.
		foreach ( $data_stores as $data_store ) {
			if ( ! $data_store instanceof DataStore ) {
				continue; // Skip if not a valid data store instance.
			}

			// Create database tables if they don't exist.
			$data_store->create_tables();

			// Update the database version for tracking.
			$data_store->update_db_version();
		}

		/**
		 * Action after initializing all data stores
		 *
		 * @since 3.0.0
		 *
		 * @param array      $data_stores Array of initialized data stores.
		 * @param Dokan_Kits $this        Plugin instance.
		 */
		do_action( 'dokan_kits_data_stores_initialized', $data_stores, $this );
	}

	/**
	 * Declare compatibility with WooCommerce custom order tables
	 *
	 * Ensures the plugin is compatible with WooCommerce's High-Performance Order Storage (HPOS)
	 * feature by declaring compatibility with custom order tables. This is required for
	 * WooCommerce 8.2+ to avoid compatibility warnings.
	 *
	 * @since 3.0.0
	 * @access public
	 *
	 * @return void
	 */
	public function declare_compatibility(): void {
		// Check if the WooCommerce FeaturesUtil class is available.
		if ( ! class_exists( FeaturesUtil::class ) ) {
			return; // Exit early if WooCommerce is not loaded or version is too old.
		}

		// Declare compatibility with WooCommerce High-Performance Order Storage.
		FeaturesUtil::declare_compatibility( 'custom_order_tables', DOKAN_KITS_FILE, true );
	}

	/**
	 * Plugin activation handler
	 *
	 * Handles the plugin activation process by:
	 * - Running system checks
	 * - Creating database tables
	 * - Setting up default configuration
	 * - Migrating data if needed
	 *
	 * Provides action hooks for third-party extensions to hook into
	 * the activation process.
	 *
	 * @since 3.0.0
	 * @access public
	 *
	 * @return void
	 */
	public function activate(): void {
		/**
		 * Action hook that fires before plugin activation
		 *
		 * @since 3.0.0
		 *
		 * @param Dokan_Kits $this Plugin instance.
		 */
		do_action( 'dokan_kits_before_activate', $this );

		$activator = $this->get_container()->get( DokanKits\Setup\Activator::class );
		$activator->run();

		/**
		 * Action hook that fires after plugin activation
		 *
		 * @since 3.0.0
		 *
		 * @param Dokan_Kits $this Plugin instance.
		 */
		do_action( 'dokan_kits_after_activate', $this );
	}

	/**
	 * Plugin deactivation handler
	 *
	 * Handles the plugin deactivation process by:
	 * - Cleaning up temporary data
	 * - Flushing rewrite rules
	 * - Running cleanup routines
	 * - Preserving user settings and data
	 *
	 * Note: This does not delete user data or settings - only performs
	 * cleanup operations necessary for proper deactivation.
	 *
	 * @since 3.0.0
	 * @access public
	 *
	 * @return void
	 */
	public function deactivate(): void {
		/**
		 * Action hook that fires before plugin deactivation
		 *
		 * @since 3.0.0
		 *
		 * @param Dokan_Kits $this Plugin instance.
		 */
		do_action( 'dokan_kits_before_deactivate', $this );

		$deactivator = $this->get_container()->get( DokanKits\Setup\Deactivator::class );
		$deactivator->run();

		/**
		 * Action hook that fires after plugin deactivation
		 *
		 * @since 3.0.0
		 *
		 * @param Dokan_Kits $this Plugin instance.
		 */
		do_action( 'dokan_kits_after_deactivate', $this );
	}

	/**
	 * Check if Pro version is active
	 *
	 * Determines whether the Pro version of Dokan Kits is currently active.
	 * This method provides a centralized way to check for Pro features availability
	 * throughout the plugin and can be filtered by third-party extensions.
	 *
	 * @since 3.0.0
	 * @access public
	 *
	 * @return bool True if Pro version is active, false otherwise.
	 */
	public function is_pro_active(): bool {
		/**
		 * Filter whether the Pro version of Dokan Kits is active
		 *
		 * Use this filter to programmatically enable or disable Pro features
		 * or modify the Pro status check behavior.
		 *
		 * @since 3.0.0
		 *
		 * @param bool $is_pro_active Whether the Pro version is active.
		 */
		return apply_filters( 'dokan_kits_is_pro_active', false );
	}

	/**
	 * Get the array of allowed settings groups that can be modified
	 *
	 * @since  3.0.0
	 *
	 * @return array<string> Array of allowed settings groups.
	 */
	public function get_settings_groups(): array {
		// Default groups.
		$default_groups = array( 'product', 'vendor', 'cart', 'shipping' );

		/**
		 * Filter the settings groups.
		 *
		 * @since 3.0.0
		 *
		 * @param array<string> $default_groups Default groups.
		 */
		$groups = apply_filters( 'dokan_kits_get_settings_groups', $default_groups );

		// Ensure no duplicate group names.
		return array_unique( $groups );
	}
}
