<?php

namespace WeDevs\AppBridge\Integrations\WooCommerce;

use WeDevs\AppBridge\Core\AbstractIntegration;
use WeDevs\AppBridge\Core\Contracts\Hookable;
use WeDevs\AppBridge\Core\REST\PushNotificationsController;
use WeDevs\AppBridge\Integrations\WooCommerce\Notifications\CustomerEventCatalog;
use WeDevs\AppBridge\Integrations\WooCommerce\Notifications\CustomerEventDispatcher;
use WeDevs\AppBridge\Integrations\WooCommerce\Bookings\BookingAddToCartItem;
use WeDevs\AppBridge\Integrations\WooCommerce\REST\AddOnsController;
use WeDevs\AppBridge\Integrations\WooCommerce\REST\AuctionController;
use WeDevs\AppBridge\Integrations\WooCommerce\REST\BookingSlotsController;
use WeDevs\AppBridge\Integrations\WooCommerce\REST\BookingsController;
use WeDevs\AppBridge\Integrations\WooCommerce\REST\CustomerController;
use WeDevs\AppBridge\Integrations\WooCommerce\REST\LicenseController;
use WeDevs\AppBridge\Integrations\WooCommerce\REST\SettingsController;
use WeDevs\AppBridge\Integrations\WooCommerce\REST\SliderController;

class WooCommerceIntegration extends AbstractIntegration {

	private LicenseManager $license_manager;

	/**
	 * Singleton cache for conditional REST controllers. Each controller
	 * self-registers routes inside its constructor, so we instantiate at
	 * most once per class to avoid duplicate `register_rest_route` calls.
	 *
	 * @var array<class-string, object>
	 */
	private static array $controller_instances = [];

	public function __construct() {
		$this->license_manager = new LicenseManager();
		add_action( 'rest_api_init', [ $this, 'register_conditional_rest_controllers' ], 8 );
	}

	public function register_conditional_rest_controllers(): void {
		if ( class_exists( 'WC_Bookings_REST_Products_Controller' ) ) {
			$this->resolve( BookingsController::class );
			$this->resolve( BookingSlotsController::class );
		}

		if ( class_exists( 'WC_Product_Addons' ) ) {
			$this->resolve( AddOnsController::class );
		}

		if ( class_exists( 'WC_Product_Auction' ) ) {
			$this->resolve( AuctionController::class );
		}
	}

	/**
	 * Lazily instantiate a controller exactly once.
	 *
	 * @template T of object
	 * @param class-string<T> $class_name
	 * @return T
	 */
	private function resolve( string $class_name ): object {
		if ( ! isset( self::$controller_instances[ $class_name ] ) ) {
			self::$controller_instances[ $class_name ] = new $class_name();
		}

		return self::$controller_instances[ $class_name ];
	}

	public function get_license_manager(): LicenseManager {
		return $this->license_manager;
	}

	public static function is_active(): bool {
		return class_exists( 'WooCommerce' );
	}

	/**
	 * @return Hookable[]
	 */
	public function hookables(): array {
		return [
			$this->license_manager,
			new PaymentHandler(),
			new Category(),
			new WooCommerceHooks(),
			new OrderHooks(),
			new CustomerEventDispatcher(),
		];
	}

	/**
	 * @return object[]
	 */
	public function rest_controllers(): array {
		$controllers = [
			new SettingsController( $this->license_manager ),
			new LicenseController( $this->license_manager ),
			new SliderController(),
			new CustomerController(),
		];

		if ( $this->license_manager->get_license()->is_valid() ) {
			$controllers[] = new PushNotificationsController(
				'app-bridge/v1/pocket-store',
				'pocket_store_woocommerce_settings'
			);
		}

		// Bookings / Addons / Auctions controllers register themselves later via
		// `register_conditional_rest_controllers()` on `rest_api_init` — see
		// the constructor for the rationale (autoloader race).

		return $controllers;
	}

	public function menus(): array {
		return [
			[
				'label'    => __( 'Pocket Store', 'app-bridge' ),
				'slug'     => 'app-bridge',
				'mount_id' => 'app-bridge-pocket-store',
			],
		];
	}

	public function event_catalogs(): array {
		return [
			'customer' => CustomerEventCatalog::categories(),
		];
	}

	public function event_option_keys(): array {
		return [
			'customer' => 'pocket_store_woocommerce_settings',
		];
	}

	/**
	 * @return Hookable[]
	 */
	public function conditional_hookables(): array {
		$hookables = [];

		if ( class_exists( 'WC_Bookings_REST_Products_Controller' ) ) {
			$hookables[] = new BookingAddToCartItem();
		}

		return $hookables;
	}
}
