<?php
/**
 * WooCommerce App
 *
 * @category   WordPress_Plugin
 * @package    Common
 * @subpackage Suppliers
 * @author     miniOrange <info@miniorange.com>
 * @license    MIT/Expat
 * @link       https://plugins.miniorange.com/woocommerce-api-product-sync-with-woocommerce-rest-apis
 */

namespace MOWCPS\Common\Suppliers;

use WP_Error;
use MOWCPS\Common\Abstracts\Base_Supplier_App;
use MOWCPS\Common\Interfaces\Supplier_App_Interface;
use MOWCPS\Common\Constants\Constants;
use MOWCPS\Common\Services\API\API_Service;
use MOWCPS\Common\Services\Authorization\Authorization_Service;
use MOWCPS\Common\Utils\Utils;
use MOWCPS\Common\Constants\Plugin_Filters;
use MOWCPS\Common\Models\Sync_Details_DAO;
use MOWCPS\Common\Models\Connection_Model;

if ( ! defined( 'ABSPATH' ) ) {
	die;
}

/**
 * Class WooCommerce
 *
 * Handles WooCommerce supplier app operations
 */
class Woocommerce extends Base_Supplier_App implements Supplier_App_Interface {
	/**
	 * Constructor
	 */
	public function __construct() {
		parent::__construct( Constants::SUPPLIER_APP_TYPE_WOOCOMMERCE );
	}

	/**
	 * Perform Woocommerce-specific connection test
	 *
	 * @param array $supplier_data Supplier data.
	 * @return array Test result.
	 */
	public function perform_connection_test( $supplier_data ) {
		// Validate supplier-specific configuration using existing validation method.
		$validation_result = $this->validate_supplier_specific_configuration( $supplier_data );
		if ( is_wp_error( $validation_result ) ) {
			return $this->get_test_result(
				false,
				sprintf( Constants::SUPPLIER_AUTH_FAILED, $this->supplier_name, $validation_result->get_error_message() ),
			);
		}

		// Load and process configuration with dynamic parameters.
		$processed_config = $this->load_and_process_configuration( $supplier_data );
		if ( is_wp_error( $processed_config ) ) {
			return $this->get_test_result(
				false,
				sprintf( Constants::SUPPLIER_CONFIG_LOADING_FAILED, $this->supplier_name, $processed_config->get_error_message() ),
			);
		}

		// Get authorization configuration.
		$authorization = $processed_config[ Constants::AUTHORIZATION ] ?? array();
		if ( empty( $authorization ) ) {
					return $this->get_test_result(
						false,
						sprintf( Constants::SUPPLIER_NO_AUTH_CONFIG, $this->supplier_name ),
					);
		}

		// Use Authorization Service to generate headers.
		$auth_service = new Authorization_Service();
		$headers      = $auth_service->generate_headers( $authorization, 0 );

		if ( is_wp_error( $headers ) ) {
			return $this->get_test_result(
				false,
				sprintf( Constants::SUPPLIER_AUTH_FAILED, $this->supplier_name, $headers->get_error_message() ),
			);
		}

		// Test with the highest priority connection (0 or 1).
		$connections     = $processed_config['connections'] ?? array();
		$test_connection = $this->find_test_connection( $connections );

		if ( empty( $test_connection ) ) {
					return $this->get_test_result(
						false,
						sprintf( Constants::SUPPLIER_NO_TEST_ENDPOINT, $this->supplier_name ),
					);
		}

		$config   = $test_connection['configuration'];
		$endpoint = add_query_arg( 'per_page', 1, $config['endpoint'] );
		$method   = $config['method'];
		$headers  = array_merge( $headers, $config['headers'] );
		$body     = $config['body'];

		if ( empty( $endpoint ) ) {
			return $this->get_test_result(
				false,
				sprintf( Constants::SUPPLIER_NO_TEST_ENDPOINT_AVAILABLE, $this->supplier_name ),
			);
		}

		// Prepare API request.
		$api_request = array(
			'endpoint' => $endpoint,
			'method'   => $method,
			'headers'  => $headers,
			'body'     => $body,
		);

		// Make the authentication request.
		$response = API_Service::make_api_call( $api_request['endpoint'], $api_request );

		$response_code    = wp_remote_retrieve_response_code( $response );
		$response_message = wp_remote_retrieve_response_message( $response );
		$response_body    = json_decode( wp_remote_retrieve_body( $response ), true );

		if ( is_wp_error( $response ) ) {
			return $this->get_test_result(
				false,
				sprintf( Constants::SUPPLIER_AUTH_FAILED, $this->supplier_name, $response->get_error_message() ),
				$response_code,
				$response_body,
			);
		}

		if ( ! Utils::is_status_success( $response ) ) {
			return $this->get_test_result(
				false,
				sprintf( Constants::SUPPLIER_AUTH_FAILED, $this->supplier_name, $response_message ),
				$response_code,
				$response_body,
			);
		}

		return $this->get_test_result(
			true,
			sprintf( Constants::SUPPLIER_AUTH_SUCCESSFUL, $this->supplier_name ),
			$response_code,
			$response_body,
		);
	}

