<?php
declare( strict_types=1 );

namespace FiboFilters\Config;

use FiboFilters\Filters\FiltersDebugger;
use FiboFilters\Settings\Settings;
use Freemius;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Class AssetsLoader
 */
class AssetsLoader {
	/** @var array Output data */
	private $output;

	/** @var Freemius */
	private $freemius;

	/** @var FiltersDebugger */
	private $filters_debugger;

	/** @var Settings */
	private $settings;

	public function __construct(
		Freemius $freemius,
		Settings $settings,
		FiltersDebugger $filters_debugger
	) {
		$this->freemius         = $freemius;
		$this->settings         = $settings;
		$this->filters_debugger = $filters_debugger;
	}

	public function enqueue_front_assets( array $output, bool $has_active_filters ): void {
		$this->output = $output;

		// Break early if "nofibofilters" = 1 || 2.
		if (
			isset( $_GET['nofibofilters'] ) && in_array( $_GET['nofibofilters'], [ '1', '2' ], true ) || // phpcs:ignore WordPress.Security.NonceVerification.Recommended
			in_array( apply_filters( 'fibofilters/nofibofilters', '' ), [ '1', '2' ], true )
		) {
			return;
		}

		do_action( 'fibofilters/before_enqueue_front_assets', $output );

		if ( is_product() && ( $this->output['config']['pagination_strategy'] === 'show-more-button' || $this->output['config']['pagination_strategy'] === 'infinite-scroll' ) ) {
			wp_register_script( 'fibofilters-single-product-handle', '', [], Env::$version, true );
			wp_enqueue_script( 'fibofilters-single-product-handle' );
			ob_start();
			?>
			<script>
				try {
					sessionStorage.setItem( 'fibofilters-last-page', 'product' );
				} catch ( e ) {}
			</script>
			<?php
			wp_add_inline_script(
				'fibofilters-single-product-handle',
				trim( str_replace( [ '<script>', '</script>' ], '', (string) ob_get_clean() ) )
			);
		}

		$this->enqueue_precache_descriptors_script();

		if ( empty( $this->output['context']['show_filters'] ) ) {
			return;
		}

		$front_dir = 'front';

		if ( $this->freemius->is__premium_only() ) {
			$front_dir = 'front-pro';
		}

		if ( ! empty( $this->output ) ) {
			// Remove internal data from context.
			if ( isset( $this->output['context']['internal'] ) ) {
				unset( $this->output['context']['internal'] );
			}

			$minimum_capability          = apply_filters( 'fibofilters/shop_manager_has_access', false ) ? 'manage_woocommerce' : 'manage_options';
			$is_filters_debugger_enabled = $this->filters_debugger->is_enabled() && current_user_can( $minimum_capability );
			if ( $is_filters_debugger_enabled ) {
				$front_dir .= '-debug';
			}
			wp_enqueue_style( 'fibofilters', Env::$build_url . '/' . $front_dir . '/front.css', [], Env::$version );
			wp_enqueue_script( 'fibofilters', Env::$build_url . '/' . $front_dir . '/front.js', [], Env::$version, false );

			wp_add_inline_style(
				'fibofilters',
				"body{
				--fibofilters--colors--base-org: {$this->settings->get('appearance_base_color')};
				--fibofilters--colors--link-org: {$this->settings->get('appearance_link_color')};
				--fibofilters--colors--base: var(--fibofilters--colors--base-org);
				--fibofilters--colors--link: var(--fibofilters--colors--link-org);
				--fibofilters--colors-input-border: #767676;
			}"
			);

			if ( $has_active_filters ) {
				wp_add_inline_style(
					'fibofilters',
					'body{
			      .fibofilters-applied-filters-container { height: 46px; }
			      .fibofilters-applied-filters-container:empty {display:block; }
			}'
				);
			}

