<?php
/**
 * Trends 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\Utils\Utils;
use MOWCPS\Common\Abstracts\Base_Supplier_App;
use MOWCPS\Common\Interfaces\Supplier_App_Interface;
use MOWCPS\Common\Constants\Constants;
use MOWCPS\Common\Constants\Plugin_Filters;
use MOWCPS\Common\Services\API\API_Service;
use MOWCPS\Common\Constants\Suppliers\Trends as Trends_Constants;
use MOWCPS\Common\Models\Connection_Model;
use MOWCPS\Common\Models\Sync_Details_DAO;
use MOWCPS\Common\Services\Authorization\Authorization_Service;

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

/**
 * Class Trends
 *
 * Handles Trends supplier app operations
 */
class Trends extends Base_Supplier_App implements Supplier_App_Interface {

	/**
	 * Constructor
	 */
	public function __construct() {
		parent::__construct( Constants::SUPPLIER_APP_TYPE_TRENDS );
	}

	/**
	 * Filter out variations with empty stock_code to prevent creating variable products without valid variations
	 *
	 * @param array $data Filter data containing event_specifier and variation_array.
	 * @return array Modified filter data.
	 */
	public static function filter_empty_variations( $data ) {
		// Only process variation data events.
		if ( ! isset( $data['event_specifier'] ) || Plugin_Filters::EVENT_VARIATION_PRODUCTS_DATA_ARRAY_INITIAL !== $data['event_specifier'] ) {
			return $data;
		}

		// Check if we have variations to filter.
		if ( ! isset( $data['variation_array'] ) || ! is_array( $data['variation_array'] ) ) {
			return $data;
		}

		// Filter out variations where stock_code is empty or not set.
		$filtered_variations = array_filter(
			$data['variation_array'],
			function ( $variation ) {
				// Ensure variation is an array or object.
				$variation_array = is_object( $variation ) ? (array) $variation : $variation;

				// Keep only variations with non-empty stock_code.
				return ! empty( $variation_array['stock_code'] );
			}
		);

		// Re-index the array to avoid gaps in numeric keys.
		$data['variation_array'] = array_values( $filtered_variations );

		return $data;
	}

	/**
	 * Perform Trends-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 the Categories endpoint to test connection.
		$connections = $processed_config[ Constants::CONNECTIONS ] ?? array();
		if ( empty( $connections ) ) {
			return $this->get_test_result(
				false,
				sprintf( Constants::SUPPLIER_NO_CONNECTIONS, $this->supplier_name ),
			);
		}

		// Find Categories connection for testing.
		$categories_connection = null;
		foreach ( $connections as $connection ) {
			if ( 'Categories' === $connection['name'] ) {
				$categories_connection = $connection;
				break;
			}
		}

		if ( ! $categories_connection ) {
			return $this->get_test_result(
				false,
				sprintf( 'Categories connection not found in %s configuration', $this->supplier_name ),
			);
		}

		$config = $categories_connection['configuration'];

		// Prepare authorization headers using Authorization Service.
		$auth_config = $processed_config[ Constants::AUTHORIZATION ] ?? array();
		if ( empty( $auth_config ) ) {
			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();
		$auth_headers = $auth_service->generate_headers( $auth_config, 0 );

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

		// Prepare request data from configuration.
		$url     = $config['endpoint'];
		$method  = strtoupper( $config['method'] );
		$headers = $config['headers'] ?? array();
		$headers = array_merge( $auth_headers, $headers );

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

		// Make the test 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 Trends-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 Trends.
		if ( empty( $supplier_data['username'] ) ) {
			return new WP_Error(
				Constants::ERROR_INVALID_SUPPLIER_CONFIG,
				sprintf( Constants::SUPPLIER_MISSING_USERNAME, $this->supplier_name ),
				array( 'status' => Constants::HTTP_BAD_REQUEST )
			);
		}

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

		// Validate username format.
		if ( ! is_string( $supplier_data['username'] ) || strlen( $supplier_data['username'] ) < 3 ) {
			return new WP_Error(
				Constants::ERROR_INVALID_SUPPLIER_CONFIG,
				sprintf( Constants::SUPPLIER_INVALID_USERNAME, $this->supplier_name ),
				array( 'status' => Constants::HTTP_BAD_REQUEST )
			);
		}

		return true;
	}

	/**
	 * Function to sync categories from Trends to WooCommerce.
	 *
	 * @param array $arguments Cron arguments.
	 *
	 * @return void
	 */
	public static function sync_categories( $arguments ) {
		$external_api_config = Connection_Model::get_by_id( $arguments['connection_id'] );
		if ( empty( $external_api_config ) ) {
			return;
		}

		if ( Trends_Constants::PRODUCTS_ENDPOINT === $external_api_config['configuration']['endpoint'] ) {
			$request = array(
				'method'  => $external_api_config['configuration']['method'] ?? 'GET',
				'headers' => $external_api_config['configuration']['headers'] ?? array(),
				'body'    => $external_api_config['configuration']['body']['data'] ?? array(),
			);

			$auth_details       = Sync_Details_DAO::get_instance()->get_authorization_details( $arguments['supplier_id'] );
			$auth_services      = new Authorization_Service();
			$auth_header        = $auth_services->generate_headers( $auth_details, $arguments['supplier_id'] );
			$request['headers'] = array_merge( $auth_header, $request['headers'] ?? array() );
			$response           = API_Service::make_api_call( Trends_Constants::CATEGORY_ENDPOINT, $request );

			if ( is_wp_error( $response ) ) {
				return;
			}

			$data = json_decode( wp_remote_retrieve_body( $response ), true );

			if ( ! empty( $data['data'] ) && is_array( $data['data'] ) ) {
				self::create_categories( $data['data'] );
			}
		}
	}

