<?php
/**
 * Admin settings page.
 *
 * @package northmill-online-shared
 *
 * @phpcs:disable Squiz.Commenting.FunctionComment.MissingParamTag
 */

declare(strict_types=1);

namespace Northmill_Online_Shared\Modules\Admin\Pages\Page;

use Closure;
use Northmill_Online_Shared\Abstracts\Hub_API\Abstract_API_Base;
use Northmill_Online_Shared\Abstracts\Hub_API\Hub_Accounts\Envs;
use Northmill_Online_Shared\Abstracts\Hub_API\Hub_Accounts\Helpers;
use Northmill_Online_Shared\Abstracts\Hub_API\Hub_Accounts\Options_Page_Processing;
use Northmill_Online_Shared\Abstracts\Hub_API\Hub_Accounts\Hub_Account_Setup;
use Northmill_Online_Shared\Abstracts\Hub_API\Hub_Accounts\WP_Repository;
use Northmill_Online_Shared\Modules\Admin\Pages\Abstract_Admin_Page;
use Northmill_Online_Shared\Modules\Admin\Settings\Settings_Fields_Module;
use Northmill_Online_Shared\Modules\Cron_Module;

defined( 'ABSPATH' ) || exit;

/**
 * Class Admin_Settings
 */
class Admin_Settings extends Abstract_Admin_Page {

	/**
	 * Set up page settings.
	 *
	 * @return void
	 */
	public function setup_page(): void {
		add_action( 'admin_init', array( $this, 'admin_init' ), 10, 0 );
	}

	/**
	 * {@inheritDoc}
	 */
	protected function render_notices(): void {
		parent::render_notices();

		if ( Hub_Account_Setup::is_setting_up_hub_accounts() ) {
			Helpers::render_settings_notices();
		}
	}

	/**
	 * {@inheritDoc}
	 */
	protected function pre_update_option( array $posted_options ): array {
		$posted_options = parent::pre_update_option( $posted_options );

		if ( Hub_Account_Setup::is_setting_up_hub_accounts() ) {
			$posted_options = Options_Page_Processing::update_from_options_page( $posted_options );
		}

		return $posted_options;
	}

	/**
	 * Handle updated options for page. This is only called if the option is updated.
	 *
	 * @param array $old_options Old options.
	 * @param array $new_options New options.
	 *
	 * @return void
	 */
	protected function updated_options( array $old_options, array $new_options ): void {
		do_action( 'northmill_online_shared_settings_page_updated', $old_options, $new_options );
		do_action( 'northmill_online_shared_settings_page_updated_' . Abstract_Admin_Page::get_settings_id(), $old_options, $new_options );

		if ( Hub_Account_Setup::is_setting_up_hub_accounts() ) {
			$cron_syncer = $this->shared->cron->get_cron_event( Cron_Module::CRON_EVENT_SYNC );
			$cron_syncer?->set_is_enabled( $new_options[ Settings_Fields_Module::AUTO_SYNC ] );
		}
	}

	/**
	 * Register settings.
	 *
	 * @return void
	 */
	public function admin_init(): void {
		$fields = $this->shared->admin->settings_fields->get_fields( 'setting', Abstract_Admin_Page::get_settings_id() );
		$values = $this->shared->admin->settings_fields->get_option( 'setting', Abstract_Admin_Page::get_settings_id() );

		if ( empty( $fields ) ) {
			return;
		}

		register_setting(
			Abstract_Admin_Page::get_settings_id(),
			Abstract_Admin_Page::get_settings_id(),
			array(
				'type'              => 'array',
				'sanitize_callback' => array( $this, 'sanitize_settings' ),
				'default'           => array_map(
					function ( $field ) {
						return $field['default'];
					},
					$fields
				),
			)
		);

		foreach ( $fields as $field ) {
			if ( ! empty( $field['label'] ) ) {
				$field['label'] = self::maybe_solve_closure( $field['label'] );
			}
			if ( ! empty( $field['description'] ) ) {
				$field['description'] = self::maybe_solve_closure( $field['description'] );
			}
			if ( ! empty( $field['default'] ) ) {
				$field['default'] = self::maybe_solve_closure( $field['default'] );
			}

			if ( 'section' === $field['type'] ) {
				add_settings_section(
					$field['id'],
					$field['label'],
					array( $this, 'html_section' ),
					Abstract_Admin_Page::get_settings_id()
				);
			} elseif ( 'hub_accounts' === $field['type'] ) {
				add_settings_section(
					$field['id'],
					$field['label'],
					array( $this, 'html_hub_accounts' ),
					Abstract_Admin_Page::get_settings_id()
				);
			} else {
				add_settings_field(
					$field['id'],
					$field['label'],
					array(
						$this,
						'html_field',
					),
					Abstract_Admin_Page::get_settings_id(),
					$field['section'],
					array(
						'field' => $field,
						'value' => $values[ $field['name'] ] ?? $field['default'],
					)
				);
			}
		}
	}

