<?php
/**
 * The file contains definition of helpers
 *
 * @category Helpers file
 * @package WLC\Algolia
 * @author White Label Coders
 * @license TO DO
 * @link TO DO
 */

namespace wlc_algolia\Helpers;

use wlc_algolia\Algolia_Index_Helper;

defined( 'ABSPATH' ) || die( 'Direct file access is forbidden' );

/**
 * Sanitizes slug.
 *
 * @param string $text Text to sanitize.
 *
 * @return string
 */
function sanitize_slug( $text ) {
	return str_replace( '_', '-', \sanitize_key( $text ) );
}


/**
 * Gets array item value by key.
 *
 * @param array  $input_array   Source array.
 * @param string $key           Key to search.
 * @param mixed  $default_value Default value.
 *
 * @return mixed
 */
function get_array_value( $input_array, $key, $default_value = null ) {
	$value = $default_value;

	if ( is_array( $input_array ) && isset( $input_array[ $key ] ) ) {
		$value = $input_array[ $key ];
	}

	return $value;
}

/**
 * Sorts array by a custom order.
 *
 * @param array $input_array Source array.
 *
 * @return array
 */
function sort_array( $input_array ) {
	foreach ( $input_array as $key => $value ) {
		if ( is_array( $value ) ) {

			// @deprecated since version 1.3.0.
			if ( isset( $value['order'] ) && is_int( $value['order'] ) ) {
				$input_array[ $key ]['_order'] = $value['order'];
			} elseif ( ! isset( $value['_order'] ) ) {
				$input_array[ $key ]['_order'] = 0;
			}
		}
	}

	return wp_list_sort( $input_array, '_order', 'ASC', true );
}

/**
 * Gets first array item value.
 *
 * @param array $input_array   Source array.
 * @param mixed $default_value Default value.
 *
 * @return mixed
 * @since 1.3.1
 */
function get_first_array_value( $input_array, $default_value = null ) {
	$value = $default_value;

	if ( is_array( $input_array ) && $input_array ) {
		$value = reset( $input_array );
	}

	return $value;
}

/**
 * Merges arrays with mixed values.
 *
 * @return array
 */
function merge_arrays() {
	$merged = array();

	foreach ( func_get_args() as $array ) {
		foreach ( $array as $key => $value ) {
			if ( ! isset( $merged[ $key ] ) || ( ! is_array( $merged[ $key ] ) || ! is_array( $value ) ) ) {
				if ( is_numeric( $key ) ) {
					$merged[] = $value;
				} else {
					$merged[ $key ] = $value;
				}
			} else {
				$merged[ $key ] = merge_arrays( $merged[ $key ], $value );
			}
		}
	}

	return $merged;
}

/**
 * Retrieve post types.
 *
 * @param string $what_to_retrieve The data to retrieve.
 *
 * @return array
 */
function wlc_retrieve_post_type_data( string $what_to_retrieve = '' ): array {
	$post_types = wlc_retrieve_searchable_post_type_objects();
	$data       = array();

	foreach ( $post_types as $post_type ) {
		switch ( $what_to_retrieve ) {
			case 'slug':
				$data[] = $post_type->name;
				break;
			case 'name':
				$data[] = $post_type->label;
				break;
			default:
				$data[] = $post_type;
				break;
		}
	}

	return apply_filters( 'wlc_retrieve_post_type_data', $data, $what_to_retrieve ); //phpcs:ignore
}

/**
 * Check whether a post type is a searchable content type.
 *
 * Mirrors filtering used by block editors to keep only "normal" content
 * post types (post/page/product/CPT) and exclude technical/system types.
 *
 * @param \WP_Post_Type $post_type Post type object.
 *
 * @return bool
 */
function wlc_is_searchable_content_post_type( \WP_Post_Type $post_type ): bool {
	return ! empty( $post_type->name )
		&& ! empty( $post_type->public )
		&& ! empty( $post_type->show_ui )
		&& empty( $post_type->exclude_from_search )
		&& ! empty( $post_type->show_in_nav_menus );
}

/**
 * Retrieve searchable content post type objects.
 *
 * @return array<string,\WP_Post_Type>
 */
function wlc_retrieve_searchable_post_type_objects(): array {
	$post_types = get_post_types( array( 'public' => true ), 'objects' );
	if ( ! is_array( $post_types ) ) {
		return array();
	}

	return array_filter(
		$post_types,
		function ( $post_type ) {
			return $post_type instanceof \WP_Post_Type && wlc_is_searchable_content_post_type( $post_type );
		}
	);
}

/**
 * Retrieve searchable content post type slugs.
 *
 * @return array<int,string>
 */
function wlc_retrieve_searchable_post_type_names(): array {
	return array_keys( wlc_retrieve_searchable_post_type_objects() );
}

/**
 * Retrieve Selected Post Types from CPT Tab.
 *
 * @return array
 */
function wlc_retrieve_selected_post_types_from_cpt_section(): array {
	$data         = wlc_retrieve_post_type_data( 'slug' );
	$cpt_settings = array();

	$options = get_option( 'wlc_algolia_settings_autocomplete', array() );

	foreach ( $data as $post_type ) {
		if ( ! empty( $options[ $post_type ]['enable'] ) ) {
			$cpt_settings[] = $post_type;
		}
	}

	return $cpt_settings;
}

/**
 * Prepare Selected Post Types from CPT Tab.
 *
 * @param array $cpt_settings Array of selected posts and attributes.
 *
 * @return array
 */
function wlc_prepare_selected_post_types_from_cpt_section( array $cpt_settings ): array {
	return array_map(
		function ( $settings ) {
			foreach ( $settings as $attributes ) {
				if ( isset( $attributes['index'] ) ) {
					if ( '1' === $attributes['index'] ) {
							return $settings['selected_post_type'];
					}
				}
			}

			return '';
		},
		$cpt_settings
	);
}