	/**
	 * Set stock status based on connection for Trends products (for FILTER_PRODUCT_SAVE_FILTER).
	 * This hook receives $mo_product as first parameter.
	 *
	 * @param object            $mo_product    Product object (can be variable parent, variable child, or simple product).
	 * @param string            $operation     Operation to be performed.
	 * @param WC_Product|string $wc_product_obj WC Product object or empty string.
	 *
	 * @return mixed Return value to continue filter chain (should be handled by default handler).
	 */
	public static function set_stock_status_by_connection_for_save( $mo_product, $operation = '', $wc_product_obj = '' ) {
		// Use the main function to set stock status (it calls the shared helper).
		self::set_stock_status_on_product( $mo_product );
		return $mo_product;
	}

	/**
	 * Set stock status based on connection for Trends products (for FILTER_UPDATE_PRO_PRODUCT).
	 *
	 * @param WC_Product $product    WC Product object.
	 * @param object     $mo_product Product object (can be variable parent, variable child, or simple product).
	 * @param string     $operation  Operation to be performed.
	 *
	 * @return mixed Return value to continue filter chain (should be handled by default handler).
	 */
	public static function set_stock_status_by_connection( $product, $mo_product, $operation = '' ) {
		// Set stock status on the product using shared helper.
		self::set_stock_status_on_product( $mo_product );
		return $product;
	}

	/**
	 * Set stock status on product based on connection for Trends products.
	 * - Products connection: Always set to 'instock'
	 * - Stock connection: Use quantity-based logic (0 = outofstock, >0 = instock)
	 *
	 * @param object $mo_product Product object (can be variable parent, variable child, or simple product).
	 *
	 * @return void
	 */
	private static function set_stock_status_on_product( $mo_product ) {
		// Only process for Trends supplier.
		$supplier_id      = Sync_Details_DAO::get_instance()->get_supplier_id();
		$supplier_details = Sync_Details_DAO::get_instance()->get_supplier_details( $supplier_id );

		if ( empty( $supplier_details ) || Constants::SUPPLIER_APP_TYPE_TRENDS !== $supplier_details['app_type'] ) {
			return;
		}

		// Get connection_id from DAO.
		$connection_id   = Sync_Details_DAO::get_instance()->get_connection_id();
		$connection_name = '';

		if ( ! empty( $connection_id ) ) {
			$connection_name = Connection_Model::get_connection_name_by_id( $connection_id );
		}

		if ( 'Products' === $connection_name ) {
			$is_variable_parent = (
				isset( $mo_product->structure ) &&
				Constants::VARIABLE === $mo_product->structure &&
				empty( $mo_product->parent_product_id )
			);

			// Set stock_status to 'instock' for all products except variable parent products.
			if ( ! $is_variable_parent ) {
				$mo_product->stock_status = 'instock';
			}
		}
	}

