<?php

namespace Planaday\Platform\WordpressConfig;

use Planaday\Api\ApiConnector;
use Planaday\Api\LoggingApi;
use Planaday\Api\Mock\MockConnector;
use Planaday\Config\Course\Calendar\CalendarSettings;
use Planaday\Config\Course\Detail\DetailSettings;
use Planaday\Config\General\Api\ApiSettings;
use Planaday\Config\General\Debug\DebugSettings;
use Planaday\Config\Internal\InternalSettings;
use Planaday\Entities\PostEntities;
use Planaday\Payment\Interfaces\PaymentHandler;
use Planaday\Platform\PlatformAbstraction\LocalizationController;
use Planaday\Platform\PlatformAbstraction\PlatformService;
use Planaday\Platform\WordpressConfig\AdminGUI\AdminPageEnum;
use Planaday\Platform\WordpressConfig\AdminGUI\AdminPages;
use Planaday\Platform\WordpressConfig\Blocks\BlockHandler;
use Planaday\Platform\WordpressConfig\Blocks\WordPressBlock;
use Planaday\Platform\WordpressConfig\Migrations\MigrationHandler;
use Planaday\Platform\WordpressConfig\ShortcodeLinks\CourseShortcodes;
use Planaday\Platform\WordpressConfig\ShortcodeLinks\TestingShortcodes;
use Planaday\Platform\WordpressConfig\Widgets\WPWidgetService;
use Planaday\Utility\Debug\Log\ErrorLogFileSaver;
use Planaday\Utility\Debug\Log\LogCompiler;
use Planaday\Utility\Debug\Log\Logging;
use Planaday\Utility\Debug\PluginFeatureTimer;
use Planaday\Utility\DependencyInjection\Container;
use Planaday\Utility\DependencyInjection\InjectionImpossibleException;
use Planaday\Utility\GeneralTermEnum;
use Planaday\Utility\PluginData;

class WPPlatformService implements PlatformService {
	use Logging;

	public function __construct(
		protected LocalizationController $localization,
		protected MigrationHandler $migrationHandler,
		protected PluginData $pluginData,
		protected CourseShortcodes $courseShortcodes,
		protected TestingShortcodes $testingShortcodes,
		protected Container $container,
		protected InternalSettings $internalSettings,
		protected BlockHandler $blockHandler,
		protected WPRestInitializer $restInitializer,
		protected PluginFeatureTimer $featureTimer
	) {}

	public function run(): void {
		$stop = $this->featureTimer->start("Platform service run");
		try {
			if ($this->logger) {
				$this->logger->logDebug = $this->container->getInstance(DebugSettings::class)->isSendDebugLogs();
				$this->logger->addLogListener($this->container->getInstance(LoggingApi::class));
				$this->logger->addLogListener($this->container->getInstance(ErrorLogFileSaver::class));
			}
		} catch (InjectionImpossibleException $e) {
			$this->logError('Failed to create logging tools, error:' . $e->getMessage(), $e);
		}

		$this->setupActions();
		$this->setupFilters();
		$this->setupShortcodes();
		$this->connectPaymentProvider();
		$stop();
	}

	private function setupActions(): void {
		add_action( 'init', $this->loadPluginTextDomain(...));
		add_action( 'init', $this->blockHandler->registerBlocks(...));
		add_action( 'init', $this->registerScripts(...));
		add_action( 'init', $this->enqueueStyle(...));
		add_action( 'init', $this->checkAndHandlePostRequest(...));
		add_action( 'widgets_init', $this->registerWidgets(...));
		add_action('rest_api_init', $this->restInitializer->registerEndpoints(...));
		add_action('wp_footer', $this->addLogger(...), PHP_INT_MAX);
		add_action('in_admin_footer', $this->addLogger(...), PHP_INT_MAX);

		add_action('elementor/editor/before_enqueue_scripts', $this->registerScripts(...));
		add_action('elementor/editor/before_enqueue_scripts', $this->enqueueStyle(...));


		if (is_admin()) {
			add_action( 'admin_enqueue_scripts', $this->editorScripts(...));
			add_action('admin_menu', $this->registerAdminMenus(...));
			add_action('admin_notices', $this->displayNotices(...));
		}
	}