	/**
	 * Get field property that might be a closure.
	 * Mostly used to delay field property translation until the field is rendered.
	 *
	 * @param mixed $field_value Field value.
	 *
	 * @return mixed
	 */
	public static function maybe_solve_closure( mixed $field_value ): mixed {
		if ( is_callable( $field_value ) ) {
			$field_value = $field_value();
		}
		return $field_value;
	}

	/**
	 * Get translated select options.
	 *
	 * @param array|Closure $field_options Field options.
	 *
	 * @return array
	 */
	public static function get_translated_select_options( array|Closure $field_options ): array {
		if ( is_callable( $field_options ) ) {
			$field_options = $field_options();
		}

		return $field_options;
	}

	/**
	 * Render an HTML section.
	 *
	 * @param array $section Section to render.
	 *
	 * @return void
	 */
	public function html_section( array $section ): void {
		$field = $this->shared->admin->settings_fields->get_field( $section['id'] );

		if ( ! isset( $field['description'] ) ) {
			return;
		}

		echo wp_kses_post( wpautop( $field['description'] ) );
	}

	/**
	 * Render field HTML.
	 *
	 * @param array $args Field to render.
	 *
	 * @return void
	 */
	public function html_field( array $args ): void {
		/**
		 * Filter the field arguments.
		 * This can be used to add or customize the attributes of the field.
		 *     - field: An array of properties for the settings field.
		 *     - target_name: The target setting ID to check if it matches the current settings ID.
		 *     - name: The name of the field to check if it's "is_receipt_active".
		 *     - description: The description of the field.
		 *     - disabled: (Optional) Whether the field should be disabled.
		 *                            Currently only implemented for checkboxes.
		 *     - description: (Optional) Additional description for the field.
		 *
		 * @param array $args Field arguments.
		 */
		$args = apply_filters( 'northmill_online_shared_admin_settings_field_' . Abstract_Admin_Page::get_settings_id() . '_' . $this->shared->package_info->get_service_name_underscored(), $args );

		$field       = $args['field'];
		$value       = $args['value'];
		$placeholder = $field['placeholder'] ?? '';
		$required    = ! empty( $field['required'] ) ? 'required' : '';

		$disabled = $field['disabled'] ?? '';
		if ( isset( $field['disabled_callback'] ) && is_callable( $field['disabled_callback'] ) ) {
			$disabled = call_user_func( $field['disabled_callback'], $value );
		}

		$extra_html = $field['extra_html'] ?? '';
		if ( isset( $field['extra_html_callback'] ) && is_callable( $field['extra_html_callback'] ) ) {
			$extra_html .= call_user_func( $field['extra_html_callback'], $value );
		}

		$input_id   = Abstract_Admin_Page::get_settings_id() . '_' . $field['name'];
		$input_name = Abstract_Admin_Page::get_settings_id() . '[' . $field['name'] . ']';
		if ( 'checkbox' === $field['type'] ) {
			$disabled = $disabled ? ' disabled="disabled"' : '';
			echo '<input id="' . esc_attr( $input_id ) . '" name="' . esc_attr( $input_name ) . '" type="checkbox" value="1" ' . checked( $value, true, false ) . ' ' . esc_html( $disabled ) . ' ' . esc_attr( $required ) . ' />';

		} elseif ( 'textarea' === $field['type'] ) {
			echo '<textarea id="' . esc_attr( $input_id ) . '" name="' . esc_attr( $input_name ) . '" cols="30" rows="5" class="regular-text" placeholder="' . esc_attr( $placeholder ) . ' ' . esc_attr( $required ) . '">' . esc_html( $value ) . '</textarea>';

		} elseif ( 'number' === $field['type'] ) {
			echo '<input id="' . esc_attr( $input_id ) . '" name="' . esc_attr( $input_name ) . '" size="40" type="number" class="regular-text" value="' . esc_attr( $value ) . '" placeholder="' . esc_attr( $placeholder ) . '" ' . esc_attr( $required ) . ' />';

		} elseif ( 'image' === $field['type'] ) {
			$image = wp_get_attachment_image( $value, 'full' );
			echo '<input id="' . esc_attr( $input_id ) . '" name="' . esc_attr( $input_name ) . '" type="hidden" value="' . esc_attr( $value ) . '" ' . esc_attr( $required ) . ' />';
			echo '<div class="nm-shared-img-preview">' . wp_kses_post( $image ) . '</div>';

		} elseif ( 'color' === $field['type'] ) {
			echo '<input id="' . esc_attr( $input_id ) . '" name="' . esc_attr( $input_name ) . '" type="color" value="' . esc_attr( $value ) . '" ' . esc_attr( $required ) . ' />';

		} elseif ( 'select' === $field['type'] ) {
			echo '<select id="' . esc_attr( $input_id ) . '" name="' . esc_attr( $input_name ) . '" ' . esc_attr( $required ) . '>';
			$field['options'] = self::get_translated_select_options( $field['options'] );
			foreach ( $field['options'] as $option_value => $option_label ) {
				echo '<option value="' . esc_attr( $option_value ) . '" ' . selected( $value, $option_value, false ) . '>' . esc_html( $option_label ) . '</option>';
			}
			echo '</select>';

		} elseif ( 'menu' === $field['type'] ) {
			wp_dropdown_categories(
				array(
					'selected'          => $value,
					'taxonomy'          => 'nav_menu',
					'hide_empty'        => false,
					'id'                => $input_id,
					'name'              => $input_name,
					'show_option_none'  => __( 'None', 'northmill-online-shared' ),
					'option_none_value' => '0',
					'required'          => ! empty( $required ),
				)
			);

		} elseif ( 'repeater' === $field['type'] ) {
			$this->shared->template_loader->get_admin_template_part(
				'settings-repeater-field-table',
				array(
					'field'          => $field,
					'value'          => $value,
					'field_renderer' => array( $this, 'html_field' ),
				),
				false
			);

		} elseif ( 'page' === $field['type'] ) {
			$page_name = '';
			if ( $value && is_numeric( $value ) ) {
				$page      = get_post( $value );
				$page_name = $page ? $page->post_title : '';
			}

			echo '<input id="' . esc_attr( $input_id ) . '-input" name="' . esc_attr( $input_name ) . '-input" size="40" type="text" class="page-input-field" value="' . esc_attr( $page_name ) . '" placeholder="' . esc_attr( $placeholder ) . '" ' . esc_attr( $required ) . ' />';
			echo '<input id="' . esc_attr( $input_id ) . '" name="' . esc_attr( $input_name ) . '" type="hidden" class="page-value-field" value="' . esc_attr( $value ) . '" placeholder="' . esc_attr( $placeholder ) . '" ' . esc_attr( $required ) . ' />';

		} else {
			echo '<input id="' . esc_attr( $input_id ) . '" name="' . esc_attr( $input_name ) . '" size="40" type="text" class="regular-text" value="' . esc_attr( $value ) . '" placeholder="' . esc_attr( $placeholder ) . '" ' . esc_attr( $required ) . ' />';
		}

		if ( $field['description'] ) {
			echo '<p class="description">' . wp_kses_post( $field['description'] ) . '</p>';
		}

		if ( $extra_html ) {
			echo wp_kses_post( $extra_html );
		}
	}