	/**
	 * Validate Woocommerce-specific configuration
	 *
	 * @param array $supplier_data Supplier data to validate.
	 * @return bool|WP_Error True if valid, WP_Error if invalid.
	 */
	public function validate_supplier_specific_configuration( $supplier_data ) {
		// Validate required fields for Woocommerce.
		if ( empty( $supplier_data['domain'] ) ) {
			return new WP_Error(
				Constants::ERROR_INVALID_SUPPLIER_CONFIG,
				sprintf( Constants::SUPPLIER_MISSING_DOMAIN, $this->supplier_name ),
				array( 'status' => Constants::HTTP_BAD_REQUEST )
			);
		}

		if ( empty( $supplier_data['customer_key'] ) ) {
			return new WP_Error(
				Constants::ERROR_INVALID_SUPPLIER_CONFIG,
				sprintf( Constants::SUPPLIER_MISSING_CUSTOMER_KEY, $this->supplier_name ),
				array( 'status' => Constants::HTTP_BAD_REQUEST )
			);
		}

		if ( empty( $supplier_data['customer_secret'] ) ) {
			return new WP_Error(
				Constants::ERROR_INVALID_SUPPLIER_CONFIG,
				sprintf( Constants::SUPPLIER_MISSING_CUSTOMER_SECRET, $this->supplier_name ),
				array( 'status' => Constants::HTTP_BAD_REQUEST )
			);
		}

		return true;
	}

	/**
	 * Modify categories
	 *
	 * @param array $arguments Arguments.
	 * @return array Modified categories.
	 */
	public static function modify_categories( $arguments ) {
		if ( Plugin_Filters::EVENT_ALTER_PRODUCTS_PRE_CATEGORY === $arguments['event_specifier'] ) {
			$sub_categories = $arguments['sub_categories'];
			if ( ! empty( $sub_categories[0] ) ) {
				$arguments['categories'] = $sub_categories[0];
			}
		}
		return $arguments;
	}

	/**
	 * Form category for WooCommerce
	 *
	 * @param array $arguments Arguments.
	 * @return array Modified categories.
	 */
	public static function form_category_for_woocommerce( $arguments ) {
		if ( Plugin_Filters::EVENT_FORM_CATEGORY_FOR_WOOCOMMERCE === $arguments['event_specifier'] ) {
			if ( self::is_woocommerce_category_format( $arguments['categories'] ) ) {
				$product_categories               = self::build_category_hierarchy_from_woocommerce_api( $arguments['categories'] );
				$arguments['categories']          = $product_categories['hierarchy'];
				$arguments['original_categories'] = $product_categories['categories_to_map'];
			}
		}
		return $arguments;
	}

