<?php
/**
 * This file contains logic of listing out API attributes to ease out attribute mapping.
 *
 * @package    Constants
 * @subpackage Common
 * @author     miniOrange <info@miniorange.com>
 * @link       https://miniorange.com
 */

namespace MOWCPS\Common\Utils;

use MOWCPS\Common\Constants\Constants;
use MOWCPS\Common\Utils\Utils;

/**
 * Displays options for the field mapping attributes for different suppliers.
 */
class Auto_Attribute_Mapping {

	/**
	 * Get API file from the test result directory.
	 *
	 * @param string $connection_name The configured name of the api.
	 *
	 * @return string
	 */
	public static function get_api_file( $connection_name ) {
		$test_result_directory = trailingslashit( MOWCPS_PLUGIN_DIR ) . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR . 'test-results';
		$test_result_file_name = 'test_result_' . $connection_name . '.json';

		$file_path = $test_result_directory . '/' . $test_result_file_name;

		return $file_path;
	}

	/**
	 * Get API response of a particular prefix from the API response.
	 *
	 * @param array|boolean $api_response Actual response from the API.
	 * @param string        $prefix particular path of the key.
	 *
	 * @return array return API response with respect to a particular prefix.
	 */
	public static function get_key_data( $api_response, $prefix = '' ) {
		if ( '' !== $prefix ) {
			$attributes = explode( '->', $prefix );
			foreach ( $attributes as $attribute ) {
				if ( isset( $api_response[ $attribute ] ) ) {
					$api_response = $api_response[ $attribute ];
				}
			}
		}
		return $api_response;
	}

	/**
	 * Display parent field mapping for sync.
	 *
	 * @param array  $data The JSON response.
	 * @param string $prefix Prefix used to get attribute key.
	 * @param array  $attribute_keys An array object where corresponding attribute keys are.
	 *
	 * @return array returns possible options for simple and variable response attribute.
	 */
	public static function get_parent_attribute( $data, $prefix = '', &$attribute_keys = array() ) {
		if ( is_array( $data ) ) {
			foreach ( $data as $key => $value ) {
				if ( is_array( $value ) ) {

					$data_keys = array_keys( $value );
					if ( isset( $value ) && ( isset( $data_keys[0] ) && is_string( $data_keys[0] ) ) ) {
						$attribute_keys[] = $prefix . $key;
						$inner_array      = array_keys( $value );
						self::get_parent_attribute( $value, $prefix . $key . '->', $attribute_keys );
					} else {
						$attribute_keys[] = $prefix . $key;
					}
				}
			}
		}
		return $attribute_keys;
	}

	/**
	 * Check API file availability in the test result directory.
	 *
	 * @param string $connection_name The configured name of the api.
	 * @param string $call_type The call has been made by php or an ajax call.
	 * @param string $required_data_type type of data required in list.
	 * @param object $order_req_attributes The object containing attributes requested by the vendor.
	 * @return string returns html options for the simple response attribute.
	 */
	public static function get_array_and_object_attributes( $connection_name, $call_type = '', $required_data_type = '', $order_req_attributes = '' ) {

		if ( empty( $order_req_attributes ) && 'order_sync' !== $call_type ) {
			$file_path    = self::get_api_file( $connection_name );
			$api_response = self::get_api_content( $file_path );
		} elseif ( empty( $order_req_attributes ) && 'order_sync' === $call_type ) {
			$api_response = self::get_preconfigured_apps_data();
		} else {
			$api_response = $order_req_attributes;
		}

		if ( $api_response ) {

			$api_response_type = Constants::TEST_CONFIGURATION_DISPLAY_TYPE_TABLE;
			$attribute_keys    = array();

			$data_keys = array();

			if ( is_array( $api_response ) ) {
				$data_keys = array_keys( $api_response );
			}

			if ( isset( $data_keys ) && is_array( $data_keys ) && isset( $data_keys[0] ) && ! is_string( $data_keys[0] ) ) {
				$possible_parent_attributes = self::get_parent_attribute( $api_response[0], '', $attribute_keys );
			} else {
				$possible_parent_attributes = self::get_parent_attribute( $api_response, '', $attribute_keys );
			}

			$options_html = '';
			if ( '' !== $required_data_type ) {
				$data                       = self::get_preconfigured_apps_data();
				$possible_parent_attributes = self::get_required_data_type_attribute( $required_data_type, $data );
			}
			foreach ( $possible_parent_attributes as $item ) {
				$options_html .= '<option value="' . esc_attr( $item ) . '">' . esc_html( $item ) . '</option>';
			}
			return $options_html;
		} else {
			return false;
		}
	}

