<?php

namespace WeDevs\AppBridge;

use Automattic\WooCommerce\Utilities\FeaturesUtil;
use WeDevs\AppBridge\Admin\Menu;
use WeDevs\AppBridge\Core\Appsero;
use WeDevs\AppBridge\Core\REST\Auth\UserController;
use WeDevs\AppBridge\Core\Contracts\Hookable;
use WeDevs\AppBridge\Core\Contracts\IntegrationInterface;
use WeDevs\AppBridge\Core\Notifications\EventRegistry;
use WeDevs\AppBridge\Core\Auth\JwtAuth;
use WeDevs\AppBridge\Core\Auth\PasswordResetOtpMailer;
use WeDevs\AppBridge\Core\REST\Auth\AuthController;
use WeDevs\AppBridge\Core\REST\HelperController;
use WeDevs\AppBridge\Core\REST\LicenseVerificationController;
use WeDevs\AppBridge\Integrations\Dokan\DokanIntegration;
use WeDevs\AppBridge\Integrations\WooCommerce\WooCommerceIntegration;
use WeDevs\AppBridge\Integrations\WPERP\WPERPIntegration;
use WeDevs\AppBridge\Integrations\WPProjectManager\WPProjectManagerIntegration;

final class AppBridge {

	public string $version = '1.0.0';

	private static ?AppBridge $instance = null;

	/** @var IntegrationInterface[] */
	private array $integrations = [];

	/** @var Hookable[] */
	private array $hookables = [];

	/** @var object[] */
	private array $rest_controllers = [];

	private function __construct() {
		$this->define_constants();

		register_activation_hook( APP_BRIDGE_FILE, [ $this, 'activate' ] );
		register_deactivation_hook( APP_BRIDGE_FILE, [ $this, 'deactivate' ] );

		add_action( 'plugins_loaded', [ $this, 'init_plugin' ] );

		// WooCommerce compat — only fires if WC is present
		add_action( 'before_woocommerce_init', [ $this, 'declare_woocommerce_feature_compatibility' ] );
	}

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

