<?php
/**
 * Auto Attribute Mapping Service
 *
 * @category   WordPress_Plugin
 * @package    Common
 * @subpackage Services\Data_Mapping
 * @author     miniOrange <info@miniorange.com>
 * @license    MIT/Expat
 * @link       https://plugins.miniorange.com/woocommerce-api-product-sync-with-woocommerce-rest-apis
 */

namespace MOWCPS\Common\Services\Data_Mapping;

use MOWCPS\Common\Constants\Constants;
use MOWCPS\Common\Utils\Utils;
use MOWCPS\Common\Models\Connection_Model;
use MOWCPS\Common\Utils\Response_Builder;

/**
 * Class Auto_Attribute_Mapping_Service
 *
 * Handles auto attribute mapping service operations
 */
class Auto_Attribute_Mapping_Service {

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

		$file_path = $test_result_directory . DIRECTORY_SEPARATOR . $test_result_file_name;

		return $file_path;
	}

	/**
	 * Get API response of a particular prefix from the API response.
	 *
	 * @param object $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 object $data The JSON response.
	 * @param array  $attribute_keys An array object where corresponding attribute keys are.
	 * @param string $prefix Prefix used to get attribute key.
	 *
	 * @return array returns possible options for simple and variable response attribute.
	 */
	public static function get_parent_attribute( $data, &$attribute_keys = array(), $prefix = '' ) {
		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;
						self::get_parent_attribute( $value, $attribute_keys, $prefix . $key . '->' );
					} else {
						$attribute_keys[] = $prefix . $key;
					}
				}
			}
		}
		return $attribute_keys;
	}

	/**
	 * Check API file availability in the test result directory.
	 *
	 * @param string $connection_id The configured id of the api.
	 * @param object $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 string $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, $call_type = '', $required_data_type = '', $order_req_attributes = '' ) {

		if ( empty( $order_req_attributes ) && 'order_sync' !== $call_type ) {
			$file_path    = self::get_api_file( $connection['id'] );
			$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 ) {
			$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 );
			}

			if ( '' !== $required_data_type ) {
				if ( false !== strpos( $required_data_type, 'mosuggest' ) ) {

					$request_body = $connection['configuration']['body']['data'];
					$data_key     = explode( '@', $required_data_type );
					$data_key     = $data_key[1];

					$required_data_type = '';

					$data = self::get_key_data( $request_body, $data_key );

					if ( isset( $data ) ) {
						if ( array_keys( $data ) !== range( 0, count( $data ) - 1 ) ) {
							$required_data_type = 'object';
						} else {
							$required_data_type = 'array';
						}
					}
				}

				$possible_parent_attributes = self::get_required_data_type_attribute( $required_data_type, $api_response );
			}

			return Response_Builder::success( $possible_parent_attributes );
		} else {
			return Response_Builder::error( Constants::EMPTY_SUGESTION, 'No API response to generate suggestions.' );
		}
	}

	/**
	 * 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 ) && count( $value ) > 0 && array_keys( $value ) !== range( 0, count( $value ) - 1 ) ) {
				$filtered_attributes[] = $key;
			}
		}

		return $filtered_attributes;
	}

	/**
	 * Get attributes for variable response attribute based on simple response attribute.
	 *
	 * @param string $prefix simple response attribute of the connection if any.
	 * @param string $connection_id The configured id of the api.
	 * @param string $call_type The call has been made by php or an ajax call.
	 *
	 * @return json returns json for the simple response attribute.
	 */
	public static function get_variable_attributes( $prefix = '', $connection_id = '', $call_type = '' ) {
		$file_path    = self::get_api_file( $connection_id );
		$api_response = self::get_api_content( $file_path );

		if ( $api_response ) {
			$api_key_data = self::get_key_data( $api_response, $prefix );

			$attribute_keys             = array();
			$possible_parent_attributes = array();

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

			return Response_Builder::success( $possible_parent_attributes );
		}
	}

	/**
	 * Get all the attribute options with respect to simple and variable response attribute.
	 *
	 * @param object $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 object 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 );
			$json_response = json_decode( $json_data, true );
			return $json_response;
		}

		return false;
	}

	/**
	 * 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_id      The configured id 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 datalist options array or ajax json response.
	 */
	public static function get_child_attributes( $prefix = '', $connection_id = '', $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.
		$order_req_attributes = $options['order_req_attributes'];
		$required_data_type   = $options['required_data_type'];

		if ( '' === $prefix && 'php' !== $call_type ) {
			$prefix = isset( $_GET['parent_attribute'] ) ? sanitize_text_field( wp_unslash( $_GET['parent_attribute'] ) ) : '';
		}

		$connection_details = Connection_Model::get_by_id( $connection_id );

		if ( '' === $attribute_type ) {
			$attribute_type = isset( $_GET['attribute_type'] ) ? sanitize_text_field( wp_unslash( $_GET['attribute_type'] ) ) : '';
		} elseif ( Constants::SIMPLE === $attribute_type ) {
			$prefix = $connection_details['configuration']['prerequisites']['product_response'];
		} elseif ( Constants::VARIABLE === $attribute_type ) {
			if ( '' === $connection_details['configuration']['prerequisites']['variable_response']['parent_identifier'] ) {
				$prefix = $connection_details['configuration']['prerequisites']['product_response'];
			} elseif ( '' === $connection_details['configuration']['prerequisites']['product_response'] ) {
				$prefix = $connection_details['configuration']['prerequisites']['variable_response']['parent_identifier'];
			} else {
				$prefix = $connection_details['configuration']['prerequisites']['product_response'] . '->0->' . $connection_details['configuration']['prerequisites']['variable_response']['parent_identifier'];
			}
		}

		$has_order_req_attributes = '';
		if ( '' === $order_req_attributes ) {
			$has_order_req_attributes = isset( $_GET['order_req_attributes'] ) ? sanitize_text_field( wp_unslash( $_GET['order_req_attributes'] ) ) : '';
		}

		if ( 'has_db_attribute' === $has_order_req_attributes ) {
			$saved_config         = Connection_Model::get_by_id( $connection_id );
			$order_req_attributes = array();

			if ( ! empty( $saved_config ) ) {
				if ( isset( $saved_config['configuration']['body']['data'] ) ) {
					$order_req_attributes = $saved_config['configuration']['body']['data'];
				}
			}
		}

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

		$data_keys = array();

		if ( $api_response ) {
			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 );
			}

			return Response_Builder::success( $inner_parent_attributes );
		} else {
			return Response_Builder::error( Constants::EMPTY_SUGESTION, 'No API response to generate suggestions.' );
		}
	}

	/**
	 * 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 = trailingslashit( MOWCPS_PLUGIN_DIR ) . 'resources' . DIRECTORY_SEPARATOR . '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;
	}
}
