<?php
/**
 * Amazon Listings Service
 *
 * This class handles product listings operations with Amazon API including
 * creating, updating, and deleting product listings.
 *
 * @package    MOMCS_Sync
 * @subpackage MOMCS_Sync/includes/common/services/amazon
 * @author     miniOrange
 * @license    Expat
 * @link       https://miniorange.com
 */

declare(strict_types=1);

namespace MOMCS_Sync\Includes\Common\Services\Amazon;

use MOMCS_Sync\Includes\Common\Services\Handlers\Api_Handler;
use MOMCS_Sync\Includes\Common\Utils\Logger;
use MOMCS_Sync\Includes\Common\Utils\Marketplace_IDs;

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

/**
 * Class for handling Amazon listings operations.
 */
class Listings_Service {

	/**
	 * API Handler instance.
	 *
	 * @var Api_Handler
	 */
	private $api_handler;

	/**
	 * Token Service instance.
	 *
	 * @var Token_Service
	 */
	private $token_service;

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

	/**
	 * Listings endpoint.
	 *
	 * @var string
	 */
	private $listings_endpoint = '/listings/2021-08-01/items';

	/**
	 * Constructor.
	 */
	public function __construct() {
		$this->api_handler   = new Api_Handler();
		$this->token_service = new Token_Service();
		$this->logger        = new Logger( 'amazon-listings-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 or update a product listing on Amazon.
	 *
	 * @param string       $channel_id      Channel UUID.
	 * @param string       $seller_id       Seller ID.
	 * @param string       $sku             Product SKU.
	 * @param array        $product_data    Product data to submit.
	 * @param string|array $marketplace_ids Amazon marketplace ID(s) - string or array.
	 * @param string       $issue_locale    Issue locale (default: en_US).
	 * @param string       $mode            Submission mode (default: NORMAL, can be VALIDATION_ONLY or NORMAL).
	 * @param array        $log_context     Log context.
	 * @return array|WP_Error Submission result or error.
	 */
	public function submit_listing( $channel_id, $seller_id, $sku, $product_data, $marketplace_ids, $issue_locale = 'en_US', $mode = 'NORMAL', $log_context = array() ) {
		$this->logger->debug(
			'Submitting product listing',
			array(
				'seller_id'       => $seller_id,
				'sku'             => $sku,
				'marketplace_ids' => $marketplace_ids,
				'mode'            => $mode,
			)
		);

		// Get access token.
		$access_token = $this->token_service->get_access_token( $channel_id );
		if ( is_wp_error( $access_token ) ) {
			$this->logger->error(
				'Failed to get access token',
				array(
					'error' => $access_token->get_error_message(),
				)
			);
			return $access_token;
		}

		// Prepare query parameters - handle both string and array.
		$marketplace_param = is_array( $marketplace_ids ) ? implode( ',', $marketplace_ids ) : $marketplace_ids;
		$query_params      = array(
			'marketplaceIds' => $marketplace_param,
			'issueLocale'    => $issue_locale,
		);

		// Use first marketplace ID for base URL.
		$first_marketplace_id = is_array( $marketplace_ids ) ? $marketplace_ids[0] : $marketplace_ids;

		// Build the URL with query parameters.
		$url = $this->get_base_url( $first_marketplace_id ) . $this->listings_endpoint . '/' . $seller_id . '/' . $sku . '?' . http_build_query( $query_params );

		// Make API request.
		$response = $this->api_handler->put(
			$url,
			$product_data,
			array(
				'X-AMZ-Access-Token' => $access_token,
				'Content-Type'       => 'application/json',
			),
			null,
			$log_context
		);

		// Check for errors.
		if ( is_wp_error( $response ) ) {
			$this->logger->error(
				'Error submitting listing',
				array(
					'error' => $response->get_error_message(),
				)
			);
			return $response;
		}

		// Parse response.
		$body        = json_decode( wp_remote_retrieve_body( $response ), true );
		$status_code = wp_remote_retrieve_response_code( $response );

		// Log the response.
		$this->logger->debug(
			'Listings API response',
			array(
				'status_code' => $status_code,
				'body'        => $body,
			)
		);

		// Check for submission issues.
		$has_issues = false;
		if ( isset( $body['issues'] ) && ! empty( $body['issues'] ) ) {
			$has_issues = true;
		}

		return array(
			'success'       => ( 200 === $status_code || 202 === $status_code ) && ! $has_issues,
			'status_code'   => $status_code,
			'submission_id' => $body['submissionId'] ?? '',
			'status'        => $body['status'] ?? '',
			'has_issues'    => $has_issues,
			'issues'        => $body['issues'] ?? array(),
		);
	}

	/**
	 * Get listing item details for a specific SKU.
	 *
	 * Fetches listing item data (e.g., summaries) for a given SKU and marketplace.
	 * Used to retrieve parent listing details when only child variations are available.
	 *
	 * @param string $channel_id     Channel UUID.
	 * @param string $seller_id      Seller ID.
	 * @param string $sku            Product SKU.
	 * @param string $marketplace_id Amazon marketplace ID.
	 * @param array  $included_data  Optional included data array (e.g., ['summaries']).
	 * @return array|WP_Error Listing item data or error.
	 */
	public function get_listing_item( string $channel_id, string $seller_id, string $sku, string $marketplace_id, array $included_data = array( 'summaries' ) ) {
		$this->logger->debug(
			'Getting listing item details',
			array(
				'seller_id'      => $seller_id,
				'sku'            => $sku,
				'marketplace_id' => $marketplace_id,
				'included_data'  => $included_data,
			)
		);

		// Get access token.
		$access_token = $this->token_service->get_access_token( $channel_id );
		if ( is_wp_error( $access_token ) ) {
			$this->logger->error(
				'Failed to get access token',
				array(
					'error' => $access_token->get_error_message(),
				)
			);
			return $access_token;
		}

		// Prepare query parameters.
		$query_params = array(
			'marketplaceIds' => $marketplace_id,
		);

		if ( ! empty( $included_data ) ) {
			$query_params['includedData'] = implode( ',', $included_data );
		}

		// Build the URL with query parameters.
		$url = $this->get_base_url( $marketplace_id ) . $this->listings_endpoint . '/' . $seller_id . '/' . $sku . '?' . http_build_query( $query_params );

		// Make API request.
		$response = $this->api_handler->get(
			$url,
			array(
				'X-AMZ-Access-Token' => $access_token,
			)
		);

		// Check for errors.
		if ( is_wp_error( $response ) ) {
			$this->logger->error(
				'Error getting listing item details',
				array(
					'error' => $response->get_error_message(),
				)
			);
			return $response;
		}

		// Parse response.
		$body        = json_decode( wp_remote_retrieve_body( $response ), true );
		$status_code = wp_remote_retrieve_response_code( $response );

		$this->logger->debug(
			'Listing item API response',
			array(
				'status_code' => $status_code,
				'body'        => $body,
			)
		);

		if ( 200 !== $status_code ) {
			return new \WP_Error(
				'listing_item_fetch_failed',
				'Failed to fetch listing item details',
				array(
					'status_code' => $status_code,
					'body'        => $body,
				)
			);
		}

		return $body;
	}

	/**
	 * Get listing status for a product.
	 *
	 * @param string $channel_id     Channel UUID.
	 * @param string $seller_id      Seller ID.
	 * @param string $sku            Product SKU.
	 * @param string $marketplace_id Amazon marketplace ID.
	 * @return array|WP_Error Listing status or error.
	 */
	public function get_listing_status( $channel_id, $seller_id, $sku, $marketplace_id ) {
		$this->logger->debug(
			'Getting listing status',
			array(
				'seller_id'      => $seller_id,
				'sku'            => $sku,
				'marketplace_id' => $marketplace_id,
			)
		);

		// Get access token.
		$access_token = $this->token_service->get_access_token( $channel_id );
		if ( is_wp_error( $access_token ) ) {
			$this->logger->error(
				'Failed to get access token',
				array(
					'error' => $access_token->get_error_message(),
				)
			);
			return $access_token;
		}

		// Prepare query parameters.
		$query_params = array(
			'marketplaceIds' => $marketplace_id,
		);

		// Add included data if provided.
		if ( ! empty( $included_data ) ) {
			$query_params['includedData'] = implode( ',', $included_data );
		}

		// Build the URL with query parameters.
		$url = $this->get_base_url( $marketplace_id ) . $this->listings_endpoint . '/' . $seller_id . '/' . $sku . '?' . http_build_query( $query_params );

		// Make API request.
		$response = $this->api_handler->get(
			$url,
			array(
				'X-AMZ-Access-Token' => $access_token,
			),
		);

		// Check for errors.
		if ( is_wp_error( $response ) ) {
			$this->logger->error(
				'Error getting listing status',
				array(
					'error' => $response->get_error_message(),
				)
			);
			return $response;
		}

		// Parse response.
		$body        = json_decode( wp_remote_retrieve_body( $response ), true );
		$status_code = wp_remote_retrieve_response_code( $response );

		// Log the response.
		$this->logger->debug(
			'Listing status API response',
			array(
				'status_code' => $status_code,
				'body'        => $body,
			)
		);

		// Handle 404 for listing not found.
		if ( 404 === $status_code ) {
			$this->logger->debug( 'Listing not found' );
			return array(
				'found'  => false,
				'status' => 'NOT_FOUND',
			);
		}

		return $body;
	}

	/**
	 * Delete a product listing from Amazon.
	 *
	 * @param string $channel_id     Channel UUID.
	 * @param string $seller_id      Seller ID.
	 * @param string $sku            Product SKU.
	 * @param string $marketplace_id Amazon marketplace ID.
	 * @param array  $log_context    Log context.
	 * @return array|WP_Error Deletion result or error.
	 */
	public function delete_listing( $channel_id, $seller_id, $sku, $marketplace_id, $log_context = array() ) {
		$this->logger->debug(
			'Deleting product listing',
			array(
				'seller_id'      => $seller_id,
				'sku'            => $sku,
				'marketplace_id' => $marketplace_id,
			)
		);

		// Get access token.
		$access_token = $this->token_service->get_access_token( $channel_id );
		if ( is_wp_error( $access_token ) ) {
			$this->logger->error(
				'Failed to get access token',
				array(
					'error' => $access_token->get_error_message(),
				)
			);
			return $access_token;
		}

		// Prepare query parameters.
		$query_params = array(
			'marketplaceIds' => $marketplace_id,
		);

		// Build the URL with query parameters.
		$url = $this->get_base_url( $marketplace_id ) . $this->listings_endpoint . '/' . $seller_id . '/' . $sku . '?' . http_build_query( $query_params );

		// Make API request.
		$response = $this->api_handler->delete(
			$url,
			array(
				'X-AMZ-Access-Token' => $access_token,
			),
			null,
			$log_context
		);

		// Check for errors.
		if ( is_wp_error( $response ) ) {
			$this->logger->error(
				'Error deleting listing',
				array(
					'error' => $response->get_error_message(),
				)
			);
			return $response;
		}

		// Parse response.
		$body        = json_decode( wp_remote_retrieve_body( $response ), true );
		$status_code = wp_remote_retrieve_response_code( $response );

		// Log the response.
		$this->logger->debug(
			'Delete listing API response',
			array(
				'status_code' => $status_code,
				'body'        => $body,
			)
		);

		// Handle HTTP errors (4xx/5xx).
		if ( $status_code >= 400 ) {
			$message = $body['errors'][0]['message'] ?? 'Unknown SP-API error';
			$this->logger->error(
				'Delete listing API HTTP error',
				array(
					'status_code' => $status_code,
					'sku'         => $sku,
					'message'     => $message,
				)
			);

			return new \WP_Error(
				'momcs_delete_listing_failed',
				sprintf( 'Amazon responded with %d – %s', $status_code, $message ),
				array( 'response' => $body )
			);
		}

		// Evaluate issues array for domain-level errors returned in a 200/202 response.
		$issues  = $body['issues'] ?? array();
		$success = empty( $issues ) && in_array( $body['status'] ?? '', array( 'ACCEPTED', 'DONE', 'SUCCESS' ), true );

		return array(
			'success'       => $success,
			'status_code'   => $status_code,
			'status'        => $body['status'] ?? 'UNKNOWN',
			'submission_id' => $body['submissionId'] ?? '',
			'issues'        => $issues,
		);
	}

	/**
	 * Format product data for Amazon API based on template mapping.
	 *
	 * @param array  $wc_product    WooCommerce product data.
	 * @param array  $template_config Template configuration from mapping.
	 * @param string $product_type  Amazon product type.
	 * @param array  $schema        Schema.
	 * @return array Formatted product data for Amazon API.
	 */
	public function format_product_data( $wc_product, $template_config, $product_type, $schema = array() ) {
		$product_data = array(
			'productType'  => $product_type,
			'requirements' => 'LISTING',
			'attributes'   => array(),
		);

		// Build lookup lists from schema (if provided).
		$schema_fields   = array();
		$required_fields = array();
		if ( ! empty( $schema ) && is_array( $schema ) ) {
			try {
				$pts_service     = new Product_Type_Service();
				$required_fields = $pts_service->extract_required_fields( $schema );
				// Only populate attributes that are marked required in the schema.
				$schema_fields = $required_fields;
			} catch ( \Exception $e ) {
				// If extraction fails, proceed without schema validation.
				$this->logger->warning( 'Failed to parse product type schema', array( 'error' => $e->getMessage() ) );
			}
		}

		// Combine mappings (general + field specific).
		$combined_mappings = array();
		if ( isset( $template_config['general_mappings'] ) && is_array( $template_config['general_mappings'] ) ) {
			$combined_mappings = $template_config['general_mappings'];
		}
		if ( isset( $template_config['field_mappings'] ) ) {
			$field_mappings_raw = $template_config['field_mappings'];

			if ( is_string( $field_mappings_raw ) ) {
				$decoded = json_decode( $field_mappings_raw, true );
				if ( JSON_ERROR_NONE === json_last_error() && is_array( $decoded ) ) {
					$field_mappings_raw = $decoded;
				} else {
					// If decoding fails, log and skip.
					$this->logger->warning( 'field_mappings JSON could not be decoded', array( 'error' => json_last_error_msg() ) );
					$field_mappings_raw = array();
				}
			}

			if ( is_array( $field_mappings_raw ) ) {
				$combined_mappings = array_merge( $combined_mappings, $field_mappings_raw );
			}
		}

		$mapped_fields = array();
		$schema_fields = array_keys( $schema_fields );

		foreach ( $combined_mappings as $amazon_field => $wc_field ) {
			// If schema provided, ensure field exists.
			if ( ! empty( $schema_fields ) && ! in_array( $amazon_field, $schema_fields, true ) ) {
				$this->logger->debug( 'Skipping unmapped field not present in schema', array( 'field' => $amazon_field ) );
				continue;
			}
			if ( is_array( $wc_field ) ) {
				$value = $wc_field['value'];
			} else {
				$value = $this->get_product_field_value( $wc_product, $wc_field );
			}
			if ( '' !== $value && null !== $value ) {
				$product_data['attributes'][ $amazon_field ] = array(
					array(
						'value'          => $value,
						'marketplace_id' => 'A21TJRUUN4KGV', // Default marketplace.
					),
				);
				$mapped_fields[]                             = $amazon_field;
			}
		}

		return $product_data;
	}

	/**
	 * Get product field value from WooCommerce product.
	 *
	 * @param array  $wc_product WooCommerce product data.
	 * @param string $field_key  Field key.
	 * @return mixed Field value.
	 */
	private function get_product_field_value( $wc_product, $field_key ) {
		// Handle special field keys.
		switch ( $field_key ) {
			case 'post_title':
				return $wc_product['post_title'] ?? '';

			case 'post_content':
				return $wc_product['post_content'] ?? '';

			case 'post_excerpt':
				return $wc_product['post_excerpt'] ?? '';

			default:
				// Check if it's a meta field.
				if ( 0 === strpos( $field_key, '_' ) ) {
					return $wc_product['meta_data'][ $field_key ] ?? '';
				}

				// Check if it's a product attribute.
				if ( isset( $wc_product['attributes'] ) && isset( $wc_product['attributes'][ $field_key ] ) ) {
					return $wc_product['attributes'][ $field_key ];
				}

				// Default to direct property.
				return $wc_product[ $field_key ] ?? '';
		}
	}
}
