<?php
/**
 * Feeds API Service
 *
 * @package    MOMCS_Sync
 * @subpackage MOMCS_Sync/includes/common/services/amazon
 * @author     miniOrange
 */

declare(strict_types=1);

namespace MOMCS_Sync\Includes\Common\Services\Amazon;

use MOMCS_Sync\Includes\Common\Exceptions\Sync_Exception;
use MOMCS_Sync\Includes\Common\Exceptions\Amazon_Api_Exception;
use MOMCS_Sync\Includes\Common\Services\Amazon\Token_Service;
use MOMCS_Sync\Includes\Common\Utils\Logger;
use MOMCS_Sync\Includes\Common\Constants\Plugin_Constants;
use MOMCS_Sync\Includes\Common\Services\Handlers\Api_Handler;
use MOMCS_Sync\Includes\Common\Utils\Marketplace_IDs;

/**
 * Feeds API Service Class
 *
 * Handles bulk product synchronization using Amazon's Feeds API v2021-06-30
 */
class Feeds_API_Service {
	/**
	 * Token service instance
	 *
	 * @var Token_Service
	 */
	private $token_service;

	/**
	 * Logger instance
	 *
	 * @var Logger
	 */
	private $logger;

	/**
	 * API endpoint
	 *
	 * @var string
	 */
	private $endpoint;

	/**
	 * API handler instance
	 *
	 * @var Api_Handler
	 */
	private $api_handler;

	/**
	 * Constructor.
	 */
	public function __construct() {
		$this->token_service = new Token_Service();
		$this->api_handler   = new Api_Handler();
		$this->logger        = new Logger( 'feeds-api-service' );
	}

	/**
	 * Get dynamic base URL for Amazon API based on marketplace.
	 *
	 * @param string $marketplace_id Amazon marketplace ID.
	 * @return string Base URL.
	 */
	private function get_base_url( string $marketplace_id ): string {
		// Get dynamic URL based on marketplace.
		return Marketplace_IDs::get_api_base_url( $marketplace_id ) ?? 'https://sellingpartnerapi-eu.amazon.com';
	}

	/**
	 * Create feed document.
	 *
	 * @param array  $marketplace_ids Array of marketplace IDs.
	 * @param array  $log_context Log context.
	 * @param string $channel_id Channel ID.
	 *
	 * @return array|WP_Error Feed document details or error.
	 * @throws \MOMCS_Sync\Includes\Common\Exceptions\Sync_Exception If channel ID is missing.
	 * @throws \MOMCS_Sync\Includes\Common\Exceptions\Amazon_Api_Exception If API request fails.
	 */
	public function create_feed_document( $marketplace_ids = array(), $log_context = array(), $channel_id = '' ) {
		// Get channel ID from transient.
		$channel_id = $channel_id ? $channel_id : get_transient( Plugin_Constants::TRANSIENT_CURRENT_CHANNEL );
		if ( empty( $channel_id ) ) {
			throw new Sync_Exception( 'Channel ID is missing. Please select a channel first.' );
		}

		$token = $this->token_service->get_access_token( $channel_id );
		if ( is_wp_error( $token ) ) {
			$this->logger->error( 'Failed to obtain Amazon access token: ' . $token->get_error_message() );
			throw new Amazon_Api_Exception( esc_html( 'Failed to obtain Amazon access token: ' . $token->get_error_message() ) );
		}

		// Use first marketplace ID to determine region.
		$first_marketplace_id = ! empty( $marketplace_ids ) ? $marketplace_ids[0] : 'A1F83G8C2ARO7P'; // Default to UK.
		$url                  = $this->get_base_url( $first_marketplace_id ) . '/feeds/2021-06-30/documents';
		$headers              = array(
			'x-amz-access-token' => $token,
			'Content-Type'       => 'application/json',
		);
		$content_type         = 'application/json; charset=UTF-8';
		$body                 = json_encode(
			array(
				'contentType' => $content_type,
			)
		);

		// Add marketplace IDs to the request if provided.
		if ( ! empty( $marketplace_ids ) ) {
			// For create_feed_document, marketplace IDs should be in the query string.
			$marketplace_param = implode( ',', $marketplace_ids );
			$url               = add_query_arg( 'marketplaceIds', $marketplace_param, $url );
		}

		$response = $this->api_handler->post(
			$url,
			$body,
			$headers,
			null,
			$log_context
		);

		if ( is_wp_error( $response ) ) {
			$this->logger->error( 'Failed to create feed document: ' . $response->get_error_message() );
			throw new Amazon_Api_Exception( esc_html( 'Failed to create feed document: ' . $response->get_error_message() ) );
		}

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

		if ( 201 !== $response_code ) {
			$error_message = isset( $response_body['errors'][0]['message'] ) ? $response_body['errors'][0]['message'] : 'Unknown error';
			$this->logger->error( 'Failed to create feed document. Status: ' . $response_code . ', Error: ' . $error_message );
			throw new Amazon_Api_Exception( esc_html( 'Failed to create feed document: ' . $error_message ) );
		}

		$this->logger->info( 'Feed document created successfully: ' . $response_body['feedDocumentId'] );
		return $response_body;
	}