	/**
	 * Render hub accounts field HTML.
	 *
	 * @param array $args Field to render.
	 *
	 * @return void
	 * @noinspection PhpUnusedParameterInspection
	 */
	public function html_hub_accounts( array $args ): void {
		$args = array();

		ob_start();
		$this->html_field(
			array(
				'field' => array(
					'name'        => Envs::SETTING_NAME,
					'required'    => true,
					'type'        => 'select',
					'description' => '',
					'options'     => Envs::get_valid_envs(),
				),
				'value' => Envs::get_current_env(),
			)
		);

		$args['env_field'] = ob_get_clean();

		$args['stock_accounts']    = WP_Repository::get_stock_accounts( null, true );
		$args['test_main_account'] = WP_Repository::get_main_account( 'test' );
		$args['prod_main_account'] = WP_Repository::get_main_account( 'prod' );

		$args['is_stock_accounts_used'] = Hub_Account_Setup::is_stock_accounts_used();

		$this->shared->template_loader->get_admin_template_part(
			'settings/hub-account-fields',
			$args,
			false
		);
	}

	/**
	 * Sanitize settings.
	 *
	 * @param array $values Values to sanitize.
	 *
	 * @return array
	 */
	public function sanitize_settings( array $values ): array {
		$fields = $this->shared->admin->settings_fields->get_fields( 'setting', Abstract_Admin_Page::get_settings_id() );

		// Only in some cases, the values are nested.
		if ( isset( $values[ Abstract_Admin_Page::get_settings_id() ] ) ) {
			$values = $values[ Abstract_Admin_Page::get_settings_id() ];
		}

		return array( Abstract_Admin_Page::get_settings_id() => $this->inner_sanitize_settings( $fields, $values ) );
	}