	private function setupFilters(): void {
		add_filter('query_vars', $this->addQueryVars(...));
		add_filter('rewrite_rules_array', $this->addRewriteRules(...));

		if (is_admin()) {
			add_filter('plugin_action_links_' . $this->pluginData->getPluginFile(), $this->actionLinks(...));
		}
	}

	private function setupShortcodes(): void {
		$this->courseShortcodes->addShortcodes();
		$this->testingShortcodes->addShortcodes();
	}

	public function checkAndConnectTestApi(): void {
		try {
			$apiOptions = $this->container->getInstance(ApiSettings::class);
			if ($apiOptions->getApiKey() == null || $apiOptions->getDomain() === null || (
					$apiOptions->getApiKey() === 'test' &&
					$apiOptions->getDomain() === 'test'
				)
			){
				$this->logInfo("key and domain are not set, connecting to test api");
				$this->container->setImplementation(ApiConnector::class, MockConnector::class);
				$this->container->getInstance(ApiConnector::class)->connect();
			} else {
				$this->logInfo("key and domain are set, not connecting to test api");
			}
		} catch (InjectionImpossibleException $e) {
			$this->logError('failed to create General Options, error:' . $e->getMessage(), $e);
		}
	}

	private function connectPaymentProvider(): void {
		try {
			$this->container->getInstance(PaymentHandler::class)->connectPaymentProvider();
		} catch ( InjectionImpossibleException $e ) {
			$this->logError('failed to create PaymentHandler, error:' . $e->getMessage(), $e);
		}
	}

	public function checkAndHandlePostRequest(): void {
		try {
			$stop = $this->featureTimer->start("Check and handle post request");
			$this->container->getInstance(PostEntities::class)->checkAndHandlePostRequest();
			$stop();
		} catch (InjectionImpossibleException $e){
			$this->logError('failed to inject dependencies, error:' . $e->getMessage(), $e);
		}
	}

	public function registerAdminMenus(): void {
		try {
			$stop = $this->featureTimer->start("Register admin menus");
			$this->container->getInstance(AdminPages::class)->registerMenus();
			$stop();
		} catch (InjectionImpossibleException $e){
			$this->logError("failed to create AdminPages, error:" . $e->getMessage(), $e);
		}
	}

	public function addLogger(): void {
		$this->logInfo('Shutdown started');

		$this->localization->logAllSourceStrings();
		try {
			if( !wp_is_json_request() && !wp_doing_ajax() && !wp_is_jsonp_request() && !wp_is_xml_request() && !wp_is_rest_endpoint()) {
				echo $this->container->getInstance(LogCompiler::class)->getScript();
			}
		} catch (InjectionImpossibleException $e){
			echo "<script> console.log('failed to create LogCompiler: " . $e->getMessage() . "') </script>";
		}
	}

	public function registerScripts(): void {
		$stop = $this->featureTimer->start("Register scripts");
		wp_enqueue_script( 'jquery-ui-tabs' );
		wp_enqueue_script( $this->pluginData->getFullPrefix() . '-functions', plugins_url( dirname( $this->pluginData->getPluginFile() ) . '/assets/js/pad-functions.js' ));

		try {
			$calendarVersion = $this->container->getInstance(CalendarSettings::class)->getCalendarVersion();
			$includeFontAwesome = $this->container->getInstance(DebugSettings::class)->isEnableFontAwesome();
		} catch (InjectionImpossibleException ) {
			$calendarVersion = 0;
			$includeFontAwesome = true;
		}

		if ($includeFontAwesome) {
			wp_enqueue_script( $this->pluginData->getFullPrefix() . '-fa', plugins_url( dirname( $this->pluginData->getPluginFile() ) . '/assets/js/fontawesome.min.js' ), array(), '6.7.2', true );
		}

		switch ($calendarVersion){
			case 0:
				wp_register_script( $this->pluginData->getFullPrefix() . '-calendar-v3', plugins_url( dirname( $this->pluginData->getPluginFile() ) . '/assets/js/full-calendar/v3/fullcalendar.min.js' ), array( 'jquery', 'moment' ), '3' );
				wp_enqueue_script( $this->pluginData->getFullPrefix() . '-calendar-v3');
				break;
			case 1:
				wp_register_script( $this->pluginData->getFullPrefix() . '-calendar-v6', plugins_url( dirname( $this->pluginData->getPluginFile() ) . '/assets/js/full-calendar/v6/fullcalendar.min.js' ), array( 'jquery' ), '6.1.15' );
				wp_enqueue_script( $this->pluginData->getFullPrefix() . '-calendar-v6' );
				break;
		}
		$stop();
	}

