<?php

namespace GravityKit\GravityExport\MultiRow;

use GFExcel\GFExcelConfigConstants;
use GravityKit\GravityExport\Addon\GravityExportAddon;
use GravityKit\GravityExport\Filters\Addon\FiltersFeedAddon;
use GravityKit\GravityExport\MultiRow\Transformer\MultipleRowsCombiner;
use GFExcel\Transformer\CombinerInterface;
use GravityKit\GravityExport\Save\Addon\SaveAddon;

/**
 * The plugin that enables the multiple rows add-on.
 *
 * @since 1.0
 * @codeCoverageIgnore
 */
class Plugin {
	/**
	 * Name of the setting field.
	 *
	 * @since 1.0
	 */
	private const SETTING_MULTI_ROW = 'gravityexport-multi-row';

	/**
	 * Constants that represent the setting's state.
	 * @since $ver$
	 */
	private const VALUE_NO = 0;
	private const VALUE_YES = 1;
	private const VALUE_INHERIT = 2;

	/**
	 * GravityExport Lite plugin.
	 *
	 * @since 1.0
	 * @var \GFExcel\Addon\GravityExportAddon
	 */
	private $plugin;

	/**
	 * The current feed ID if any
	 * @var null
	 */
	private $feed_id = null;

	/**
	 * Micro cache for plugin settings.
	 * @since $ver$
	 * @var array|null
	 */
	private $plugin_settings = null;

	/**
	 * Registers all hooks.
	 *
	 * @since 1.0
	 */
	public function __construct() {
		$this->plugin = \GFExcel\Addon\GravityExportAddon::get_instance();

		add_filter( 'gfexcel_general_settings', \Closure::fromCallable( [ $this, 'addMultiRowSettingField' ] ) );
		add_filter( 'gk/gravityexport/settings/sections', \Closure::fromCallable( [ $this, 'getGlobalSettings' ] ) );
		add_filter( GFExcelConfigConstants::GFEXCEL_DOWNLOAD_COMBINER, \Closure::fromCallable( [
			$this,
			'replaceCombiner'
		] ), 10, 2 );
		add_action( 'request', \Closure::fromCallable( [ $this, 'setFeedFromRequest' ] ), 20 );

		foreach ( [ FiltersFeedAddon::get_instance(), SaveAddon::get_instance() ] as $addon ) {
			// Add a multi-row section to the add-ons.
			add_action(
				"gform_{$addon->get_slug()}_feed_settings_fields",
				\Closure::fromCallable( [ $this, 'addMultiRowAddonSettingField' ] )
			);
			// Register hook on these add-ons to keep track of the current feed id.
			add_action(
				"gform_{$addon->get_slug()}_pre_process_feeds",
				\Closure::fromCallable( [ $this, 'setFeedFromProcess' ] )
			);
		}
	}

	/**
	 * Adds the setting to the general settings page.
	 *
	 * @since 1.0
	 *
	 * @param array $settings The default general settings.
	 *
	 * @return array The updated settings.
	 */
	private function addMultiRowSettingField( array $settings ): array {
		$form_settings = $this->plugin->get_current_settings();

		return array_merge( $settings, [
			[
				'title'  => esc_html__( 'Multiple Rows', 'gk-gravityexport' ),
				'fields' => [
					[
						'name'        => self::SETTING_MULTI_ROW,
						'label'       => esc_html__( 'Enable multi-row splitting', 'gk-gravityexport' ),
						// translators: Do not translate the words inside the {} curly brackets; they are replaced.
						'description' => strtr( esc_html__( 'For List fields and GP Nested Forms, add a distinct row for each item. {url}Learn more about multi-row splitting{/url}.', 'gk-gravityexport' ), [
							'{url}'  => '<a href="https://docs.gravitykit.com/article/905-enable-multi-row-splitting-setting" target="_blank" rel="noopener noreferrer">',
							'{/url}' => '</a>',
						] ),
						'type'        => 'checkbox',
						'choices'     => [
							[
								'name'          => self::SETTING_MULTI_ROW,
								'label'         => esc_html__( 'Yes, split fields with multiple values into multiple rows', 'gk-gravityexport' ),
								'default_value' => rgar( $form_settings, self::SETTING_MULTI_ROW, $this->get_plugin_settings( self::SETTING_MULTI_ROW ) ),
							],
						],
					]
				],
			],
		] );
	}

	/**
	 * Adds the multi row section to the appropriate add-ons.
	 *
	 * @since $ver$
	 *
	 * @param array $sections The feed Add-on fields.
	 *
	 * @return array The changed fields.
	 */
	private function addMultiRowAddonSettingField( array $sections ): array {
		$sections[] = [
			'title'  => esc_html__( 'Multiple Rows', 'gk-gravityexport' ),
			'fields' => [
				[
					'name'          => self::SETTING_MULTI_ROW,
					'label'         => esc_html__( 'Enable multi-row splitting', 'gk-gravityexport' ),
					'type'          => 'select',
					// translators: Do not translate the words inside the {} curly brackets; they are replaced.
					'description'   => strtr( esc_html__( 'For List fields and GP Nested Forms, add a distinct row for each item. {url}Learn more about multi-row splitting{/url}.', 'gk-gravityexport' ), [
						'{url}'  => '<a href="https://docs.gravitykit.com/article/905-enable-multi-row-splitting-setting" target="_blank" rel="noopener noreferrer">',
						'{/url}' => '</a>',
					] ),
					'default_value' => self::VALUE_INHERIT,
					'choices'       => [
						[
							'name'  => self::SETTING_MULTI_ROW,
							'value' => self::VALUE_INHERIT,
							'label' => esc_html__( 'Default (use general setting)', 'gk-gravityexport' ),
						],
						[
							'name'  => self::SETTING_MULTI_ROW,
							'value' => self::VALUE_NO,
							'label' => esc_html__( 'Don\'t split into multiple rows', 'gk-gravityexport' ),
						],
						[
							'name'  => self::SETTING_MULTI_ROW,
							'value' => self::VALUE_YES,
							'label' => esc_html__( 'Yes, split fields with multiple values into multiple rows', 'gk-gravityexport' ),
						],
					],
				],
			]
		];

		return $sections;
	}