	/**
	 * Sanitize settings.
	 *
	 * @param array $fields Fields to sanitize.
	 * @param array $values Values to sanitize.
	 *
	 * @return array
	 */
	private function inner_sanitize_settings( array $fields, array $values ): array {
		$new_values = array();

		foreach ( $fields as $field ) {

			if ( 'section' === $field['type'] ) {
				continue;
			} elseif ( 'checkbox' === $field['type'] ) {
				$new_values[ $field['name'] ] = ( isset( $values[ $field['name'] ] ) && '1' === $values[ $field['name'] ] );
			} elseif ( 'number' === $field['type'] ) {
				$new_values[ $field['name'] ] = ( isset( $values[ $field['name'] ] ) ) ? intval( $values[ $field['name'] ] ) : $field['default'];
			} elseif ( 'image' === $field['type'] ) {
				$new_values[ $field['name'] ] = ( isset( $values[ $field['name'] ] ) ) ? intval( $values[ $field['name'] ] ) : $field['default'];
			} elseif ( 'select' === $field['type'] ) {
				$new_values[ $field['name'] ] = $field['default'];
				$field['options']             = self::get_translated_select_options( $field['options'] );

				if ( isset( $values[ $field['name'] ] ) ) {
					foreach ( $field['options'] as $option_value => $option_label ) {
						if ( $option_value !== $values[ $field['name'] ] ) {
							continue;
						}

						$new_values[ $field['name'] ] = $option_value;
					}
				}
			} elseif ( 'repeater' === $field['type'] ) {
				$sub_fields = array();
				for ( $i = 0; $i < $field['max_fields']; $i++ ) {
					foreach ( $field['fields'] as $sub_field ) {
						$new_sub_field         = $sub_field;
						$new_sub_field['name'] = $field['name'] . '_' . $sub_field['name'] . '_' . $i;
						$sub_fields[]          = $new_sub_field;
					}
				}
				$sub_field_values = $this->inner_sanitize_settings( $sub_fields, $values );
				$new_values       = array_merge( $new_values, $sub_field_values );
			} elseif ( 'menu' === $field['type'] ) {
				$new_values[ $field['name'] ] = ( isset( $values[ $field['name'] ] ) ) ? intval( $values[ $field['name'] ] ) : $field['default'];
			} elseif ( 'hub_accounts' === $field['type'] ) {
				// Static field name for hub accounts, since there should only be one instance.
				// Also ensures that the value isn't passed under some other field name skipping sanitization.
				$new_values[ Options_Page_Processing::OPTIONS_PAGE_KEY ] = $values[ Options_Page_Processing::OPTIONS_PAGE_KEY ] ?? array();
			} else {
				$new_values[ $field['name'] ] = isset( $values[ $field['name'] ] ) ? sanitize_text_field( $values[ $field['name'] ] ) : '';
			}
		}

		if (
			Hub_Account_Setup::is_setting_up_hub_accounts() &&
			! empty( $values[ Envs::SETTING_NAME ] )
		) {
			$new_values[ Envs::SETTING_NAME ] = sanitize_text_field( $values[ Envs::SETTING_NAME ] );
		}

		return $new_values;
	}

	/**
	 * Get page params.
	 *
	 * @return array
	 */
	public function get_page_params(): array {
		return array(
			'settings_id' => Abstract_Admin_Page::get_settings_id(),
			'title'       => $this->shared->package_info->package_data->get_name() . ' - ' . $this->get_menu_title(),
		);
	}

	/**
	 * Enqueue scripts and styles for the admin area.
	 *
	 * @return void
	 */
	public function admin_enqueue_scripts(): void {
		wp_enqueue_style( 'wp-jquery-ui-dialog' );
		wp_enqueue_script( 'jquery-ui-autocomplete' );
		parent::admin_enqueue_scripts();
	}

	/**
	 * Get plugin localization.
	 * Retrieves an array of pages with their labels and IDs.
	 *
	 * @param bool $is_shared Whether the script file is from shared.
	 *
	 * @return array Array of pages with their labels and IDs.
	 */
	public function get_script_localization( bool $is_shared ): array {
		$localization = parent::get_script_localization( $is_shared );

		$pages       = get_pages();
		$pages_array = array();

		foreach ( $pages as $page ) {
			$pages_array[] = array(
				'label' => $page->post_title,
				'value' => $page->ID,
			);
		}

		// TODO: Array item 'pages' are used by Booking. Maybe move this to Booking? Filter?
		$localization['pages'] = $pages_array;

		if ( ! $is_shared ) {
			return $localization;
		}

		if ( Hub_Account_Setup::is_setting_up_hub_accounts() ) {
			$localization['l10n']['prod_title'] = esc_html__( 'Production stock account', 'northmill-online-shared' );
			$localization['l10n']['test_title'] = esc_html__( 'Test stock account', 'northmill-online-shared' );

			$localization['api_urls'] = array(
				'prod' => Abstract_API_Base::get_api_url_options( 'prod' ),
				'test' => Abstract_API_Base::get_api_url_options( 'test' ),
			);

			$localization['hub_field_name_prefix'] = self::get_settings_id() . '[' . Options_Page_Processing::OPTIONS_PAGE_KEY . ']';
		}

		return $localization;
	}
}