		return self::$instance;
	}

	/** @return IntegrationInterface[] */
	public function integrations(): array {
		return $this->integrations;
	}

	public function dokan(): ?DokanIntegration {
		foreach ( $this->integrations as $integration ) {
			if ( $integration instanceof DokanIntegration ) {
				return $integration;
			}
		}

		return null;
	}

	public function init_plugin(): void {
		$this->register_hooks();
		$this->register_services();

		foreach ( $this->hookables as $hookable ) {
			$hookable->register_hooks();
		}
	}

	public function activate(): void {
		if ( ! get_option( JwtAuth::SECRET_KEY_OPTION ) ) {
			update_option( JwtAuth::SECRET_KEY_OPTION, bin2hex( random_bytes( 32 ) ) );
		}
	}

	public function deactivate(): void {
	}

	// ── Service registration ──

	private function register_services(): void {
		// Core (always loaded)
		$this->hookables[] = new Assets();
		$this->hookables[] = new Menu();
		$this->hookables[] = new Appsero();
		$this->hookables[] = new PasswordResetOtpMailer();

		$this->rest_controllers[] = new AuthController();
		$this->rest_controllers[] = new UserController();
		$this->rest_controllers[] = new LicenseVerificationController();
		$this->rest_controllers[] = new HelperController();

		/** @var class-string<IntegrationInterface>[] $integration_classes */
		$integration_classes = apply_filters(
            'app_bridge_integration_classes',
			[
				WooCommerceIntegration::class,
				DokanIntegration::class,
				WPERPIntegration::class,
				WPProjectManagerIntegration::class,
			]
        );

		foreach ( $integration_classes as $class ) {
			if ( ! $class::is_active() ) {
				continue;
			}

			$integration              = new $class();
			$this->integrations[]     = $integration;

			array_push( $this->hookables, ...$integration->hookables(), ...$integration->conditional_hookables() );
			array_push( $this->rest_controllers, ...$integration->rest_controllers() );

			$this->register_event_catalogs( $integration );
		}
	}

	/**
	 * Feed each app's event catalog into the EventRegistry filter so the
	 * dispatcher can resolve `<app>.<event_id>` keys for active integrations.
	 *
	 * Also wires the integration's per-app override option key into the
	 * `app_bridge_notification_option_keys` filter so EventRegistry reads
	 * each app's toggle/title/body overrides from the right wp_option, and
	 * registers a cache flush whenever that option is updated.
	 *
	 * `event_catalogs()` is invoked lazily inside the filter closure since the
	 * catalog values run `__()` — calling them at `plugins_loaded` would trip
	 * WP 6.7's just-in-time textdomain notice.
	 */
	private function register_event_catalogs( IntegrationInterface $integration ): void {
		add_filter(
			'app_bridge_event_catalogs',
			static function ( array $catalogs ) use ( $integration ): array {
				foreach ( $integration->event_catalogs() as $app => $categories ) {
					$app              = (string) $app;
					$catalogs[ $app ] = array_merge( $catalogs[ $app ] ?? [], (array) $categories );
				}

				return $catalogs;
			}
		);

		$option_keys = $integration->event_option_keys();

		add_filter(
			'app_bridge_notification_option_keys',
			static function ( array $keys ) use ( $option_keys ): array {
				foreach ( $option_keys as $app => $option_key ) {
					$option_key = (string) $option_key;
					if ( '' === $option_key ) {
						continue;
					}
					$keys[ (string) $app ] = $option_key;
				}

				return $keys;
			}
		);

		foreach ( $option_keys as $option_key ) {
			$option_key = (string) $option_key;
			if ( '' === $option_key ) {
				continue;
			}

			add_action(
				"update_option_{$option_key}",
				static function () {
					EventRegistry::flush();
				}
			);
		}
	}

	// ── Core hooks ──

	private function register_hooks(): void {
		add_action( 'init', [ $this, 'localization_setup' ] );
		add_action( 'init', [ $this, 'define_jwt_auth_secret_key' ] );
		add_action( 'rest_api_init', [ $this, 'load_rest_api' ], 9 );

		add_action( 'plugins_loaded', [ $this, 'early_user_resolution' ], 1 );
	}

	public function load_rest_api(): void {
		foreach ( $this->rest_controllers as $controller ) {
			if ( method_exists( $controller, 'register_routes' ) ) {
				$controller->register_routes();
			}
		}
	}

	public function localization_setup(): void {
		load_plugin_textdomain( 'app-bridge', false, dirname( plugin_basename( APP_BRIDGE_FILE ) ) . '/languages/' );
	}

	public function define_jwt_auth_secret_key(): void {
		if ( defined( 'JWT_AUTH_SECRET_KEY' ) ) {
			return;
		}

		$secret = (string) get_option( JwtAuth::SECRET_KEY_OPTION, '' );

		if ( '' !== $secret ) {
			define( 'JWT_AUTH_SECRET_KEY', $secret );
		}
	}

	public function early_user_resolution(): void {
		if ( empty( $_SERVER['REQUEST_URI'] ) || ! isset( $_GET['dokan_app_token'] ) ) { // phpcs:ignore
			return;
		}

		$rest_prefix = trailingslashit( rest_get_url_prefix() );
		$request_uri = esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) );

		if ( false === strpos( $request_uri, $rest_prefix ) || strpos( $request_uri, 'token/validate' ) > 0 ) {
			return;
		}

		do_action( 'determine_current_user', get_current_user_id() );
	}

	// ── WooCommerce compatibility ──

	public function declare_woocommerce_feature_compatibility(): void {
		if ( class_exists( FeaturesUtil::class ) ) {
			FeaturesUtil::declare_compatibility( 'custom_order_tables', APP_BRIDGE_FILE, true );
			FeaturesUtil::declare_compatibility( 'cart_checkout_blocks', APP_BRIDGE_FILE, true );
		}
	}

	// ── Constants ──

	private function define_constants(): void {
		if ( ! defined( 'APP_BRIDGE_VERSION' ) ) {
			define( 'APP_BRIDGE_VERSION', $this->version );
		}

		if ( ! defined( 'APP_BRIDGE_DIR' ) ) {
			define( 'APP_BRIDGE_DIR', plugin_dir_path( APP_BRIDGE_FILE ) );
		}

		if ( ! defined( 'APP_BRIDGE_ASSETS' ) ) {
			define( 'APP_BRIDGE_ASSETS', plugins_url( '/assets', APP_BRIDGE_FILE ) );
		}
	}
}
