<?php
declare( strict_types=1 );

namespace FiboFilters\Config;

use FiboFilters\Filters\FilterSources;
use FiboFilters\Indexer\BuildInfo;
use FiboFilters\Indexer\Config;
use FiboFilters\Settings\Settings;
use FiboFilters\Settings\Storage;
use Freemius;

/**
 * Install Class
 */
class Install {
	const SETTINGS_VERSION = 6;

	/** @var FilterSources */
	private $filter_sources;

	/** @var Config */
	private $config;

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

	/** @var Index */
	private $index;

	/** @var Storage */
	private Storage $storage;

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

	public function __construct( FilterSources $filter_sources, Config $config, Freemius $freemius, Index $index, Storage $storage, Settings $settings ) {
		$this->filter_sources = $filter_sources;
		$this->config         = $config;
		$this->freemius       = $freemius;
		$this->index          = $index;
		$this->storage        = $storage;
		$this->settings       = $settings;
	}

	public function install_without_reindexing(): void {
		if ( ! $this->freemius->is_premium() ) {
			$this->install();
		}

		if ( $this->freemius->is__premium_only() ) {
			if ( $this->freemius->is_premium() ) {
				$this->install__premium_only( true );
			}
		}
	}

	/**
	 * Triggered on hooks:
	 * - 'fibofilters/indexer/index_pusher'
	 * - 'admin_init'
	 *
	 * @see \FiboFilters\Config\Initializer::initialize
	 */
	public function maybe_install( bool $skip_reindexing = false ): void {
		if ( wp_doing_ajax() ) {
			return;
		}
		if ( defined( 'IFRAME_REQUEST' ) ) {
			return;
		}

		if ( ! $this->freemius->is_premium() && get_option( Env::$plugin_prefix . 'version' ) !== Env::$version ) {
			$this->install();
		}

		if ( $this->freemius->is__premium_only() ) {
			if ( $this->freemius->is_premium() && get_option( Env::$plugin_prefix . 'version_pro' ) !== Env::$version ) {
				$this->install__premium_only( $skip_reindexing );
			}
		}

		// Remove backup of filter settings.
		if ( defined( 'FIBOFILTERS_STORAGE_ONLY' ) && FIBOFILTERS_STORAGE_ONLY === true ) {
			delete_option( 'fibofilters_filters_backup' );
		}
	}

	public function install(): void {
		add_option( Env::$plugin_prefix . 'activation_timestamp', time(), '', false );

		update_option( Env::$plugin_prefix . 'version', Env::$version );
	}

	public function install__premium_only( bool $skip_reindexing = false ): void {
		add_option( Env::$plugin_prefix . 'activation_timestamp_pro', time(), '', false );
		update_option( Env::$plugin_prefix . 'version_pro', Env::$version );

		$this->storage->maybe_create();

		$this->maybe_upgrade_settings( 'pro' );

		/**
		 * We don't need to initialize the options in the database because register_setting()
		 * returns default values when the option is not in the database.
		 */

		delete_transient( Env::$plugin_prefix . 'sources_analyse_data' );

		// Remove deprecated options.
		delete_option( BuildInfo::LAST_BUILD_OPTION_KEY . '_logs' );
		delete_option( BuildInfo::LAST_BUILD_OPTION_KEY . '_logs_tmp' );

		/**
		 * Whenever a plugin version is changed, we always remove the old index
		 * when parallel building is enabled, to avoid compatibility problems.
		 */
		if ( $this->config->is_parallel_building_enabled() && $skip_reindexing ) {
			$this->index->remove_index( 'main' );
		}

		if ( $skip_reindexing ) {
			return;
		}

		$this->index->build();
	}

	public function maybe_upgrade_settings( string $version = 'free' ): void {
		$option_key       = $version === 'free' ? 'fibofilters_settings_version' : 'fibofilters_settings_version_pro';
		$settings_version = (int) get_option( $option_key, 0 );
		$upgraded         = false;

		if ( $settings_version === 0 ) {
			$this->upgrade_settings_to_1();
			$settings_version = 1;
			$upgraded         = true;
		}

		if ( $settings_version === 1 ) {
			$this->upgrade_settings_1_to_2();
			$settings_version = 2;
			$upgraded         = true;
		}

		if ( $settings_version === 2 ) {
			$this->upgrade_settings_2_to_3();
			$settings_version = 3;
			$upgraded         = true;
		}

		if ( $settings_version === 3 ) {
			$this->upgrade_settings_3_to_4();
			$settings_version = 4;
			$upgraded         = true;
		}

		if ( $settings_version === 4 ) {
			$this->upgrade_settings_4_to_5();
			$settings_version = 5;
			$upgraded         = true;
		}

		if ( $settings_version === 5 ) {
			$this->upgrade_settings_5_to_6();
			$settings_version = 6;
			$upgraded         = true;
		}

		if ( $upgraded ) {
			update_option( $option_key, self::SETTINGS_VERSION );
		}
	}