	/**
	 * Check if connection file is available and return appropriate data.
	 *
	 * @param string $connection_name The configured name of the api.
	 * @return string|false Returns html options for the simple response attribute or false if file not found.
	 */
	public static function check_connection_file_availability( $connection_name ) {
		return self::get_array_and_object_attributes( $connection_name );
	}

	/**
	 * Retrieves attributes from the pre-configured app file based on the required data type.
	 *
	 * This method reads a JSON file and filters attributes based on whether they match the specified data type ('array' or 'object').
	 *
	 * @param string $required_data_type The type of data ('array' or 'object') to filter attributes by.
	 * @param array  $data The input data from which to filter attributes.
	 *
	 * @return array $filtered_attributes Array of filtered attributes matching the required data type.
	 */
	public static function get_required_data_type_attribute( $required_data_type, $data ) {
		$api_response        = $data;
		$filtered_attributes = array();
		foreach ( $api_response as $key => $value ) {
			if ( 'array' === $required_data_type && is_array( $value ) && array_keys( $value ) === range( 0, count( $value ) - 1 ) ) {
				$filtered_attributes[] = $key;
			} elseif ( 'object' === $required_data_type && is_array( $value ) && array_keys( $value ) !== range( 0, count( $value ) - 1 ) ) {
				$filtered_attributes[] = $key;
			}
		}

		return $filtered_attributes;
	}

	/**
	 * Get all the attribute options with respect to simple and variable response attribute.
	 *
	 * @param array  $data           The JSON response.
	 * @param array  $attribute_keys Used to save the attributes.
	 * @param string $prefix         Used to get the indented attributes with respect to simple and variable response attribute.
	 *
	 * @return array return all the attributes with respect to simple or variable response attribute.
	 */
	public static function get_all_attributes( $data, &$attribute_keys, $prefix = '' ) {
		if ( is_array( $data ) ) {
			foreach ( $data as $key => $value ) {
				if ( '' === $prefix && ! is_string( $key ) ) {
					$attribute_keys[] = $prefix;
				} else {
					$attribute_keys[] = $prefix . $key;
				}

				if ( is_array( $value ) ) {
					$data_keys = array();
					if ( isset( $value[0] ) && is_array( $value[0] ) ) {
						$data_keys = array_keys( $value[0] );
					}
					if ( count( $data_keys ) > 0 && is_string( $data_keys[0] ) ) {
						self::get_all_attributes( $value[0], $attribute_keys, $prefix . $key . '->0->' );
					} else {
						self::get_all_attributes( $value, $attribute_keys, $prefix . $key . '->' );
					}
				} else {
					self::get_all_attributes( $value, $attribute_keys, $prefix . $key . '->' );
				}
			}
		}

		return $attribute_keys;
	}

	/**
	 * Get API response from the API test result file.
	 *
	 * @param string $file_path Actual path of the file.
	 *
	 * @return array|boolean return connection API response.
	 */
	public static function get_api_content( $file_path ) {
		$file_exists = file_exists( $file_path );
		if ( $file_exists ) {
			$json_data     = file_get_contents( $file_path ); // phpcs:ignore --WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- File is read from the current server and not remote server.
			$json_response = json_decode( $json_data, true );
			$api_response  = $json_response['response'] ?? false;
			return $api_response;
		}

		return false;
	}

