<?php
/**
 * Shopify 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\Constants\Plugin_Filters;
use MOWCPS\Common\Utils\Utils;
use MOWCPS\Common\Utils\Model_Utils;

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

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

	/**
	 * Load and process configuration, overriding auth type based on use_oauth flag.
	 *
	 * @param array      $details      Supplier details for dynamic parameter replacement.
	 * @param array|null $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 ) {
		$processed_config = parent::load_and_process_configuration( $details, $request_data );

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

		// Override authorization configuration based on the use_oauth flag.
		if ( ! empty( $details['use_oauth'] ) ) {
			$domain        = $details['domain'] ?? '';
			$client_id     = $details['client_id'] ?? '';
			$client_secret = $details['client_secret'] ?? '';

			$processed_config[ Constants::AUTHORIZATION ] = array(
				'auth_type'     => Constants::AUTH_TYPE_OAUTH2,
				'auth_settings' => array(
					'key'              => Constants::SHOPIFY_ACCESS_TOKEN_HEADER_NAME,
					'secret'           => '',
					'request_settings' => array(
						'access_token_url'          => 'https://' . $domain . '/admin/oauth/access_token',
						'client_id'                 => $client_id,
						'client_secret'             => $client_secret,
						'grant_type'                => 'client_credentials',
						'send_client_credentials'   => array( 'body' ),
						'authorization_header_name' => Constants::SHOPIFY_ACCESS_TOKEN_HEADER_NAME,
					),
					'response_settings' => array(
						'token_key'      => 'access_token',
						'expires_in_key' => 'expires_in',
						'expires_in'     => 86399,
						'token_type'     => '',
					),
				),
			);
		} else {
			$processed_config[ Constants::AUTHORIZATION ] = array(
				'auth_type'     => Constants::AUTH_TYPE_API_KEY,
				'auth_settings' => array(
					'key'    => Constants::SHOPIFY_ACCESS_TOKEN_HEADER_NAME,
					'secret' => $details['x_shopify_access_token'] ?? '',
				),
			);
		}

		return $processed_config;
	}

	/**
	 * Perform Shopify-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( 'limit', 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 Shopify-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 Shopify.
		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 )
			);
		}

		$use_oauth = ! empty( $supplier_data['use_oauth'] );

		// OAuth mode: client_id and client_secret are required.
		if ( $use_oauth && empty( $supplier_data['client_id'] ) ) {
			return new WP_Error(
				Constants::ERROR_INVALID_SUPPLIER_CONFIG,
				Constants::SUPPLIER_MISSING_CLIENT_ID,
				array( 'status' => Constants::HTTP_BAD_REQUEST )
			);
		}

		if ( $use_oauth && empty( $supplier_data['client_secret'] ) ) {
			return new WP_Error(
				Constants::ERROR_INVALID_SUPPLIER_CONFIG,
				Constants::SUPPLIER_MISSING_CLIENT_SECRET,
				array( 'status' => Constants::HTTP_BAD_REQUEST )
			);
		}

		// Access token mode: x_shopify_access_token is required.
		if ( ! $use_oauth && empty( $supplier_data['x_shopify_access_token'] ) ) {
			return new WP_Error(
				Constants::ERROR_INVALID_SUPPLIER_CONFIG,
				Constants::SUPPLIER_MISSING_ACCESS_TOKEN,
				array( 'status' => Constants::HTTP_BAD_REQUEST )
			);
		}

		return true;
	}

	/**
	 * Function to store response headers in database for further API calls.
	 *
	 * @param array $arguments API data coming from Shopify.
	 *
	 * @return array
	 */
	public static function store_response_headers( $arguments ) {
		if ( Plugin_Filters::EVENT_ALTER_HEADER_AND_BODY_POST_API_CALL_FILTER === $arguments['event_specifier'] ) {
			$arguments['response_headers'] = json_encode($arguments['response_headers']);
			Model_Utils::update_option( $arguments['connection_id'] . '_page_details', $arguments['response_headers'] );
		}
		return $arguments;
	}

	/**
	 * Treat Shopify single-variant products as WooCommerce simple products.
	 *
	 * Shopify always wraps even simple products in a variants array. When a product has only one variant (e.g. title "Default Title"), there are no real choices for the customer, so the variant array is emptied here. The sync layer then detects an empty variant array and creates/updates a WooCommerce simple product instead of a variable product.
	 *
	 * @param array  $arguments Data alteration arguments.
	 * @return array Modified arguments.
	 */
	public static function check_variance_array_size( $arguments ) {
		if ( Plugin_Filters::EVENT_VARIATION_PRODUCTS_DATA_ARRAY_INITIAL === $arguments['event_specifier'] ) {
			$variance_array = $arguments['variation_array'];

			// Treat as simple product when there is exactly one variant and it is
			// Shopify's placeholder "Default Title" variant (no real customer options).
			if ( count( $variance_array ) < 2 && isset( $variance_array[0]['option1'] ) && Constants::DEFAULT_VARIANT_TITLE === $variance_array[0]['option1'] ) {
				$variance_array = array();
			}

			$arguments['variation_array'] = $variance_array;
		}
		return $arguments;
	}
}
