<?php

declare( strict_types=1 );

namespace WPDesk\ShopMagic;

use ShopMagicVendor\Psr\Container\ContainerInterface;
use ShopMagicVendor\DI\ContainerBuilder;
use ShopMagicVendor\WPDesk\Migrations\Migrator;
use ShopMagicVendor\WPDesk\PluginBuilder\Plugin\AbstractPlugin;
use ShopMagicVendor\WPDesk\PluginBuilder\Plugin\Activateable;
use ShopMagicVendor\WPDesk\PluginBuilder\Plugin\Deactivateable;
use ShopMagicVendor\WPDesk\PluginBuilder\Plugin\HookableCollection;
use ShopMagicVendor\WPDesk\PluginBuilder\Plugin\HookableParent;
use ShopMagicVendor\WPDesk\ShowDecision\PostTypeStrategy;
use ShopMagicVendor\WPDesk_Plugin_Info;
use WPDesk\ShopMagic\Admin\Admin;
use WPDesk\ShopMagic\Admin\RateNotice\RateNotices;
use WPDesk\ShopMagic\Admin\RateNotice\TwoWeeksNotice;
use WPDesk\ShopMagic\Admin\Settings\GeneralSettings;
use WPDesk\ShopMagic\Admin\Settings\ModulesSettings;
use WPDesk\ShopMagic\Admin\Welcome\Welcome;
use WPDesk\ShopMagic\Components\HookProvider\Conditional;
use WPDesk\ShopMagic\Components\Routing\RestRoutesRegistry;
use WPDesk\ShopMagic\Components\Routing\WpRoutesRegistry;
use WPDesk\ShopMagic\Customer\Guest\Interceptor\BackgroundOrderInterceptor;
use WPDesk\ShopMagic\Helper\PluginBag;
use WPDesk\ShopMagic\Helper\TemplateResolver;
use WPDesk\ShopMagic\Helper\WordPressPluggableHelper;
use WPDesk\ShopMagic\Integration\ExternalPluginsAccess;
use WPDesk\ShopMagic\Modules\Module;
use WPDesk\ShopMagic\Modules\Mulitilingual\MultilingualModule;
use WPDesk\ShopMagic\Workflow\Action\Action;
use WPDesk\ShopMagic\Workflow\Automation\AutomationPostType;
use WPDesk\ShopMagic\Workflow\Event\Builtin\Order\OrderPending;
use WPDesk\ShopMagic\Workflow\Event\Event;
use WPDesk\ShopMagic\Workflow\Event\EventMutex;
use WPDesk\ShopMagic\Workflow\Extensions\Builtin\CoreExtension;
use WPDesk\ShopMagic\Workflow\Extensions\Builtin\FlexibleCheckoutFieldsExtension;
use WPDesk\ShopMagic\Workflow\Extensions\Builtin\FlexibleShippingExtension;
use WPDesk\ShopMagic\Workflow\Extensions\Builtin\PaidPromotionExtension;
use WPDesk\ShopMagic\Workflow\Extensions\Builtin\StagingExtension;
use WPDesk\ShopMagic\Workflow\Extensions\Builtin\WooCommerceExtension;
use WPDesk\ShopMagic\Workflow\Extensions\ExtensionsSet;
use WPDesk\ShopMagic\Workflow\Filter\ComparisonType\ComparisonType;
use WPDesk\ShopMagic\Workflow\Filter\Filter;
use WPDesk\ShopMagic\Workflow\Placeholder\Placeholder;
use WPDesk\ShopMagic\Workflow\Placeholder\TemplateRendererForPlaceholders;
use WPDesk\ShopMagic\Workflow\Queue\Queue;
use WPDesk\ShopMagic\Workflow\Validator\FailingLanguageValidator;
use WPDesk\ShopMagic\Workflow\WorkflowInitializer;

/**
 * Main plugin class. The most important flow decisions are made here.
 */
final class Plugin extends AbstractPlugin implements HookableCollection, Activateable, Deactivateable {
	use HookableParent;

	private const ADDONS = [
		'shopmagic-advanced-filters/shopmagic-advanced-filters.php',
		'shopmagic-customer-coupons/shopmagic-customer-coupons.php',
		'shopmagic-delayed-actions/shopmagic-delayed-actions.php',
		'shopmagic-for-gravity-forms/shopmagic-for-gravity-forms.php',
		'shopmagic-manual-actions/shopmagic-manual-actions.php',
		'shopmagic-reviews/shopmagic-reviews.php',
		'shopmagic-slack/shopmagic-slack.php',
		'shopmagic-woocommerce-bookings/shopmagic-woocommerce-bookings.php',
		'shopmagic-woocommerce-memberships/shopmagic-woocommerce-memberships.php',
		'shopmagic-woocommerce-subscriptions/shopmagic-woocommerce-subscriptions.php',
	];