			if ( $this->output['config']['pagination_strategy'] === 'show-more-button' || $this->output['config']['pagination_strategy'] === 'infinite-scroll' ) {
				$style = '';
				if ( is_array( $this->output['config']['pagination_selectors'] ) ) {
					foreach ( $this->output['config']['pagination_selectors'] as $selector ) {
						$style .= sprintf( '%s {display: none !important;}', esc_attr( $selector ) );
					}
				}
				if ( ! empty( $style ) ) {
					wp_add_inline_style(
						'fibofilters',
						$style
					);
				}
			}

			if ( $this->output['config']['filter_location'] === 'above_products' && $this->output['config']['mobile_filters_enabled'] ) {
				$this->additional_styles();
			}

			$this->skeleton_style();

			$this->maybe_print_canonical();

			add_action(
				'wp_footer',
				function () {
					wp_print_inline_script_tag(
						wp_json_encode( $this->output ),
						[
							'type' => 'application/json',
							'id'   => 'fibofilters-data',
						]
					);
				},
				PHP_INT_MAX - 10
			);

			if ( $this->output['config']['early_products_caching'] ) {
				wp_register_script( 'fibofilters-early-products-caching', '', [], Env::$version, true );
				wp_enqueue_script( 'fibofilters-early-products-caching' );
				wp_add_inline_script(
					'fibofilters-early-products-caching',
					"window.fibofiltersPageBody = document.querySelector('body').cloneNode(true);"
				);
			}
		}
	}

	/**
	 * Enqueue precache descriptors script
	 *
	 * This is part of the Request.getStoreDescriptors script that precaches
	 * descriptors if they are not in the browser cache.
	 */
	private function enqueue_precache_descriptors_script(): void {
		if ( ! apply_filters( 'fibofilters/precache_descriptors', true ) ) {
			return;
		}

		// Break early if index is not valid.
		if ( isset( $this->output['index_valid'] ) && $this->output['index_valid'] === false ) {
			return;
		}

		$descriptors_endpoint      = $this->output['config']['descriptors_endpoint'] ?? false;
		$descriptors_json_url      = ! empty( $this->output['config']['descriptors_json_url'] ) ? $this->output['config']['descriptors_json_url'] : false;
		$descriptors_data_fake_url = $this->output['config']['descriptors_data_fake_url'] ?? false;
		$index_build_id            = $this->output['config']['index_build_id'] ?? false;
		$last_updated_date         = $this->output['config']['last_updated_date'] ?? false;

		if ( $descriptors_endpoint === false || $descriptors_json_url === false || $descriptors_data_fake_url === false || $index_build_id === false || $last_updated_date === false ) {
			return;
		}

		wp_register_script( 'fibofilters-prefetch-descriptors', '', [], Env::$version, false );
		wp_enqueue_script( 'fibofilters-prefetch-descriptors' );
		ob_start();
		?>
		<script>
			try {
				window.fibofiltersFetchingDescriptors = false;
				const fibofiltersCacheDescriptors = async () => {
					window.fibofiltersFetchingDescriptors = true;
					try {
						const fibofiltersDescriptorsUrl = '<?php echo esc_js( $descriptors_endpoint ); ?>';
						const fibofiltersDescriptorsJsonUrl = '<?php echo esc_js( $descriptors_json_url ); ?>';
						const fibofiltersDescriptorsUrlSuffix = '<?php echo ! empty( $last_updated_date ) && strpos( $descriptors_json_url, '.json' ) !== false ? sprintf( '?last-modified=%s', esc_js( str_replace( [ ' ', ':', '-' ], [ '', '', '' ], $last_updated_date ) ) ) : ''; ?>';
						const fibofiltersDescriptorsDataFakeUrl = '<?php echo esc_js( $descriptors_data_fake_url ); ?>';
						const fibofiltersCache = await caches.open('fibofilters');

						let response = await fibofiltersCache.match(fibofiltersDescriptorsUrl);
						let descriptorsDataResponse = await fibofiltersCache.match(fibofiltersDescriptorsDataFakeUrl);
						if (typeof descriptorsDataResponse === 'undefined' && typeof response === 'undefined') {
							fetch(fibofiltersDescriptorsJsonUrl + fibofiltersDescriptorsUrlSuffix).then(async (serverResponse) => {
								if (serverResponse.ok) {
									await fibofiltersCache.put( fibofiltersDescriptorsUrl, serverResponse );
									await fibofiltersCache.put(
										fibofiltersDescriptorsDataFakeUrl,
										new Response(
											JSON.stringify( { indexBuildId: '<?php echo esc_js( $index_build_id ); ?>', lastUpdatedDate: '<?php echo esc_js( $last_updated_date ); ?>' } )
										)
									);
								}
								window.fibofiltersFetchingDescriptors = false;
							}).catch(() => {
								window.fibofiltersFetchingDescriptors = false;
							});
						} else {
							window.fibofiltersFetchingDescriptors = false;
						}
					} catch (e) {
						window.fibofiltersFetchingDescriptors = false;
					}
				}

				fibofiltersCacheDescriptors();
			} catch ( e ) {}
		</script>
		<?php
		wp_add_inline_script(
			'fibofilters-prefetch-descriptors',
			trim( str_replace( [ '<script>', '</script>' ], '', (string) ob_get_clean() ) )
		);
	}

	private function additional_styles(): void {
		$mobile_breakpoint = $this->output['config']['mobile_breakpoint'];
		ob_start();
		?>
		<style>
			.fibofilters-horizontal-filters-container {
				display: none;
			}

			@media screen and ( min-width: <?php echo esc_attr( (string) ( $mobile_breakpoint + 1 ) ); ?>px ) {
				.fibofilters-horizontal-filters-container {
					min-height: 66px;
					display: block;
				}
				.fibofilters-horizontal-filters-container:empty {
					display: block;
				}
			}

			.fibofilters-mobile-filters-container {
				display: none;
			}

			@media screen and ( max-width: <?php echo esc_attr( $mobile_breakpoint ); ?>px ) {
				.fibofilters-mobile-filters-container {
					min-height: 66px;
					display: block;
				}
				.fibofilters-mobile-filters-container:empty {
					display: block;
				}
			}
		</style>
		<?php
		$css = (string) ob_get_clean();
		wp_add_inline_style(
			'fibofilters',
			str_replace( [ '<style>', '</style>' ], '', $css )
		);
	}

	private function skeleton_style() {
		if ( ! apply_filters( 'fibofilters/front/display_skeleton', true ) ) {
			return;
		}

		ob_start();
		require_once Env::$path . '/partials/views/front/skeleton.php';
		$css = (string) ob_get_clean();
		wp_add_inline_style(
			'fibofilters',
			str_replace( [ '<style>', '</style>' ], '', $css )
		);
	}

	/**
	 * Publish canonical link to the <head>.
	 */
	private function maybe_print_canonical(): void {
		$print_canonical = $this->are_filtering_params();
		$print_canonical = $print_canonical || is_paged();
		$print_canonical = apply_filters( 'fibofilters/print_canonical', $print_canonical );

		// Don't print canonical URL if there is no filtering params in the URL.
		if ( ! $print_canonical ) {
			return;
		}

		// Remove main the WordPress action that add canonical URL.
		remove_action( 'wp_head', 'rel_canonical' );

		add_action(
			'wp_head',
			function () {
				$url = $this->get_canonical_url();

				if ( ! empty( $url ) ) {
					echo '<link rel="canonical" href="' . esc_url( $url ) . '" />' . "\n";
				}
			}
		);
	}

	/**
	 * Generate canonical URL.
	 */
	private function get_canonical_url(): string {
		global $wp_rewrite;

		$paged       = get_query_var( 'paged' );
		$params      = $this->get_filtering_url_params();
		$remove_page = $this->are_filtering_params() && is_paged();
		$path        = add_query_arg( null, null );
		// Remove pagination from the URL if we have filtering params.
		if ( $remove_page ) {
			// TODO Sprawdzić wtyczki językowe.
			$path = str_replace( '/' . $wp_rewrite->pagination_base . '/' . $paged, '', $path );
		}
		$url_parts = wp_parse_url( home_url() );
		$url       = $url_parts['scheme'] . '://' . $url_parts['host'] . $path;

		foreach ( $params as $param ) {
			$url = remove_query_arg( $param, $url );
		}

		return $url;
	}

	/**
	 * Get all URL params that are related to filtering.
	 */
	private function get_fibofilters_url_params(): array {
		$params = [];
		if ( ! empty( $this->output['filters'] ) ) {
			$params = array_map(
				function ( $filter ) {
					return $filter['url_slug'];
				},
				$this->output['filters']
			);
		}

		return $params;
	}

	/**
	 * Get all URL params that are related to filtering.
	 */
	private function get_filtering_url_params(): array {
		$params = $this->get_fibofilters_url_params();

		// Other URL params related to filtering.
		$params[] = 'pr'; // Product ID.
		$params[] = 'orderby';
		$params[] = 'order';

		return apply_filters( 'fibofilters/url_params_related_to_filtering', $params );
	}

	/**
	 * Check if the current URL includes params that are related to filtering.
	 */
	private function are_filtering_params(): bool {
		$include = false;
		foreach ( $this->get_filtering_url_params() as $param ) {
			// phpcs:ignore WordPress.Security.NonceVerification.Recommended
			if ( array_key_exists( $param, $_GET ) ) {
				$include = true;
				break;
			}
		}

		return $include;
	}

	/**
	 * Enqueue inline script
	 *
	 * @param string     $js JavaScript code.
	 * @param array      $deps Dependencies.
	 * @param string     $handle_suffix Handle suffix.
	 * @param bool|array $args Arguments.
	 * @param int        $priority Priority.
	 */
	public function enqueue_inline_script( string $js, array $deps = [], string $handle_suffix = '', $args = true, int $priority = 15 ) {
		add_action(
			'wp_enqueue_scripts',
			function () use ( $js, $deps, $handle_suffix, $args ) {
				$handle = sprintf( 'fibofilters-integration%s', ! empty( $handle_suffix ) ? '-' . $handle_suffix : '' );
				wp_register_script( $handle, '', $deps, Env::$version, $args );
				wp_enqueue_script( $handle );
				wp_add_inline_script(
					$handle,
					trim( str_replace( [ '<script>', '</script>' ], '', $js ) )
				);

				// Exclude scripts from delayed loading in WP Rocket.
				if ( did_action( 'fibofilters/integrations/plugin/init/wp_rocket' ) ) {
					add_filter(
						'rocket_delay_js_exclusions',
						function ( $excluded ) use ( $handle, $deps ) {
							// Test if exclusion for FiboFilters is enabled in WP Rocket settings.
							$options = get_option( 'wp_rocket_settings', [] );
							if (
								! empty( $options['delay_js_exclusions_selected'] ) &&
								is_array( $options['delay_js_exclusions_selected'] ) &&
								// You can find hash for our plugin on this list:  https://b.rucss.wp-rocket.me/api/delay-js-exclusions/list.
								in_array( '081461cf-dbd0-42f3-9557-10cdc16cf145', $options['delay_js_exclusions_selected'], true )
							) {
								$excluded[] = $handle;

								if ( ! empty( $deps ) ) {
									$excluded = array_merge( $excluded, $deps );
								}
							}

							return $excluded;
						}
					);
				}
			},
			$priority
		);
	}

	/**
	 * Enqueue inline style
	 *
	 * @param string $css CSS.
	 */
	public function enqueue_inline_style( $css = '' ) {
		add_action(
			'wp_enqueue_scripts',
			function () use ( $css ) {
				wp_add_inline_style(
					'fibofilters',
					str_replace( [ '<style>', '</style>' ], '', $css )
				);
			}
		);
	}
}