	/**
	 * Get attribute options for children of variable and simple response attribute via ajax.
	 *
	 * @return void
	 */
	public static function get_ajax_child_attributes() {
		if ( check_ajax_referer( 'mowcps_parent_attributes_nonce', 'nonce', false ) && current_user_can( 'manage_options' ) ) {
			$connection_name = ! empty( $_GET['connection_name'] ) ? sanitize_text_field( wp_unslash( $_GET['connection_name'] ) ) : false;
			$attribute_type  = ! empty( $_GET['attribute_type'] ) ? sanitize_text_field( wp_unslash( $_GET['attribute_type'] ) ) : false;
			$prefix          = ! empty( $_GET['parent_attribute'] ) ? sanitize_text_field( wp_unslash( $_GET['parent_attribute'] ) ) : '';
			self::get_child_attributes( $prefix, $connection_name, $attribute_type, '', false, array() );
		} else {
			wp_send_json_error( Constants::INVALID_NONCE_ERROR );
		}
	}

	/**
	 * Get attribute options for children of variable and simple response attribute.
	 *
	 * @param string $prefix             Simple or variable response attribute of the connection if any.
	 * @param string $connection_name    The configured name of the API.
	 * @param string $attribute_type     Defines simple or variable attribute.
	 * @param string $call_type          The call has been made by php or an ajax call.
	 * @param bool   $is_filter          If true, the attributes will be transformed.
	 * @param array  $options Additional options like 'order_req_attributes' and 'required_data_type'.
	 *
	 * @return array|string datalist options array or ajax json response.
	 */
	public static function get_child_attributes( $prefix = '', $connection_name = '', $attribute_type = '', $call_type = '', $is_filter = false, $options = array() ) {
		// Set default values for order_req_attributes and required_data_type.
		$defaults = array(
			'order_req_attributes' => '',
			'required_data_type'   => '',
		);

		// Merge passed options with defaults.
		$options = array_merge( $defaults, $options );

		// Extract individual variables from the options array.
		$required_data_type = $options['required_data_type'];

		$file_path    = self::get_api_file( $connection_name );
		$api_response = self::get_api_content( $file_path );

		$data_keys = array();
		if ( is_array( $api_response ) ) {
			$data_keys = array_keys( $api_response );
		}

		$inner_parent_attributes = array();
		$attribute_keys          = array();

		if ( isset( $data_keys ) && ! is_string( $data_keys[0] ) ) {
			$api_key_data = self::get_key_data( $api_response[0], $prefix );
		} else {
			$api_key_data = self::get_key_data( $api_response, $prefix );
		}

		if ( Constants::VARIABLE === $attribute_type ) {
			if ( '' === $prefix ) {
				$inner_parent_attributes = self::get_all_attributes( $api_key_data, $attribute_keys, '' );
			} elseif ( isset( $data_keys ) && ! is_string( $data_keys[0] ) ) {
					$inner_parent_attributes = self::get_all_attributes( $api_key_data, $attribute_keys, '' );
			} elseif ( is_array( $api_key_data ) && isset( $api_key_data[0] ) ) {
				$inner_parent_attributes = self::get_all_attributes( $api_key_data[0], $attribute_keys, '' );
			}
		} elseif ( '' === $prefix ) {
			if ( isset( $data_keys ) && ! is_string( $data_keys[0] ) ) {
				$inner_parent_attributes = self::get_all_attributes( $api_key_data, $attribute_keys, '' );
			} else {
				$inner_parent_attributes = self::get_all_attributes( $api_key_data, $attribute_keys, '' );
			}
		} elseif ( is_array( $api_key_data ) && isset( $api_key_data[0] ) ) {
			$inner_parent_attributes = self::get_all_attributes( $api_key_data[0], $attribute_keys, '' );
		} elseif ( is_array( $api_key_data ) ) {
			$inner_parent_attributes = self::get_all_attributes( $api_key_data, $attribute_keys, '' );
		}

		if ( 'string' === $required_data_type ) {
			$inner_parent_attributes = self::extract_values( $inner_parent_attributes );
		}

		if ( true === $is_filter ) {
			$inner_parent_attributes = self::transform_attributes( $inner_parent_attributes );
		}

		$options_html = '';
		if ( isset( $inner_parent_attributes ) && is_array( $inner_parent_attributes ) ) {
			foreach ( $inner_parent_attributes as $item ) {
				$options_html .= '<option value="' . esc_attr( $item ) . '">' . esc_html( $item ) . '</option>';
			}
		}

		if ( 'php' === $call_type ) {
			return $options_html;
		}

		wp_send_json(
			array(
				'status' => 'success',
				'code'   => 200,
				'data'   => $inner_parent_attributes,
			)
		);
	}