	/** @var array<int, class-string<Module>> */
	private const MODULES = [
		MultilingualModule::class,
	];

	/** @var WPDesk_Plugin_Info */
	protected $plugin_info;

	/** @var ContainerInterface */
	private $container;

	/** @var array<string, Module> */
	private $modules = [];

	public function __construct( WPDesk_Plugin_Info $plugin_info ) {
		/** @noinspection PhpParamsInspection */
		parent::__construct( $plugin_info );

		$this->set_class_aliases();

		TemplateResolver::set_root_path( $plugin_info->get_plugin_dir() );
	}

	private function set_class_aliases(): void {
		class_alias( self::class, 'ShopMagic' );
		class_alias( Placeholder::class, \WPDesk\ShopMagic\Placeholder\BasicPlaceholder::class );
		class_alias( Event::class, \WPDesk\ShopMagic\Event\BasicEvent::class );
		class_alias( Action::class, \WPDesk\ShopMagic\Action\BasicAction::class );
		class_alias( Filter::class, \WPDesk\ShopMagic\Filter\BasicFilter::class );
		class_alias( ComparisonType::class, WPDesk\ShopMagic\Filter\ComparisionType\ComparisionType::class );
		class_alias(
			\ShopMagicVendor\WPDesk\Forms\Field\CheckboxField::class,
			\WPDesk\ShopMagic\FormField\Field\CheckboxField::class
		);
		class_alias(
			\ShopMagicVendor\WPDesk\Forms\Field\Header::class,
			\WPDesk\ShopMagic\FormField\Field\Header::class
		);
		class_alias(
			\ShopMagicVendor\WPDesk\Forms\Field\HiddenField::class,
			\WPDesk\ShopMagic\FormField\Field\HiddenField::class
		);
		class_alias(
			\ShopMagicVendor\WPDesk\Forms\Field\InputTextField::class,
			\WPDesk\ShopMagic\FormField\Field\InputTextField::class
		);
		class_alias(
			\ShopMagicVendor\WPDesk\Forms\Field\MultipleInputTextField::class,
			\WPDesk\ShopMagic\FormField\Field\MultipleInputTextField::class
		);
		class_alias(
			\ShopMagicVendor\WPDesk\Forms\Field\ImageInputField::class,
			\WPDesk\ShopMagic\FormField\Field\ImageInputField::class
		);
		class_alias(
			\ShopMagicVendor\WPDesk\Forms\Field\NoOnceField::class,
			\WPDesk\ShopMagic\FormField\Field\NoOnceField::class
		);
		class_alias(
			\ShopMagicVendor\WPDesk\Forms\Field\NoValueField::class,
			\WPDesk\ShopMagic\FormField\Field\NoValueField::class
		);
		class_alias(
			\ShopMagicVendor\WPDesk\Forms\Field\Paragraph::class,
			\WPDesk\ShopMagic\FormField\Field\Paragraph::class
		);
		class_alias(
			\ShopMagicVendor\WPDesk\Forms\Field\ProductSelect::class,
			\WPDesk\ShopMagic\FormField\Field\ProductSelect::class
		);
		class_alias(
			\ShopMagicVendor\WPDesk\Forms\Field\RadioField::class,
			\WPDesk\ShopMagic\FormField\Field\RadioField::class
		);
		class_alias(
			\ShopMagicVendor\WPDesk\Forms\Field\SelectField::class,
			\WPDesk\ShopMagic\FormField\Field\SelectField::class
		);
		class_alias(
			\ShopMagicVendor\WPDesk\Forms\Field\SubmitField::class,
			\WPDesk\ShopMagic\FormField\Field\SubmitField::class
		);
		class_alias(
			\ShopMagicVendor\WPDesk\Forms\Field\TextAreaField::class,
			\WPDesk\ShopMagic\FormField\Field\TextAreaField::class
		);
		class_alias(
			\ShopMagicVendor\WPDesk\Forms\Field\WooSelect::class,
			\WPDesk\ShopMagic\FormField\Field\WooSelect::class
		);
		class_alias(
			\ShopMagicVendor\WPDesk\Forms\Field\WyswigField::class,
			\WPDesk\ShopMagic\FormField\Field\WyswigField::class
		);

		class_alias(
			\ShopMagicVendor\WPDesk\Forms\Field\NoValueField::class,
			\WPDesk\ShopMagic\FormField\NoValueField::class
		);
		class_alias(
			\ShopMagicVendor\WPDesk\Forms\Field\BasicField::class,
			\WPDesk\ShopMagic\FormField\BasicField::class
		);
	}