	/**
	 * Upload feed payload.
	 *
	 * @param string $upload_url Upload URL from create_feed_document response.
	 * @param array  $payload    Feed payload data.
	 * @param array  $log_context Log context.
	 *
	 * @return bool|WP_Error True on success or error.
	 * @throws \MOMCS_Sync\Includes\Common\Exceptions\Amazon_Api_Exception If API request fails.
	 */
	public function upload_feed_payload( $upload_url, $payload, $log_context = array() ) {
		$json_payload = json_encode( $payload );

		$response = $this->api_handler->put(
			$upload_url,
			$json_payload,
			array(
				'Content-Type' => 'application/json; charset=UTF-8',
			),
			null,
			$log_context
		);

		if ( is_wp_error( $response ) ) {
			$this->logger->error( 'Failed to upload feed payload: ' . $response->get_error_message() );
			throw new Amazon_Api_Exception( esc_html( 'Failed to upload feed payload: ' . $response->get_error_message() ) );
		}

		$response_code = wp_remote_retrieve_response_code( $response );

		if ( 200 !== $response_code ) {
			$error_message = 'Failed to upload feed payload. Status: ' . $response_code;
			$this->logger->error( $error_message );
			throw new Amazon_Api_Exception( esc_html( $error_message ) );
		}

		$this->logger->info( 'Feed payload uploaded successfully' );
		return true;
	}

	/**
	 * Create feed.
	 *
	 * @param string $feed_document_id Feed document ID from create_feed_document.
	 * @param array  $marketplace_ids  Array of marketplace IDs.
	 * @param array  $log_context      Log context.
	 * @param string $channel_id       Channel ID.
	 *
	 * @return array|WP_Error Feed details or error.
	 * @throws \MOMCS_Sync\Includes\Common\Exceptions\Sync_Exception If channel ID is missing.
	 * @throws \MOMCS_Sync\Includes\Common\Exceptions\Amazon_Api_Exception If API request fails.
	 */
	public function create_feed( $feed_document_id, $marketplace_ids, $log_context = array(), $channel_id = '' ) {
		// Get channel ID from transient.
		$channel_id = $channel_id ? $channel_id : get_transient( Plugin_Constants::TRANSIENT_CURRENT_CHANNEL );
		if ( empty( $channel_id ) ) {
			throw new Sync_Exception( 'Channel ID is missing. Please select a channel first.' );
		}

		$token = $this->token_service->get_access_token( $channel_id );
		if ( is_wp_error( $token ) ) {
			$this->logger->error( 'Failed to obtain Amazon access token: ' . $token->get_error_message() );
			throw new Amazon_Api_Exception( esc_html( 'Failed to obtain Amazon access token: ' . $token->get_error_message() ) );
		}

		// Use first marketplace ID to determine region.
		$first_marketplace_id = ! empty( $marketplace_ids ) ? $marketplace_ids[0] : 'A1F83G8C2ARO7P'; // Default to UK.
		$url                  = $this->get_base_url( $first_marketplace_id ) . '/feeds/2021-06-30/feeds';
		$headers              = array(
			'x-amz-access-token' => $token,
			'Content-Type'       => 'application/json',
		);

			// For production mode, use the provided parameters.
			$feed_data = array(
				'feedType'            => 'JSON_LISTINGS_FEED',
				'marketplaceIds'      => $marketplace_ids,
				'inputFeedDocumentId' => $feed_document_id,
			);

			$body = json_encode( $feed_data );

			$response = $this->api_handler->post(
				$url,
				$body,
				$headers,
				null,
				$log_context
			);

		if ( is_wp_error( $response ) ) {
			$this->logger->error( 'Failed to create feed: ' . $response->get_error_message() );
			throw new Amazon_Api_Exception( esc_html( 'Failed to create feed: ' . $response->get_error_message() ) );
		}

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

		if ( 202 !== $response_code ) {
			$error_message = isset( $response_body['errors'][0]['message'] ) ? $response_body['errors'][0]['message'] : 'Unknown error';
			$this->logger->error( 'Failed to create feed. Status: ' . $response_code . ', Error: ' . $error_message );
			throw new Amazon_Api_Exception( esc_html( 'Failed to create feed: ' . $error_message ) );
		}

		$this->logger->info( 'Feed created successfully: ' . $response_body['feedId'] );
		return $response_body;
	}