	/**
	 * Check if categories are in WooCommerce REST API format.
	 *
	 * @param array $categories Categories array to check.
	 * @return bool True if in WooCommerce format, false otherwise.
	 */
	private static function is_woocommerce_category_format( $categories ) {
		if ( ! is_array( $categories ) || empty( $categories ) ) {
			return false;
		}

		$first_item = reset( $categories );
		if ( ! is_array( $first_item ) ) {
			return false;
		}

		return isset( $first_item['id'] ) && isset( $first_item['name'] ) && isset( $first_item['slug'] );
	}

	/**
	 * Build category hierarchy from WooCommerce REST API format by fetching parent info from remote store.
	 * Uses cache to avoid repeated API calls.
	 *
	 * @param array $wc_categories Array of category objects from WooCommerce API.
	 * @return array Hierarchical category structure compatible with set_categories.
	 */
	public static function build_category_hierarchy_from_woocommerce_api( $wc_categories ) {
		$hierarchy         = array();
		$category_map      = array();
		$categories_to_map = array();

		$category_ids_to_fetch = array();
		foreach ( $wc_categories as $category ) {
			if ( ! isset( $category['id'] ) || ! isset( $category['name'] ) ) {
				continue;
			}

			$category_id                         = intval( $category['id'] );
			$category_name                       = sanitize_text_field( $category['name'] );
			$categories_to_map[ $category_name ] = true;

			// Need to fetch from API.
			$category_ids_to_fetch[]      = $category_id;
			$category_map[ $category_id ] = array(
				'id'        => $category_id,
				'name'      => $category_name,
				'parent_id' => 0,
			);
		}

		$category_map = array();
		$category_map = self::fetch_categories_from_remote_store( $category_ids_to_fetch, $category_map );

		// Build hierarchy structure from parent relationships.
		$processed = array();
		foreach ( $category_map as $cat_id => $cat_data ) {
			$cat_name  = $cat_data['name'];
			$parent_id = $cat_data['parent_id'];

			// Skip if already processed.
			if ( isset( $processed[ $cat_id ] ) ) {
				continue;
			}

			// If this category has a parent in our list, add it as child.
			if ( $parent_id > 0 && isset( $category_map[ $parent_id ] ) ) {
				$parent_name = $category_map[ $parent_id ]['name'];

				if ( ! isset( $hierarchy[ $parent_name ] ) ) {
					$hierarchy[ $parent_name ] = array();
				}

				// Add this category as child of its parent.
				if ( ! in_array( $cat_name, $hierarchy[ $parent_name ], true ) ) {
					$hierarchy[ $parent_name ][] = $cat_name;
				}

				$processed[ $cat_id ] = true;
			} else {
				// Check if it has children in our category list.
				$children = array();
				foreach ( $category_map as $child_id => $child_data ) {
					if ( $child_data['parent_id'] === $cat_id ) {
						$children[]             = $child_data['name'];
						$processed[ $child_id ] = true;
					}
				}

				if ( ! empty( $children ) ) {
					$hierarchy[ $cat_name ] = $children;
				} else {
					// Standalone category with no children in our list.
					$hierarchy[ $cat_name ] = array();
				}

				$processed[ $cat_id ] = true;
			}
		}

		$cached_categories = self::load_cached_categories();
		foreach ( $category_map as $cat_id => $cat_data ) {
			if ( ! isset( $processed[ $cat_id ] ) ) {
				$cat_name  = $cat_data['name'];
				$parent_id = $cat_data['parent_id'];

				if ( $parent_id > 0 ) {
					// Check if parent exists in cache.
					if ( isset( $cached_categories[ $parent_id ] ) ) {
						$parent_name = $cached_categories[ $parent_id ]['name'];
						if ( ! isset( $hierarchy[ $parent_name ] ) ) {
							$hierarchy[ $parent_name ] = array();
						}
						if ( ! in_array( $cat_name, $hierarchy[ $parent_name ], true ) ) {
							$hierarchy[ $parent_name ][] = $cat_name;
						}
					} else {
						// Parent not in cache and not in current product categories, treat as top-level.
						$hierarchy[ $cat_name ] = array();
					}
				} else {
					// No parent, treat as top-level.
					$hierarchy[ $cat_name ] = array();
				}
			}
		}

		self::save_hierarchy_mapping( $hierarchy, $category_map );

		return array(
			'categories_to_map' => $categories_to_map,
			'hierarchy'         => $hierarchy,
		);
	}