	/**
	 * Extracts values from the provided inner parent attributes.
	 *
	 * This method filters attributes that either contain '->' or ensures that a parent key without '->' is included if no
	 * related child keys exist in the array.
	 *
	 * @param array $inner_parent_attributes Array of inner parent attributes to filter.
	 * @return array $filtered_array         Filtered array of attributes.
	 */
	public static function extract_values( $inner_parent_attributes ) {

		$filtered_array = array();
		foreach ( $inner_parent_attributes as $key ) {
			if ( strpos( $key, '->' ) !== false ) {
				$filtered_array[] = $key;
			} else {
				$parent_key  = explode( '->', $key )[0];
				$has_details = false;

				foreach ( $inner_parent_attributes as $inner_key ) {
					if ( strpos( $inner_key, $parent_key . '->' ) === 0 ) {
						$has_details = true;
						break;
					}
				}

				if ( ! $has_details ) {
					$filtered_array[] = $key;
				}
			}
		}
		return $filtered_array;
	}

	/**
	 * Retrieves the preconfigured application data from a JSON file.
	 *
	 * @return array|null The decoded JSON data as an associative array, or null if the file does not exist.
	 */
	public static function get_preconfigured_apps_data() {
		$pre_configured_apps_file = MOWCPS_PLUGIN_DIR . 'resources/order-sync-default.json';

		if ( file_exists( $pre_configured_apps_file ) ) {
			return json_decode( Utils::retrieve_data_from_file( $pre_configured_apps_file ), true );
		}
		return null;
	}

	/**
	 * Transforms attribute strings by formatting them according to specific rules.
	 *
	 * This function processes each attribute string by breaking it into parts, handling numeric
	 * values, and reassembling it with a specific format for attributes that are not numeric.
	 *
	 * @param array $attributes An array of attribute strings to be transformed.
	 *
	 * @return array The transformed array of attribute strings.
	 */
	public static function transform_attributes( $attributes ) {
		$result = array();

		foreach ( $attributes as $attribute ) {
			$parts            = explode( '->', $attribute );
			$formatted_string = '';
			$last_index       = count( $parts ) - 1;

			foreach ( $parts as $index => $part ) {

				if ( 0 === $index ) {
					// First part (e.g., categories).
					$formatted_string .= $part;
				} elseif ( is_numeric( $parts[ $index ] ) ) {
					continue;
				} elseif ( is_numeric( $parts[ $index - 1 ] ) ) {
					// Previous part is numeric, add as bracketed value if not a number.
					if ( ! is_numeric( $part ) ) {
						$formatted_string .= '->[' . $part . ']';
					}
				} else {
					// Not numeric, add normally.
					$formatted_string .= '->' . $part;
				}
			}
			// Add the formatted string to the result if it's not empty.
			if ( ! empty( $formatted_string ) && $index === $last_index && ! is_numeric( $part ) ) {
				$result[] = $formatted_string;
			}
		}

		return $result;
	}
}