	public function enqueueStyle(): void {
		$stop = $this->featureTimer->start("Enqueue style");
		wp_enqueue_style( $this->pluginData->getFullPrefix() . '-calendar_style', plugins_url( dirname( $this->pluginData->getPluginFile() ) . '/assets/css/fullcalendar.min.css' ), false, '6.0');
		wp_enqueue_style( $this->pluginData->getFullPrefix() . '-style', plugins_url( dirname( $this->pluginData->getPluginFile() ) . '/assets/css/planaday-style.css' ), false, '8.6' );
		wp_enqueue_style( $this->pluginData->getFullPrefix() . '-parsley', plugins_url( dirname( $this->pluginData->getPluginFile() ) . '/assets/css/parsley.css' ), false, '6.0');
		wp_enqueue_style( $this->pluginData->getFullPrefix() . '-jquery-tabs', plugins_url( dirname( $this->pluginData->getPluginFile() ) . '/assets/css/jquery-tabs.css' ), false, '6.0');

		wp_register_style(
			$this->pluginData->getFullPrefix() . '-form-builder-style',
			plugins_url( dirname( $this->pluginData->getPluginFile() ) . '/src/Planaday/Html/JSModules/build/FormBuilder/FormBuilder.css' )
		);

		wp_register_style(
            $this->pluginData->getFullPrefix() . '-global-style',
			false
		);
		wp_enqueue_style( $this->pluginData->getFullPrefix() . '-global-style' );

		$content = $this->internalSettings->getGlobalCss();
		$content = wp_kses($content, []);
		$content     = str_replace( '&gt;', '>', $content );
		wp_add_inline_style($this->pluginData->getFullPrefix() . '-global-style', $content);
		$stop();
	}

	public function registerWidgets(): void {
		try {
			$stop = $this->featureTimer->start("Register widgets");
			$this->container->getInstance(WPWidgetService::class)->registerWidgets();
			$stop();
		} catch (InjectionImpossibleException $e){
			$this->logError("Failed to create WPWidgetService, error:" . $e->getMessage(), $e);
		}
	}

	public function editorScripts(): void {
		$stop = $this->featureTimer->start("Editor scripts");
		wp_enqueue_style( $this->pluginData->getFullPrefix() . '-css-editor-css', plugins_url( dirname( $this->pluginData->getPluginFile() ) . '/assets/css/editor.css' ) );
		wp_enqueue_code_editor( array( 'type' => 'text/html' ) );
		wp_enqueue_script( $this->pluginData->getFullPrefix() . '-css-editor-js', plugins_url( dirname( $this->pluginData->getPluginFile() ) . '/assets/js/editor.js' ), array( 'jquery' ), '4.0.0', true );
		$stop();
	}

	public function loadPluginTextDomain(): void {
		$stopLoadTextDomain = $this->featureTimer->start("Load plugin text domain");
		load_plugin_textdomain( $this->pluginData->getFullPrefix(), false, dirname( $this->pluginData->getPluginFile() ) . '/languages' );
		$stopLoadTextDomain();
	}

	/**
	 * @param $aVars
	 * @return array
	 */
	public function addQueryVars($aVars):array
	{
		$aVars[] = $this->pluginData->getShortPrefix() . GeneralTermEnum::COURSE_ID_URL_QUERY;
		$aVars[] = $this->pluginData->getShortPrefix() . GeneralTermEnum::BOOKING_UUID_URL_QUERY;
		$aVars[] = $this->pluginData->getShortPrefix() . GeneralTermEnum::BOOKING_PASSCODE_URL_QUERY;
		return $aVars;
	}