	/**
	 * Save category hierarchy mapping to a JSON file for reference.
	 *
	 * @param array $hierarchy   Hierarchical structure.
	 * @param array $category_map Category map with IDs and parent relationships.
	 * @return bool True on success, false on failure.
	 */
	private static function save_hierarchy_mapping( $hierarchy, $category_map ) {
		$connection_id = Sync_Details_DAO::get_instance()->get_connection_id();

		$cache_dir = MOWCPS_PLUGIN_DIR . 'data' . DIRECTORY_SEPARATOR . 'category-cache';

		if ( ! file_exists( $cache_dir ) ) {
			wp_mkdir_p( $cache_dir );
		}

		$hierarchy_file = $cache_dir . DIRECTORY_SEPARATOR . 'hierarchy_' . $connection_id . '.json';

		$hierarchy_data = array(
			'last_updated'       => current_time( 'mysql' ),
			'hierarchy'          => $hierarchy,
			'category_relations' => array(),
		);

		foreach ( $category_map as $cat_id => $cat_data ) {
			$hierarchy_data['category_relations'][ $cat_id ] = array(
				'id'          => $cat_data['id'],
				'name'        => $cat_data['name'],
				'parent_id'   => $cat_data['parent_id'],
				'parent_name' => ( $cat_data['parent_id'] > 0 && isset( $category_map[ $cat_data['parent_id'] ] ) )
					? $category_map[ $cat_data['parent_id'] ]['name']
					: null,
			);
		}

		$json_data = wp_json_encode( $hierarchy_data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES );

		if ( false === $json_data ) {
			return false;
		}

		return Utils::store_data_in_file( basename( $hierarchy_file ), $cache_dir, $json_data );
	}