	/**
	 * Get feed status.
	 *
	 * @param string $feed_id Feed ID.
	 * @param array  $log_context Log context.
	 * @param string $channel_id Channel ID.
	 *
	 * @return array|WP_Error Feed status or error.
	 * @throws \MOMCS_Sync\Includes\Common\Exceptions\Sync_Exception If channel ID is missing.
	 * @throws \MOMCS_Sync\Includes\Common\Exceptions\Amazon_Api_Exception If API request fails.
	 */
	public function get_feed_status( $feed_id, $log_context = array(), $channel_id = null ) {
		// Get channel ID from transient.
		if ( empty( $channel_id ) ) {
			throw new Sync_Exception( 'Channel ID is missing. Please select a channel first.' );
		}

		$token = $this->token_service->get_access_token( $channel_id );
		if ( is_wp_error( $token ) ) {
			$this->logger->error( 'Failed to obtain Amazon access token: ' . $token->get_error_message() );
			throw new Amazon_Api_Exception( esc_html( 'Failed to obtain Amazon access token: ' . $token->get_error_message() ) );
		}

		// Default to UK marketplace for determining region when marketplace ID not available.
		$url = $this->get_base_url( 'A1F83G8C2ARO7P' ) . '/feeds/2021-06-30/feeds/' . $feed_id;

		$headers = array(
			'x-amz-access-token' => $token,
			'Content-Type'       => 'application/json',
		);

		$response = $this->api_handler->get(
			$url,
			$headers,
			null,
			$log_context
		);

		if ( is_wp_error( $response ) ) {
			$this->logger->error( 'Failed to get feed status: ' . $response->get_error_message() );
			throw new Amazon_Api_Exception( esc_html( $response->get_error_message() ) );
		}

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

		if ( 200 !== $response_code ) {
			$error_message = isset( $response_body['errors'][0]['message'] ) ? $response_body['errors'][0]['message'] : 'Unknown error';
			$this->logger->error( 'Failed to get feed status. Status: ' . $response_code . ', Error: ' . $error_message );
			throw new Amazon_Api_Exception( esc_html( 'Failed to get feed status: ' . $error_message ) );
		}

		$this->logger->info( 'Feed status retrieved successfully: ' . $response_body['processingStatus'] );
		return $response_body;
	}

	/**
	 * Get feed document.
	 *
	 * @param string $document_id Feed document ID.
	 * @param array  $log_context Log context.
	 * @param string $channel_id Channel ID.
	 *
	 * @return array|WP_Error Feed document details or error.
	 * @throws \MOMCS_Sync\Includes\Common\Exceptions\Sync_Exception If channel ID is missing.
	 * @throws \MOMCS_Sync\Includes\Common\Exceptions\Amazon_Api_Exception If API request fails.
	 */
	public function get_feed_document( $document_id, $log_context = array(), $channel_id = null ) {
		// Get channel ID from transient.
		if ( empty( $channel_id ) ) {
			throw new Sync_Exception( 'Channel ID is missing. Please select a channel first.' );
		}

		$token = $this->token_service->get_access_token( $channel_id );
		if ( is_wp_error( $token ) ) {
			$this->logger->error( 'Failed to obtain Amazon access token: ' . $token->get_error_message() );
			throw new Amazon_Api_Exception( esc_html( 'Failed to obtain Amazon access token: ' . $token->get_error_message() ) );
		}

		// Default to UK marketplace for determining region when marketplace ID not available.
		$url     = $this->get_base_url( 'A1F83G8C2ARO7P' ) . '/feeds/2021-06-30/documents/' . $document_id;
		$headers = array(
			'x-amz-access-token' => $token,
			'Content-Type'       => 'application/json',
		);

		$response = $this->api_handler->get(
			$url,
			$headers,
			null,
			$log_context
		);

		if ( is_wp_error( $response ) ) {
			$this->logger->error( 'Failed to get feed document: ' . $response->get_error_message() );
			throw new Amazon_Api_Exception( esc_html( $response->get_error_message() ) );
		}

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

		if ( 200 !== $response_code ) {
			$error_message = isset( $response_body['errors'][0]['message'] ) ? $response_body['errors'][0]['message'] : 'Unknown error';
			$this->logger->error( 'Failed to get feed document. Status: ' . $response_code . ', Error: ' . $error_message );
			throw new Amazon_Api_Exception( esc_html( 'Failed to get feed document: ' . $error_message ) );
		}

		$this->logger->info( 'Feed document retrieved successfully' );
		return $response_body;
	}