	/**
	 * Replaces the default combiner with the multiple rows combiner.
	 *
	 * @since 1.0
	 *
	 * @param CombinerInterface $combiner The current combiner.
	 * @param int|null          $form_id  The form ID.
	 *
	 * @return CombinerInterface The new combiner.
	 */
	private function replaceCombiner( CombinerInterface $combiner, ?int $form_id = null ): CombinerInterface {
		if ( $form_id ) {
			$settings = $this->plugin->get_form_settings( \GFAPI::get_form( $form_id ) );
			$default  = (int) ( $settings[ Plugin::SETTING_MULTI_ROW ] ?? self::VALUE_NO );

			$feeds = \GFAPI::get_feeds( [ $this->feed_id ], $form_id );
			$feed  = is_wp_error( $feeds ) ? null : reset( $feeds );

			$should_replace = $default !== self::VALUE_NO;

			if ( $feed ) {
				$meta         = rgar( $feed, 'meta', [] );
				$feed_setting = (int) ( $meta[ self::SETTING_MULTI_ROW ] ?? self::VALUE_INHERIT );

				if ( $feed_setting !== self::VALUE_INHERIT ) {
					$should_replace = $feed_setting !== self::VALUE_NO;
				}
			}

			if ( $should_replace ) {
				$combiner = new MultipleRowsCombiner();
			}
		}

		return $combiner;
	}


	/**
	 * Saves the feed id if it is present on the request.
	 * Used for downloads via hash.
	 *
	 * @since $ver$
	 *
	 * @param array $query_vars The query vars.
	 *
	 * @return array The query vars.
	 */
	private function setFeedFromRequest( array $query_vars ): array {
		$this->feed_id = rgar( $query_vars, 'gfexcel_download_feed' );

		return $query_vars;
	}

	/**
	 * Saves the feed id if it is present on the request.
	 * Used for processable feed actions.
	 *
	 * @since $ver$
	 *
	 * @param array $feeds The feeds.
	 *
	 * @return array The feeds.
	 */
	private function setFeedFromProcess( array $feeds ): array {
		$this->feed_id = count( $feeds ) === 1 ? $feeds[0]['id'] : null;

		return $feeds;
	}

	/**
	 * Adds the global settings for the multiple row plugin.
	 * @since $ver$
	 *
	 * @param array $sections The section to add the global settings to.
	 *
	 * @return array The updated sections.
	 */
	private function getGlobalSettings( array $sections ): array {
		$sections[] = [
			'title'       => esc_html__( 'Multiple rows', 'gk-gravityexport' ),
			'description' => esc_html__( 'These are default settings for new forms. You can overwrite them per form using the available hooks.', 'gk-gravityexport' ),
			'settings'    => [
				[
					'id'          => self::SETTING_MULTI_ROW,
					'name'        => self::SETTING_MULTI_ROW,
					'type'        => 'checkbox',
					'title'       => esc_html__( 'Enable multi-row splitting by default', 'gk-gravityexport' ),
					'description' => strtr( esc_html__( 'For List fields and GP Nested Forms, add a distinct row for each item. {url}Learn more about multi-row splitting{/url}.', 'gk-gravityexport' ), [
						'{url}'  => '<a class="text-blue-700 hover:text-blue-600" href="https://docs.gravitykit.com/article/905-enable-multi-row-splitting-setting" target="_blank" rel="noopener noreferrer">',
						'{/url}' => '</a>',
					] ),
					'value'       => $this->get_plugin_settings( self::SETTING_MULTI_ROW, self::VALUE_NO ),
				],
			]
		];

		return $sections;
	}

	/**
	 * Returns the default global settings for this plugin.
	 * @since $ver$
	 * @return array The default settings.
	 */
	private function get_default_plugin_settings(): array {
		$defaults = [
			self::SETTING_MULTI_ROW => 0,
		];

		/**
		 * @filter `gk/multiple_rows/settings/defaults` Filter default global settings.
		 *
		 * @param array $defaults The defaults settings.
		 */
		return apply_filters( 'gk/multiple_rows/settings/defaults', $defaults );
	}

	/**
	 * Returns the setting for the provided key.
	 * @since $ver$
	 *
	 * @param string $key     The setting to retrieve.
	 * @param mixed  $default The default value to return
	 *
	 * @return mixed The setting.
	 */
	public function get_plugin_settings( string $key, $default = null ) {
		if ( $this->plugin_settings !== null ) {
			return $this->plugin_settings[ $key ] ?? $default;
		}

		$this->plugin_settings = array_merge(
			$this->get_default_plugin_settings(),
			GravityExportAddon::get_instance()->get_plugin_settings(),
		);

		return $this->plugin_settings[ $key ] ?? $default;
	}
}