	/**
	 * @param $aRules
	 * @return array
	 */
	public function addRewriteRules($aRules):array
	{
		$stop = $this->featureTimer->start("Add rewrite rules");
		try {
			$overriddenPage = $this->container->getInstance(DetailSettings::class)->getOverriddenDetailPage();
		} catch (InjectionImpossibleException ){
			$overriddenPage = 0;
		}

		$courseShortcode = $this->pluginData->getShortPrefix() . CourseShortcodes::COURSE_OVERVIEW_SHORTCODE;
		$courseBlock = $this->pluginData->getFullPrefix() . '/' . WordPressBlock::CourseDetails->value;
		$pages = get_pages();
		$this->logDebug('Found ' . count($pages) . ' pages with shortcode ' . $courseShortcode);

		$aNewRules = [];
		foreach ($pages as $page) {
			if (has_shortcode($page->post_content, $courseShortcode) || has_block($courseBlock, $page) || $overriddenPage === ($page->ID ?? null)) {
				$post_name = $page->post_name;
				$newRule = 'index.php?pagename=' . ($post_name ?? '') . '&' . $this->pluginData->getShortPrefix() . GeneralTermEnum::COURSE_ID_URL_QUERY . '=$matches[1]';
				$this->logInfo('Adding rewrite rule: ' . print_r($newRule, true));
				$aNewRules[($post_name ?? '') . '/(\d+)/([^/]*)/?'] = $newRule;
			}
		}

		$this->logInfo('Adding rewrite rules: ' . print_r($aNewRules + $aRules, true));

		$stop();
		return $aNewRules + $aRules;
	}

	/**
	 * @param string[] $links
	 *
	 * @return string[]
	 */
	public function actionLinks( array $links):array
	{
		array_unshift($links, '<a href="https://wordpress.org/support/plugin/planaday-connector/reviews/">' . $this->localization->translate('Laat een beoordeling achter') . '</a>');
		array_unshift($links, '<a href="' . esc_url(admin_url('admin.php?page=' . $this->pluginData->getFullPrefix() . AdminPageEnum::PLANADAY_ADMIN->value)) . '">' . $this->localization->translate('instellingen') . '</a>');
		return $links;
	}

	public function UpdateCurrentVersion():void {
		$lastInstalledVersion = get_option( $this->pluginData->getFullPrefix() . '-version', '0.0.0');
		$databaseVersion = explode('.', $lastInstalledVersion);

		$this->migrationHandler->migrate($databaseVersion);

		update_option($this->pluginData->getFullPrefix() . '-version', implode('.', $this->pluginData->getPluginVersion()));
	}

	public function displayNotices(): void {
		$stop = $this->featureTimer->start("Display notices");
		$phpVersion = explode('.', phpversion());

		if (((int)$phpVersion[0] < 8 || (int)$phpVersion[0] == 8 && (int)$phpVersion[1] < 1)) {
			echo '<div class="update-nag notice notice-warning inline planaday-admin-notice"><img src="' . plugins_url( dirname( $this->pluginData->getPluginFile() ) . '/assets/logobreed.png') . '" alt="Planaday Logo"><p>De volgende versie van de Planaday Connector zal een php versie van 8.1 of nieuwer vereisen. Jouw website gebruikt nog PHP ' . phpversion() . ', om te zien hoe je kan updaten klik <a href="https://wordpress.org/support/update-php/" target="_blank">hier</a>.</p></div>';
		}
		$stop();
	}

	public function queueApiRefresh(): void {
		add_action('wp_footer', $this->insertRefreshScript(...));
		add_action('in_admin_footer', $this->insertRefreshScript(...));
	}

	public function insertRefreshScript(): void {
		if( !wp_is_json_request() && !wp_doing_ajax() && !wp_is_jsonp_request() && !wp_is_xml_request() && !wp_is_rest_endpoint()) {
			$url = rest_url( $this->pluginData->getFullPrefix() . '/v1/refresh/tryFull' );
			echo '<script>fetch("' . $url . '");</script>';
		}
	}
}