	/**
	 * Transform product data to convert category IDs to category names (similar to Amrod).
	 *
	 * @param array $data Filter data containing event_specifier and product_data.
	 * @return array Modified filter data.
	 */
	public static function transform_product_data( $data ) {
		if ( Plugin_Filters::EVENT_ALTER_PRODUCTS_CATEGORY === $data['event_specifier'] ) {
			// Transform category objects to category slugs.
			// Extract categories from MO_NO_OPERATION key.
			if ( ! empty( $data['categories'] ) && isset( $data['categories'][ Constants::MO_NO_OPERATION ] ) ) {
				$categories = $data['categories'][ Constants::MO_NO_OPERATION ];

				// When mapping is "categories", the full array is stored nested: [[{...}, {...}]]
				// Flatten: extract first element if it's an array of category objects.
				if ( is_array( $categories ) && isset( $categories[0] ) && is_array( $categories[0] ) ) {
					$category_list = $categories[0];
				} else {
					$category_list = is_array( $categories ) ? $categories : array( $categories );
				}

				$curated_categories = array();
				$category_slugs     = array();

				foreach ( $category_list as $category ) {
					// Convert to array if object.
					$category_array = is_array( $category ) ? $category : (array) $category;

					// Extract name and parent_id from category object.
					$category_name = $category_array['name'] ?? '';
					$parent_id     = isset( $category_array['parent_id'] ) ? (int) $category_array['parent_id'] : 0;

					if ( ! empty( $category_name ) ) {
						// Form slug: parent_id_child_name pattern (same as create_categories).
						if ( ! empty( $parent_id ) && $parent_id > 0 ) {
							// Child category: parent_id_child_name.
							$slug = $parent_id . '_' . sanitize_title( $category_name );
						} else {
							// Top-level category: use category name.
							$slug = sanitize_title( $category_name );
						}
						$category_slugs[] = $slug;
					}
				}

				$category_slugs = array_unique( $category_slugs );

				if ( ! empty( $category_slugs ) ) {
					$curated_categories[0] = $category_slugs;
					$data['categories']    = $curated_categories;
				}
			}
		}

		return $data;
	}

	/**
	 * Function to create categories and subcategories inside WooCommerce related to Trends.
	 *
	 * @param array $categories      Categories data coming from the category endpoint of Trends.
	 * @param int   $parent_category Parent category term ID if creating subcategories.
	 *
	 * @return void
	 */
	public static function create_categories( $categories, $parent_category = 0 ) {
		foreach ( $categories as $category ) {
			// Determine slug based on parent_id (like Amrod pattern).
			if ( ! empty( $category['parent_id'] ) && $category['parent_id'] > 0 ) {
				// Child category: parent_id_child_name pattern.
				$slug = $category['parent_id'] . '_' . sanitize_title( $category['name'] );
			} else {
				// Top-level category: use category name.
				$slug = sanitize_title( $category['name'] );
			}

			$term = get_term_by( 'slug', $slug, 'product_cat' );

			if ( ! $term ) {
				// Create new category.
				$term = wp_insert_term(
					$category['name'],
					'product_cat',
					array(
						'parent' => $parent_category,
						'slug'   => $slug,
					)
				);
			} else {
				// Update existing category.
				$term = wp_update_term(
					$term->term_id,
					'product_cat',
					array(
						'name'   => $category['name'],
						'parent' => $parent_category,
					)
				);
			}

			// Store category metadata for reference.
			if ( ! is_wp_error( $term ) && isset( $term['term_id'] ) ) {
				update_term_meta( $term['term_id'], 'trends_category_id', $category['id'] );
				update_term_meta( $term['term_id'], 'trends_category_number', $category['number'] ?? '' );
				update_term_meta( $term['term_id'], 'trends_parent_id', $category['parent_id'] ?? '' );

				// Recursively create subcategories.
				if ( ! empty( $category['sub_categories'] ) ) {
					self::create_categories( $category['sub_categories'], $term['term_id'] );
				}
			}
		}
	}
}