/**
 * Removes WLC Algolia prefix.
 *
 * @param mixed $names Names to unprefix.
 *
 * @return mixed
 */
function unprefix( $names ) {
	$unprefixed = '';

	if ( is_array( $names ) ) {
		$unprefixed = array_map(
			function ( $name ) {
				return preg_replace( '/^wlc_algolia_/', '', $name );
			},
			$names
		);
	} else {
		$unprefixed = preg_replace( '/^wlc_algolia_/', '', $names );
	}

	return $unprefixed;
}

/**
 * Render settings file.
 *
 * @param string $file_name the file name.
 * @param array  $args Array of args.
 *
 * @return bool
 */
function render_settings_file( string $file_name, array $args ): bool {
	ob_start();
	$params = prepare_params_array_to_include( $args );
	extract( $params, EXTR_SKIP ); //phpcs:ignore

	// Build path safely (file name is internal, but still sanitize it for filesystem usage).
	$relative = ltrim( (string) $file_name, "/\\ \t\n\r\0\x0B" );
	$relative = str_replace( '\\', '/', $relative );

	// Prevent traversal.
	if ( str_contains( $relative, '..' ) ) {
		return ob_end_flush();
	}

	// Allow subdirectories like `schema/attributes` by sanitizing each segment.
	$segments           = array_filter( explode( '/', $relative ) );
	$segments           = array_map( 'sanitize_file_name', $segments );
	$sanitized_relative = implode( '/', $segments );

	$file = WLC_ALGOLIA_PLUGIN_DIR_PATH . 'includes/templates/admin/' . $sanitized_relative . '.php';

	if ( file_exists( $file ) ) {
		include $file;
	}

	return ob_end_flush();
}

/**
 * Prepare params to include.
 *
 * @param array $args Array of args.
 *
 * @return array
 */
function prepare_params_array_to_include( array $args ): array {
	$output = array();
	foreach ( $args as $key => $value ) {
		if ( '' !== $key ) {
			$safe_key = sanitize_key( (string) $key );
			if ( is_array( $value ) ) {
				$output[ $safe_key ] = $value;
			} else {
				$output[ $safe_key ] = sanitize_text_field( (string) $value );
			}
		}
	}

	return $output;
}

/**
 * Filter text.
 *
 * @param string $text text to be sanitized.
 *
 * @return string
 */
function wlc_ksess_text( string $text ): string {
	return wp_kses(
		$text,
		array(
			'a'      => array(
				'href'  => true,
				'class' => true,
				'id'    => true,
			),
			'p'      => array(
				'class' => true,
			),
			'div'    => array(
				'class' => true,
			),
			'strong' => array(
				'class' => true,
			),
		)
	);
}

/**
 * Set section page slug.
 *
 * @return string
 */
function wlc_sections_page_slug(): string {
	$slug            = '';
	$current_section = Algolia_Index_Helper::wlc_retrieve_current_section_name();

	// Only add section slug if there's a current section.
	// Translations is now a separate tab, no longer a subtab of general.
	if ( $current_section ) {
		$slug = '_' . $current_section;//phpcs:ignore
	}

	return esc_attr( $slug );
}

/**
 * Get Settings Tab.
 *
 * @return string
 */
function wlc_get_settings_tab(): string {
	$tabs = array_keys( sort_array( \wlc_algolia\WLC_Algolia::instance()->get_config( 'settings' ) ) );

	$first_tab   = get_first_array_value( $tabs );
	$current_tab = get_array_value( $_GET, 'tab', $first_tab ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended

	// Set the default tab.
	if ( ! in_array( $current_tab, $tabs, true ) ) {
		$current_tab = $first_tab;
	}

	return $current_tab;
}

//phpcs:disable
/**
 * Settings form.
 *
 * PHPCS Fails when it sees TODO Why?
 *
 * TODO: Maybe it needs to be divided by appropriate pages: partials/page-general.php partials/page-post-types.php partials/page-cpt.php ?
 *
 * @return void
 */
function wlc_do_settings_section_fields(): void {
	//phpcs:enable
	$section         = wlc_sections_page_slug();
	$tab             = wlc_get_settings_tab();
	$current_section = Algolia_Index_Helper::wlc_retrieve_current_section_name();
	$query_arg       = '';
	if ( $current_section ) {
		$query_arg = esc_attr( '&section=' . $current_section ); //phpcs:ignore
	}

	// Add extra form class for styling.
	$form_class = $tab;

	?>
	<form method="POST" class="<?php echo esc_attr( $form_class ); ?>"<?php //phpcs:ignore ?>
			action="<?php echo esc_url( admin_url( 'options.php?tab=' . $tab . $query_arg ) ); ?>"
		  data-component="form"><?php //phpcs:ignore ?>
	<?php

	do_action( 'wlc_algolia_sections_' . $tab );//phpcs:ignore

	do_action( 'wlc_algolia_settings', $tab );//phpcs:ignore

	do_settings_sections( 'wlc_algolia_settings_' . esc_attr( $tab . $section ) );//phpcs:ignore
	settings_fields( 'wlc_algolia_settings_' . esc_attr( $tab . $section ) );//phpcs:ignore
	// Use default wrapper (<p class="submit">) so our custom styling can target it consistently.
	submit_button( __( 'Save Changes', 'wlc-search-integration-with-algolia' ), 'primary', 'submit', true, array( 'class' => 'wlc-algolia-save-button' ) );
	?>
	</form><?php //phpcs:ignore ?>
	<?php
}