	/**
	 * Download and process feed result.
	 *
	 * @param string $url Feed result URL.
	 * @param array  $encryption_details Encryption details if provided.
	 * @param array  $log_context Log context.
	 *
	 * @return array|WP_Error Processed feed result or error.
	 * @throws \MOMCS_Sync\Includes\Common\Exceptions\Amazon_Api_Exception If API request fails.
	 */
	public function download_feed_result( $url, $encryption_details = null, $log_context = array() ) {

		$response = $this->api_handler->get(
			$url,
			array(),
			null,
			$log_context
		);

		if ( is_wp_error( $response ) ) {
			$this->logger->error( 'Failed to download feed result: ' . $response->get_error_message() );
			throw new Amazon_Api_Exception( esc_html( $response->get_error_message() ) );
		}

		$response_code = wp_remote_retrieve_response_code( $response );
		$response_body = wp_remote_retrieve_body( $response );

		if ( 200 !== $response_code ) {
			$error_message = 'Failed to download feed result. Status: ' . $response_code;
			$this->logger->error( $error_message );
			throw new Amazon_Api_Exception( esc_html( $error_message ) );
		}

		// Decrypt if encryption details are provided.
		if ( ! empty( $encryption_details ) ) {
			$response_body = $this->decrypt_feed_result( $response_body, $encryption_details );
			if ( is_wp_error( $response_body ) ) {
				throw new Amazon_Api_Exception( esc_html( $response_body->get_error_message() ) );
			}
		}

			// In production mode, response is JSON.
			$result = json_decode( $response_body, true );
		if ( JSON_ERROR_NONE !== json_last_error() ) {
			$error_message = 'Failed to parse feed result: ' . json_last_error_msg();
			$this->logger->error( $error_message );
			throw new Amazon_Api_Exception( esc_html( $error_message ) );
		}

			$this->logger->info( 'Feed result downloaded and processed successfully' );
			return $result;
	}

	/**
	 * Decrypt feed result
	 *
	 * @param string $content Encrypted content.
	 * @param array  $encryption_details Encryption details.
	 *
	 * @return string|WP_Error Decrypted content or error.
	 */
	private function decrypt_feed_result( $content, $encryption_details ) {
		if ( empty( $encryption_details ) ) {
			return $content;
		}

		$key = base64_decode( $encryption_details['key'] );
		$iv  = base64_decode( $encryption_details['initializationVector'] );

		// Decrypt with AES-256-CBC.
		$decrypted = openssl_decrypt( $content, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv );

		if ( false === $decrypted ) {
			$error_message = 'Decryption failed: ' . openssl_error_string();
			$this->logger->error( $error_message );
			return new \WP_Error( 'decryption_failed', $error_message );
		}

		// If compressed (check response 'compressionAlgorithm'), e.g., GZIP.
		if ( isset( $encryption_details['compressionAlgorithm'] ) && 'GZIP' === $encryption_details['compressionAlgorithm'] ) {
			$decrypted = gzdecode( $decrypted );
			if ( false === $decrypted ) {
				$error_message = 'Failed to decompress feed result';
				$this->logger->error( $error_message );
				return new \WP_Error( 'decompression_failed', $error_message );
			}
		}

		return $decrypted;
	}
}
