<?php
/**
 * Toptex 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\Constants\Plugin_Filters;
use MOWCPS\Common\Models\Supplier_Model;
use MOWCPS\Common\Services\API\API_Service;
use MOWCPS\Common\Services\Authorization\Authorization_Service;
use MOWCPS\Common\Utils\Utils;

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

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

	/**
	 * Perform Toptex-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();

		$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 = $config['endpoint'];

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

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

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

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

		$response_code = wp_remote_retrieve_response_code( $response );

		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,
			);
		}

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

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

		return true;
	}

	/**
	 * Load and process configuration with dynamic parameters for Toptex
	 * Overrides parent to add 'lang' parameter from 'preferred_language'
	 *
	 * @param array $details Supplier details for dynamic parameter replacement.
	 * @param array $request_data Request data from API request body.
	 * @return array|WP_Error Processed configuration or error.
	 */
	protected function load_and_process_configuration( $details, $request_data = null ) {
		// Add 'lang' parameter from 'preferred_language' for Toptex.
		if ( isset( $details['preferred_language'] ) ) {
			$details['lang'] = $details['preferred_language'];
		}

		// Call parent method with modified details.
		return parent::load_and_process_configuration( $details, $request_data );
	}

	/**
	 * Check if decoded API response is a timeout/connection error that should trigger retry.
	 *
	 * @param array|mixed $response Decoded response (array or scalar).
	 * @return bool True if response is a timeout or ETIMEDOUT error.
	 */
	private static function is_timeout_or_connection_error( $response ) {
		if ( ! is_array( $response ) ) {
			return false;
		}
		if ( isset( $response['message'] ) && $response['message'] === 'Endpoint request timed out' ) {
			return true;
		}
		if ( isset( $response['errorMessage'] ) && $response['errorMessage'] === 'connect ETIMEDOUT' ) {
			return true;
		}
		return false;
	}

	/**
	 * On alter_processed_api_data_with_request: if Toptex and response is timeout/ETIMEDOUT, retry using
	 * request_url and api_arguments (auth headers already included) up to 3 times with 10s delay.
	 *
	 * @param array $data Filter data with event_specifier, supplier_id, api_response, request_url, api_arguments.
	 * @return array Filter data with api_response possibly replaced by retry result.
	 */
	public static function check_for_retry( $data ) {
		if ( ( $data['event_specifier'] ?? '' ) !== Plugin_Filters::EVENT_ALTER_PROCESSED_API_DATA_WITH_REQUEST ) {
			return $data;
		}
		$supplier_id = isset( $data['supplier_id'] ) ? (int) $data['supplier_id'] : 0;
		if ( $supplier_id <= 0 ) {
			return $data;
		}
		$supplier = Supplier_Model::get_by_id( $supplier_id );
		if ( empty( $supplier['app_type'] ) || $supplier['app_type'] !== Constants::SUPPLIER_APP_TYPE_TOPTEX ) {
			return $data;
		}
		if ( ! self::is_timeout_or_connection_error( $data['api_response'] ) ) {
			return $data;
		}
		$request_url   = isset( $data['request_url'] ) ? $data['request_url'] : '';
		$api_arguments = isset( $data['api_arguments'] ) && is_array( $data['api_arguments'] ) ? $data['api_arguments'] : array();
		if ( empty( $request_url ) || empty( $api_arguments ) ) {
			return $data;
		}

		$max_attempts   = 3;
		$delay_seconds  = 10;
		$last_response  = $data['api_response'];

		for ( $attempt = 1; $attempt <= $max_attempts; $attempt++ ) {
			sleep( $delay_seconds );
			$raw_response = API_Service::make_api_call( $request_url, $api_arguments );
			if ( is_wp_error( $raw_response ) ) {
				$last_response = array(
					'message' => $raw_response->get_error_message(),
				);
				continue;
			}
			$body    = wp_remote_retrieve_body( $raw_response );
			$decoded = Utils::safe_json_decode( $body );
			if ( null === $decoded ) {
				$decoded = array();
			}
			$last_response = $decoded;
			if ( ! self::is_timeout_or_connection_error( $decoded ) ) {
				$data['api_response'] = $decoded;
				return $data;
			}
		}

		$data['api_response'] = $last_response;
		return $data;
	}
}