	/**
	 * Fetch multiple categories from remote WooCommerce store.
	 *
	 * @param array $category_ids Array of category IDs to fetch.
	 * @return array Map of category_id => array('id', 'name', 'parent_id').
	 */
	private static function fetch_categories_from_remote_store( $category_ids, &$all_category_ids ) {
		$fetched_categories = array();

		// Load cached category data.
		$cached_categories = self::load_cached_categories();

		$category_ids_to_fetch = array();

		foreach ( $category_ids as $category_id ) {
			// Check if category exists in cache.
			if ( isset( $cached_categories[ $category_id ] ) ) {
				$all_category_ids[ $category_id ] = $cached_categories[ $category_id ];
			} else {
				$category_ids_to_fetch[] = $category_id;
			}
		}

		if ( ! empty( $category_ids_to_fetch ) ) {

			$dao         = Sync_Details_DAO::get_instance();
			$supplier_id = $dao->get_supplier_id();

			$category_connection = $dao->get_connection_details(
				Connection_Model::get_connection_id_by_name( $supplier_id, 'Categories' )
			);

			if ( ! $category_connection ) {
				return;
			}

			$authorization_config = $dao->get_authorization_details( $supplier_id );
			$category_config      = $category_connection['configuration'];

			if ( $category_connection['configuration']['auth_enabled'] ) {
				$auth_service = new Authorization_Service();
				$headers      = $auth_service->generate_headers( $authorization_config, $supplier_id );
			} else {
				$headers = array();
			}

			$headers = array_merge( $headers, $category_config['headers'] );
			$body    = $category_config['body'];

			// Replace categoryID placeholder with single categoryID for this product.
			$endpoint    = str_replace( '{{categoryID}}', implode( ',', $category_ids_to_fetch ), $category_config['endpoint'] );
			$api_request = array(
				'endpoint' => $endpoint,
				'method'   => $category_config['method'],
				'headers'  => $headers,
				'body'     => $body['data'] ?? '',
			);

			// Make API call.
			$response = API_Service::make_api_call( $api_request['endpoint'], $api_request );

			$parent_category_ids = array();
			$fetched_categories  = array();
			if ( ! is_wp_error( $response ) ) {
				$response_code = wp_remote_retrieve_response_code( $response );
				if ( 200 === $response_code ) {
					$body            = wp_remote_retrieve_body( $response );
					$categories_data = json_decode( $body, true );
					foreach ( $categories_data as $category_data ) {
						if ( ! empty( $category_data ) && is_array( $category_data ) ) {
							$fetched_categories[ $category_data['id'] ] = array(
								'id'        => intval( $category_data['id'] ),
								'name'      => sanitize_text_field( $category_data['name'] ),
								'parent_id' => isset( $category_data['parent'] ) ? intval( $category_data['parent'] ) : 0,
							);

							if ( $category_data['parent'] > 0 ) {
								$parent_category_ids[] = $category_data['parent'];
							}
						}
					}

					if ( ! empty( $fetched_categories ) ) {
						self::save_cached_categories( $fetched_categories );
					}

					$new_categories   = array();
					$final_parent_ids = array_unique( array_diff( $parent_category_ids, $category_ids ) );

					if ( ! empty( $final_parent_ids ) ) {
						$new_categories = self::fetch_categories_from_remote_store( $final_parent_ids, $all_category_ids );
					}
					$all_category_ids = $all_category_ids + $fetched_categories + $new_categories;
				}
			}
		} else {
			$all_category_ids = $all_category_ids + $cached_categories;
		}

		return $all_category_ids;
	}

	/**
	 * Get cache file path for category hierarchies.
	 *
	 * @return string Cache file path.
	 */
	private static function get_category_cache_file_path() {
		$connection_id = Sync_Details_DAO::get_instance()->get_connection_id();

		$cache_dir = MOWCPS_PLUGIN_DIR . 'data' . DIRECTORY_SEPARATOR . 'category-cache';

		if ( ! file_exists( $cache_dir ) ) {
			wp_mkdir_p( $cache_dir );
		}

		return $cache_dir . DIRECTORY_SEPARATOR . 'categories_' . $connection_id . '.json';
	}

	/**
	 * Load cached category data from file.
	 *
	 * @return array Map of category_id => array('id', 'name', 'parent_id').
	 */
	private static function load_cached_categories() {
		$cache_file = self::get_category_cache_file_path();

		if ( ! file_exists( $cache_file ) ) {
			return array();
		}

		$file_data = Utils::retrieve_data_from_file( $cache_file );
		if ( empty( $file_data ) ) {
			return array();
		}

		$cached_data = json_decode( $file_data, true );
		if ( ! is_array( $cached_data ) ) {
			return array();
		}

		return $cached_data;
	}

	/**
	 * Save category data to cache file.
	 *
	 * @param array $categories Map of category_id => array('id', 'name', 'parent_id').
	 * @return bool True on success, false on failure.
	 */
	private static function save_cached_categories( $categories ) {
		$cached_categories = self::load_cached_categories();
		$new_categories    = $categories + $cached_categories;
		$cache_file        = self::get_category_cache_file_path();
		$json_data         = wp_json_encode( $new_categories, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES );

		if ( false === $json_data ) {
			return false;
		}

		$cache_dir = dirname( $cache_file );
		if ( ! file_exists( $cache_dir ) ) {
			wp_mkdir_p( $cache_dir );
		}

		return Utils::store_data_in_file( basename( $cache_file ), $cache_dir, $json_data );
	}
}