	private function upgrade_settings_to_1(): void {
		$filters_option = get_option( 'fibofilters_filters' );
		if ( empty( $filters_option ) ) {
			$filters_option = '[]';
		}
		$blocks = json_decode( $filters_option, true );
		if ( json_last_error() === JSON_ERROR_NONE && is_array( $blocks ) ) {
			foreach ( $blocks as $index => $block ) {
				if ( $block['name'] !== 'fibofilters/filter' ) {
					continue;
				}
				if ( empty( $block['attributes']['source'] ) ) {
					continue;
				}
				$source = $this->filter_sources->get_source( $block['attributes']['source'] );
				$blocks[ $index ]['attributes']['user_input_type'] = $source->get_data_attr( 'userInputType' );
			}
			update_option( 'fibofilters_filters', wp_json_encode( $blocks ) );
		}
	}

	/**
	 * Copy 'id' to 'url_slug' and set 'id' as 'filter-X'.
	 */
	private function upgrade_settings_1_to_2(): void {
		$filters_option = get_option( 'fibofilters_filters' );
		if ( empty( $filters_option ) ) {
			$filters_option = '[]';
		}
		$blocks = json_decode( $filters_option, true );
		if ( json_last_error() === JSON_ERROR_NONE && is_array( $blocks ) ) {
			foreach ( $blocks as $index => $block ) {
				if ( $block['name'] !== 'fibofilters/filter' ) {
					continue;
				}
				$blocks[ $index ]['attributes']['url_slug'] = $block['attributes']['id'] === 'tag' ? 'fftag' : $block['attributes']['id'];
				$blocks[ $index ]['attributes']['id']       = $index + 1;
			}
			update_option( 'fibofilters_filters', wp_json_encode( $blocks ) );
		}
	}

	/**
	 * Convert 'id' from numeric to 'filter-X'
	 */
	private function upgrade_settings_2_to_3(): void {
		$blocks = $this->get_filter_blocks();
		if ( empty( $blocks ) ) {
			return;
		}

		foreach ( $blocks as $index => $block ) {
			if ( $block['name'] !== 'fibofilters/filter' ) {
				continue;
			}
			$blocks[ $index ]['attributes']['id'] = sprintf( 'filter-%d', $index + 1 );
		}
		update_option( 'fibofilters_filters', wp_json_encode( $blocks ) );
	}

	/**
	 * Rename "products_loading_strategy" to "pagination_strategy".
	 */
	private function upgrade_settings_3_to_4(): void {
		$value = get_option( 'fibofilters_appearance_products_loading_strategy' );
		if ( in_array( $value, [ 'pagination', 'show-more-button' ], true ) ) {
			add_option( 'fibofilters_appearance_pagination_strategy', $value );
		}
		delete_option( 'fibofilters_appearance_products_loading_strategy' );
	}

	/**
	 * Add meta values to filters with "Range" type.
	 */
	private function upgrade_settings_4_to_5(): void {
		$blocks = $this->get_filter_blocks();
		if ( empty( $blocks ) ) {
			return;
		}

		foreach ( $blocks as $index => $block ) {
			if ( $block['name'] !== 'fibofilters/filter' ) {
				continue;
			}
			if ( $block['attributes']['type'] === 'range' && $block['attributes']['source'] !== 'price' ) {
				$blocks[ $index ]['attributes']['user_input_type']          = 'number_range';
				$blocks[ $index ]['attributes']['meta']['number_precision'] = 0;
				$blocks[ $index ]['attributes']['meta']['number_prefix']    = '';
				$blocks[ $index ]['attributes']['meta']['number_suffix']    = '';
			}
		}

		update_option( 'fibofilters_filters', wp_json_encode( $blocks ) );
	}

	/**
	 * Move filters from the `fibofilters_filters` option to the storage table.
	 */
	private function upgrade_settings_5_to_6() {
		// We want load filters from wp_options, not from the shorage.
		remove_filter( 'pre_option_fibofilters_filters', [ $this->settings, 'load_filters' ] );

		$filters_option = get_option( 'fibofilters_filters' );
		if ( empty( $filters_option ) ) {
			$filters_option = '[]';
		} else {
			add_option( 'fibofilters_filters_backup', $filters_option, '', false );
		}

		$this->storage->save( 'filters', $filters_option );
		delete_option( 'fibofilters_filters' );
	}

	private function get_filter_blocks(): array {
		$filters_option = get_option( 'fibofilters_filters' );
		if ( empty( $filters_option ) ) {
			$filters_option = '[]';
		}
		$blocks = json_decode( $filters_option, true );
		if ( json_last_error() === JSON_ERROR_NONE && is_array( $blocks ) ) {
			return $blocks;
		}

		return [];
	}
}