	private function pre_init(): void {
		$this->initialize_modules();
		$this->initialize_container();
	}

	public function init(): void {
		$this->pre_init();

		$this->container->get( Migrator::class )->migrate();

		parent::init();
	}

	/**
	 * Check if any of ShopMagic PRO add-ons is active
	 */
	private function is_pro_active(): bool {
		foreach ( self::ADDONS as $addon ) {
			if ( WordPressPluggableHelper::is_plugin_active( $addon ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Returns true when debug mode is on.
	 */
	private function is_debug_mode(): bool {
		$enable_log = GeneralSettings::get_option( 'sm_enable_logs' );
		if ( $enable_log === '1' || $enable_log === true ) {
			return true;
		}
		$helper_options = get_option( 'wpdesk_helper_options', [] );

		return isset( $helper_options['debug_log'] ) && '1' === $helper_options['debug_log'];
	}

	/**
	 * Integrate with WordPress and with other plugins using action/filter system.
	 */
	public function hooks(): void {
		parent::hooks();

		// $pro_is_active = $this->is_pro_active();
		// require_once __DIR__ . '/Admin/Welcome/Popups.php';

		add_action(
			'plugins_loaded',
			function (): void {
				foreach ( $this->modules as $module ) {
					$module->set_container( $this->container );
					$module->initialize();
				}

				OrderPending::initialize_pending_on_created_check( $this->container->get( Queue::class ) );

				foreach ( $this->container->get( 'hookable.plugins_loaded' ) as $item ) {
					$item->hooks();
				}
			}
		);

		add_action(
			'action_scheduler_init',
			function (): void {
				$this->container->get( BackgroundOrderInterceptor::class )
						->start_guest_extraction_if_needed();
			}
		);

		add_action(
			'rest_api_init',
			function () {
				$routes       = require $this->plugin_info->get_plugin_dir() . '/config/routes/api.php';
				$configurator = $this->container->get( 'routesConfigurator.api' );
				$registry     = $this->container->get( RestRoutesRegistry::class );
				$routes( $configurator );
				do_action( 'shopmagic/core/rest/init' );
				$registry->hooks();
			}
		);

		add_action(
			'init',
			function (): void {
				$extensions_set = $this->container->get( ExtensionsSet::class );
				$this->register_extensions( $extensions_set );

				// FIXME: this should belong to services definition as
				// it is the default behavior, but due to stateful
				// nature of WorkflowValidator we have to do this as
				// a trick, until fixed.
				$this->container->get( WorkflowInitializer::class )->add_validator( $this->container->get( FailingLanguageValidator::class ) );

				/**
				 * If you want to write an integration with ShopMagic you should use this action.
				 * This action is executed when the ShopMagic core is ready to be used and provides a ExternalPluginsAccess object to facilitate integration.
				 * Please make sure that your integration is checking $plugin_access->get_version() to ensure that your integration is compatible with current ShopMagic version.
				 * Remember that we use semantic versioning so can be sure that every time we make a breaking change we also increase a major version of the plugin.
				 *
				 * @param ExternalPluginsAccess $plugin_access Object with various tools that can be used for integration.
				 */
				do_action(
					'shopmagic/core/initialized/v2',
					$this->container->get( ExternalPluginsAccess::class )
				);

				$resolver = $this->container->get( TemplateResolver::class );
				$resolver->add_resolver( $this->container->get( 'resolver.default' ) );

				foreach ( $this->container->get( 'hookable.init' ) as $item ) {
					if ( $item instanceof Conditional && ! $item::is_needed() ) {
						continue;
					}
					$item->hooks();
				}

				$this->register_public_routes();

				do_action(
					'shopmagic/core/after_initialization',
					$this->container->get( ExtensionsSet::class )
				);

				$automations = $this->container->get( WorkflowInitializer::class )
						->initialize_active_automations();

				if ( is_admin() ) {
					// FIXME: This should land in DI container. BTW, it doesn't work either, as
					// we dont have AutomationPostType page in admin (due to JS app).
					( new RateNotices(
						[
							new TwoWeeksNotice(
								$this->plugin_url . '/assets',
								$automations,
								new PostTypeStrategy( AutomationPostType::TYPE )
							),
						]
					) )->hooks();
				}
			},
			9
		);

		add_action(
			'flexible_checkout_fields/init',
			function ( $integrator ): void {
				$extensions_set = $this->container->get( ExtensionsSet::class );
				$fcf_extension  = new FlexibleCheckoutFieldsExtension( $integrator, $this->container->get( 'logger' ) );
				$extensions_set->add_extension( $fcf_extension );
			}
		);

		if ( is_admin() ) {
			$this->container->get( Admin::class )->hooks();
		}

		$this->hooks_on_hookable_objects();
	}

	private function register_extensions( ExtensionsSet $extensions ): void {
		$extensions->add_extension( $this->container->get( CoreExtension::class ) );

		if ( WordPressPluggableHelper::is_plugin_active( 'woocommerce/woocommerce.php' ) ) {
			$extensions->add_extension(
				new WooCommerceExtension(
					new EventMutex(),
					$this->container->get( TemplateRendererForPlaceholders::class )
				)
			);
		}

		$extensions->add_extension( new PaidPromotionExtension( $this->is_pro_active() ) );
		$extensions->add_extension( new FlexibleShippingExtension( $this->container->get( TemplateRendererForPlaceholders::class ) ) );
		$extensions->add_extension( new StagingExtension() );
	}

	private function register_public_routes(): void {
		$routes   = require $this->plugin_info->get_plugin_dir() . '/config/routes/public.php';
		$registry = $this->container->get( WpRoutesRegistry::class );
		$routes( $this->container->get( 'routesConfigurator.public' ) );
		$registry->hooks();
	}

	/**
	 * Quick links on plugins page.
	 *
	 * @param string[] $links .
	 *
	 * @return string[]
	 * @internal
	 */
	public function links_filter( $links ): array {
		$plugin_links = [];

		$plugin_links[] = '<a href="' . admin_url( 'admin.php?page=shopmagic-admin#/settings' ) . '">' . __(
			'Settings',
			'shopmagic-for-woocommerce'
		) . '</a>';
		$plugin_links[] = '<a href="https://shopmagic.app/sk/shopmagic-for-woocommerce-docs/" target="_blank">' . __(
			'Docs',
			'shopmagic-for-woocommerce'
		) . '</a>';
		$plugin_links[] = '<a href="https://wpdesk.link/shopmagic-for-woocommerce-support/" target="_blank">' . __(
			'Support',
			'shopmagic-for-woocommerce'
		) . '</a>';

		if ( ! $this->is_pro_active() ) {
			$plugin_links[] = '<a href="https://shopmagic.app/sk/shopmagic-for-woocommerce-list-pro" target="_blank" style="color:#d64e07;font-weight:bold;">' . __(
				'Buy PRO',
				'shopmagic-for-woocommerce'
			) . '</a>';
		}

		return array_merge( $plugin_links, $links );
	}

	public function activate(): void {
		( new Welcome() )->welcome_activate();
	}

	public function deactivate(): void {
		( new Welcome() )->welcome_deactivate();
	}

	public function build_container(): ContainerBuilder {
		$builder = new ContainerBuilder();

		$plugin = new PluginBag();
		$plugin->set_plugin_info( $this->plugin_info );
		$plugin->set_debug( $this->is_debug_mode() );
		$plugin->set_pro( $this->is_pro_active() );

		$builder->addDefinitions(
			$this->plugin_info->get_plugin_dir() . '/config/services.inc.php',
			[ PluginBag::class => $plugin ]
		);

		// Compilation is disabled for now because new implementation of modules requires us to.
		// We cannot compile container ahead as modules are conditionally included.
		// if ( ! getenv( 'DEVELOPMENT' ) ) {
		// $builder->enableCompilation( __DIR__ . '/../cache' );
		// }

		$this->prepare_container( $builder );

		return $builder;
	}

	/**
	 * @return void
	 */
	public function initialize_modules(): void {
		$modules_info = ModulesSettings::get_settings_persistence();
		foreach ( self::MODULES as $module ) {
			$new_module = new $module();
			if ( $modules_info->has( $new_module->get_name() ) ) {
				$this->modules[] = $new_module;
			}
		}
	}

	/**
	 * @return void
	 * @throws \Exception
	 */
	public function initialize_container(): void {
		$builder = $this->build_container();

		$this->container = $builder->build();
	}

	private function prepare_container( ContainerBuilder $builder ): void {
		foreach ( $this->modules as $module ) {
			$module->build( $builder );
		}
	}
}
