<?php
// phpcs:disable WordPress.WP.I18n.TextDomainMismatch
// Text domain 'activetrail-for-woocommerce' is correct per WordPress review requirements.
/**
 * ActiveTrail API Client - Enhanced Enterprise Edition
 *
 * @package ActiveTrail_WooCommerce
 * @since 1.0.0
 * @version 2.0.0
 */

// Prevent direct access.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * ActiveTrail API Client Class
 *
 * Enhanced enterprise-grade API client with:
 * - Proper Bearer token authentication
 * - Smart retry logic and error handling
 * - Data transformation helpers
 * - Rate limiting awareness
 * - Comprehensive logging
 * - Settings integration
 */
class ActiveTrail_API_Client {


	/**
	 * API Base URL
	 */
	const API_BASE_URL = 'https://webapi.mymarketing.co.il/api';

	/**
	 * API Version
	 */
	const API_VERSION = '1.0';

	/**
	 * Request timeout in seconds
	 */
	const REQUEST_TIMEOUT = 30;

	/**
	 * Retry delay in seconds for rate limiting
	 */
	const RETRY_DELAY = 2;

	/**
	 * API Key
	 *
	 * @var string
	 */
	private $api_key;

	/**
	 * Settings instance
	 *
	 * @var ActiveTrail_Settings
	 */
	private $settings;

	/**
	 * Last API response for debugging
	 *
	 * @var array
	 */
	private $last_response = array();

	/**
	 * Constructor
	 *
	 * @param string $api_key ActiveTrail API key.
	 */
	public function __construct( $api_key = '' ) {
		$this->api_key = $api_key;

		// Load settings if available.
		if ( class_exists( 'ActiveTrail_Settings' ) ) {
			$this->settings = new ActiveTrail_Settings();
		}
	}

	/**
	 * Test API connection
	 *
	 * @return array
	 */
	public function test_connection() {
		if ( empty( $this->api_key ) ) {
			return array(
				'success' => false,
				'message' => esc_html__( 'API key is required', 'activetrail-for-woocommerce' ),
			);
		}

		// Check for test mode.
		if ( 'test_key_123' === $this->api_key ) {
			return array(
				'success' => true,
				'message' => esc_html__( 'Test mode: API connection simulated successfully', 'activetrail-for-woocommerce' ),
			);
		}

		$response = $this->make_request( 'GET', '/groups' );

		if ( is_wp_error( $response ) ) {
			$error_message = $response->get_error_message();
			$error_code    = $response->get_error_code();

			$this->log_request_error( 'Connection test failed', $error_message );

			// Provide more detailed error messages for common connection issues.
			if ( 'http_request_failed' === $error_code ) {
				if ( strpos( $error_message, 'cURL error 28' ) !== false ) {
					return array(
						'success' => false,
						'message' => esc_html__( 'Connection failed: Connection timeout. Please check your internet connection or try again later.', 'activetrail-for-woocommerce' ),
					);
				} elseif ( strpos( $error_message, 'cURL error 6' ) !== false || strpos( $error_message, 'cURL error 7' ) !== false ) {
					return array(
						'success' => false,
						'message' => esc_html__( 'Connection failed: Could not resolve host. Please check your internet connection.', 'activetrail-for-woocommerce' ),
					);
				}
			}

			// If no specific error was identified, return a generic message if the error message is empty.
			if ( empty( $error_message ) ) {
				return array(
					'success' => false,
					'message' => esc_html__( 'Connection failed: Unable to connect to ActiveTrail API. Please check your internet connection and try again.', 'activetrail-for-woocommerce' ),
				);
			}

			return array(
				'success' => false,
				/* translators: %s: Error message */
				'message' => sprintf( esc_html__( 'Connection failed: %s', 'activetrail-for-woocommerce' ), $error_message ),
			);
		}

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

		// Log detailed response for debugging.
		$this->log_request_debug(
			'API Test Response',
			array(
				'response_code' => $response_code,
				'body'          => $body,
				'parsed_data'   => $data,
				'headers'       => wp_remote_retrieve_headers( $response ),
			)
		);

		if ( $response_code === 200 ) {
			return array(
				'success' => true,
				'message' => esc_html__( 'API connection successful', 'activetrail-for-woocommerce' ),
			);
		} else {
			// ActiveTrail uses "Message" (capital M) for error responses.
			$error_message = isset( $data['Message'] ) ? $data['Message'] :
				( isset( $data['message'] ) ? $data['message'] :
					/* translators: %d: HTTP response code */
					sprintf( esc_html__( 'HTTP %d: API connection failed', 'activetrail-for-woocommerce' ), $response_code ) );

			return array(
				'success'    => false,
				'message'    => $error_message,
				'debug_info' => array(
					'response_code' => $response_code,
					'response_body' => $body,
					'request_url'   => self::API_BASE_URL . '/groups',
				),
			);
		}
	}

	/**
	 * Get groups from ActiveTrail
	 *
	 * @param array $params Query parameters (SearchTerm, Page, Limit).
	 * @return array
	 */
	public function get_groups( $params = array() ) {
		if ( empty( $this->api_key ) ) {
			return array(
				'success' => false,
				'message' => esc_html__( 'API key is required', 'activetrail-for-woocommerce' ),
				'data'    => array(),
			);
		}

		// Check for test mode.
		if ( 'test_key_123' === $this->api_key ) {
			return array(
				'success' => true,
				'message' => esc_html__( 'Test mode: Groups retrieved successfully', 'activetrail-for-woocommerce' ),
				'data'    => array(
					array(
						'Id'   => 1,
						'Name' => 'Test Group 1',
					),
					array(
						'Id'   => 2,
						'Name' => 'Test Group 2',
					),
					array(
						'Id'   => 3,
						'Name' => 'Test Group 3',
					),
				),
			);
		}

		// Build query parameters.
		$query_params = array();
		if ( ! empty( $params['SearchTerm'] ) ) {
			$query_params['SearchTerm'] = $params['SearchTerm'];
		}
		if ( ! empty( $params['Page'] ) ) {
			$query_params['Page'] = $params['Page'];
		}
		if ( ! empty( $params['Limit'] ) ) {
			$query_params['Limit'] = $params['Limit'];
		}

		$endpoint = '/groups';
		if ( ! empty( $query_params ) ) {
			$endpoint .= '?' . http_build_query( $query_params );
		}

		$response = $this->make_request( 'GET', $endpoint );

		if ( is_wp_error( $response ) ) {
			return array(
				'success' => false,
				'message' => $response->get_error_message(),
				'data'    => array(),
			);
		}

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

		// Log detailed response for debugging.
		$this->log_request_debug(
			'Groups API Response',
			array(
				'response_code' => $response_code,
				'body'          => $body,
				'parsed_data'   => $data,
			)
		);

		if ( $response_code === 200 ) {
			// ActiveTrail API returns groups directly in an array, not wrapped in 'data'.
			$groups = is_array( $data ) ? $data : array();

			return array(
				'success' => true,
				'message' => esc_html__( 'Groups retrieved successfully', 'activetrail-for-woocommerce' ),
				'data'    => $groups,
			);
		} else {
			$error_message = isset( $data['Message'] ) ? $data['Message'] :
				( isset( $data['message'] ) ? $data['message'] :
					esc_html__( 'Failed to retrieve groups', 'activetrail-for-woocommerce' ) );

			return array(
				'success' => false,
				'message' => $error_message,
				'data'    => array(),
			);
		}
	}

	/**
	 * Import customer to ActiveTrail (with smart upsert logic)
	 *
	 * @param array|WC_Customer $customer_data Customer data or WC_Customer object.
	 * @return array
	 */
	public function import_customer( $customer_data ) {
		if ( empty( $this->api_key ) ) {
			return array(
				'success' => false,
				'message' => esc_html__( 'API key is required', 'activetrail-for-woocommerce' ),
			);
		}

		// Transform WooCommerce customer to ActiveTrail format if needed.
		if ( is_object( $customer_data ) && ( method_exists( $customer_data, 'get_email' ) || method_exists( $customer_data, 'get_billing_email' ) ) ) {
			$customer_data = $this->transform_wc_customer_to_activetrail( $customer_data );
		}

		// Validate required fields.
		$validation = $this->validate_customer_data( $customer_data );
		if ( ! $validation['valid'] ) {
			return array(
				'success' => false,
				'message' => $validation['message'],
			);
		}

		// Rename subscriberStatus to subscriptionStatus if present.
		if ( isset( $customer_data['subscriberStatus'] ) ) {
			$customer_data['subscriptionStatus'] = $customer_data['subscriberStatus'];
			unset( $customer_data['subscriberStatus'] );
		}

		// Try smart upsert approach to avoid import limits.
		return $this->upsert_customer( $customer_data );
	}

	/**
	 * Import customer from user data (for registration)
	 *
	 * @param array $user_data User data array.
	 * @return array
	 */
	public function import_customer_from_user( $user_data ) {
		if ( empty( $this->api_key ) ) {
			return array(
				'success' => false,
				'message' => esc_html__( 'API key is required', 'activetrail-for-woocommerce' ),
			);
		}

		// Validate required fields.
		if ( empty( $user_data['email'] ) ) {
			return array(
				'success' => false,
				'message' => esc_html__( 'Email is required', 'activetrail-for-woocommerce' ),
			);
		}

		// Generate proper external ID using the helper function.
		$external_id = '';
		if ( ! empty( $user_data['externalId'] ) ) {
			// Use provided external ID if available.
			$external_id = $user_data['externalId'];
		} elseif ( function_exists( 'activetrail_external_id_for_contact' ) ) {
			// Use the helper function if available.
			$user_id     = isset( $user_data['user_id'] ) ? $user_data['user_id'] : 0;
			$external_id = activetrail_external_id_for_contact( $user_id, $user_data['email'] );
		} else {
			// Fallback to a safe format that doesn't use email directly.
			$user_id = isset( $user_data['user_id'] ) ? $user_data['user_id'] : 0;
			if ( $user_id ) {
				$external_id = 'WC-CONTACT-' . $user_id;
			} else {
				$external_id = 'WC-CONTACT-GUEST-' . md5( $user_data['email'] );
			}
		}

		// Transform user data to customer format.
		$customer_data = array(
			'email'        => $user_data['email'],
			'sms'          => isset( $user_data['phone'] ) ? $user_data['phone'] : '',
			'first_name'   => isset( $user_data['first_name'] ) ? $user_data['first_name'] : '',
			'last_name'    => isset( $user_data['last_name'] ) ? $user_data['last_name'] : '',
			'externalId'   => $external_id,
			'externalName' => 'WooCommerce',
		);
		
		// Add address fields if available (always include, even for unsubscribed contacts).
		if ( ! empty( $user_data['street'] ) ) {
			$customer_data['street'] = $user_data['street'];
		} elseif ( ! empty( $user_data['address'] ) ) {
			$customer_data['street'] = $user_data['address'];
		}
		
		if ( ! empty( $user_data['city'] ) ) {
			$customer_data['city'] = $user_data['city'];
		}
		
		if ( ! empty( $user_data['zip_code'] ) ) {
			$customer_data['zip_code'] = $user_data['zip_code'];
		} elseif ( ! empty( $user_data['postcode'] ) ) {
			$customer_data['zip_code'] = $user_data['postcode'];
		}

		// Add subscriptionStatus if provided in user_data.
		if ( isset( $user_data['subscriptionStatus'] ) ) {
			$customer_data['subscriptionStatus'] = $user_data['subscriptionStatus'];
		}

		// Add group ID if provided - check user_data first, then fallback to settings.
		$group_id = null;
		if ( ! empty( $user_data['group_id'] ) && is_numeric( $user_data['group_id'] ) && intval( $user_data['group_id'] ) > 0 ) {
			$group_id = intval( $user_data['group_id'] );
		} else {
			// Fallback to settings if not in user_data.
			if ( class_exists( 'ActiveTrail_Settings' ) ) {
				$settings = ActiveTrail_Settings::get_all();
				if ( ! empty( $settings['group_id'] ) && is_numeric( $settings['group_id'] ) && intval( $settings['group_id'] ) > 0 ) {
					$group_id = intval( $settings['group_id'] );
					$this->log_api_debug(
						'Import Customer From User - Using group_id from settings',
						array(
							'group_id' => $group_id,
							'email'    => $user_data['email'],
						)
					);
				}
			}
		}

		if ( ! empty( $group_id ) ) {
			$customer_data['groupId'] = $group_id;
			$this->log_api_debug(
				'Import Customer From User - Group ID set',
				array(
					'groupId' => $group_id,
					'email'   => $user_data['email'],
				)
			);
		} else {
			$this->log_api_debug(
				'Import Customer From User - No group_id available',
				array(
					'user_data_group_id' => isset( $user_data['group_id'] ) ? $user_data['group_id'] : 'not_set',
					'email'              => $user_data['email'],
				)
			);
		}

		// Validate customer data.
		$validation = $this->validate_customer_data( $customer_data );
		if ( ! $validation['valid'] ) {
			return array(
				'success' => false,
				'message' => $validation['message'],
			);
		}

		// Try smart upsert approach.
		return $this->upsert_customer( $customer_data );
	}

	/**
	 * Smart customer upsert - check if exists, then create or update
	 *
	 * @param array $customer_data Customer data.
	 * @return array
	 */
	private function upsert_customer( $customer_data ) {
		$email = $customer_data['email'];

		// Step 1: Check if customer exists by email using the correct endpoint.
		$existing_customer = $this->get_customer_by_email( $email );

		if ( is_wp_error( $existing_customer ) ) {
			return array(
				'success' => false,
				'message' => $existing_customer->get_error_message(),
			);
		}

		if ( $existing_customer['exists'] ) {
			// Customer exists - update them using external ID. data may be array of contacts; use first for update.
			$data   = $existing_customer['data'];
			$contact = ( is_array( $data ) && isset( $data[0] ) ) ? $data[0] : $data;
			return $this->update_existing_customer_by_external_id( $contact, $customer_data );
		} else {
			// Customer doesn't exist - try to import them.
			return $this->import_single_customer( $customer_data );
		}
	}

	/**
	 * Get customer by email from ActiveTrail using correct API endpoint
	 *
	 * @param string $email Customer email.
	 * @return array|WP_Error
	 */
	public function get_customer_by_email( $email ) {
		// Use the correct endpoint from the API docs: GET /external?Email={Email}.
		$endpoint = '/external?Email=' . urlencode( $email );
		$response = $this->make_request( 'GET', $endpoint );

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

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

		if ( $response_code === 200 ) {
			// Check if we got any results. Return full array so callers can choose by state (e.g. exclude Blocked).
			if ( ! empty( $data ) && is_array( $data ) && count( $data ) > 0 ) {
				// Customer found - return all contacts for this email.
				return array(
					'exists'  => true,
					'data'    => $data,
					'message' => esc_html__( 'Customer found', 'activetrail-for-woocommerce' ),
				);
			} else {
				// No customer found with this email.
				return array(
					'exists'  => false,
					'data'    => null,
					'message' => esc_html__( 'Customer not found', 'activetrail-for-woocommerce' ),
				);
			}
		} else {
			// Some other error.
			$error_message = $this->extract_error_message( $data );
			return new WP_Error( 'customer_lookup_failed', $error_message );
		}
	}

	/**
	 * Locate an ActiveTrail contact by email or external ID
	 *
	 * @param string $external_id External identifier.
	 * @param string $email Email address.
	 * @return array|WP_Error
	 */
	private function get_contact_by_identifier( $external_id, $email = '' ) {
		if ( ! empty( $email ) && is_email( $email ) ) {
			$by_email = $this->get_customer_by_email( $email );
			if ( is_wp_error( $by_email ) ) {
				return $by_email;
			}

			if ( ! empty( $by_email['exists'] ) ) {
				return $by_email;
			}
		}

		if ( ! empty( $external_id ) ) {
			return $this->get_contact_by_external_id( $external_id );
		}

		return array(
			'exists'  => false,
			'data'    => null,
			'message' => esc_html__( 'Contact not found', 'activetrail-for-woocommerce' ),
		);
	}

	/**
	 * Locate contact by external ID
	 *
	 * @param string $external_id External identifier.
	 * @return array|WP_Error
	 */
	private function get_contact_by_external_id( $external_id ) {
		if ( empty( $external_id ) ) {
			return array(
				'exists'  => false,
				'data'    => null,
				'message' => esc_html__( 'External ID is required', 'activetrail-for-woocommerce' ),
			);
		}

		$endpoints = array(
			'/external?ExternalId=',
			'/external?externalId=',
		);

		foreach ( $endpoints as $endpoint_prefix ) {
			$endpoint = $endpoint_prefix . urlencode( $external_id );
			$response = $this->make_request( 'GET', $endpoint );

			if ( is_wp_error( $response ) ) {
				// If first request fails, try next format.
				$error = $response;
				continue;
			}

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

			if ( $response_code === 200 ) {
				if ( ! empty( $data ) && is_array( $data ) && count( $data ) > 0 ) {
					return array(
						'exists'  => true,
						'data'    => $data[0],
						'message' => esc_html__( 'Contact found', 'activetrail-for-woocommerce' ),
					);
				}

				return array(
					'exists'  => false,
					'data'    => null,
					'message' => esc_html__( 'Contact not found', 'activetrail-for-woocommerce' ),
				);
			}

			$error_message = $this->extract_error_message( $data );
			$error         = new WP_Error( 'contact_lookup_failed', $error_message );
		}

		return isset( $error ) ? $error : array(
			'exists'  => false,
			'data'    => null,
			'message' => esc_html__( 'Contact not found', 'activetrail-for-woocommerce' ),
		);
	}

	/**
	 * Submit cart data to ActiveTrail
	 *
	 * @param array $cart_data Cart data.
	 * @return array
	 */
	public function submit_cart( $cart_data ) {
		if ( empty( $this->api_key ) ) {
			return array(
				'success' => false,
				'message' => esc_html__( 'API key is required', 'activetrail-for-woocommerce' ),
			);
		}

		// Cart events are anonymous & read-only. Contacts are created or updated only on explicit user actions (registration / checkout).
		$endpoint = '/commerce/Cart';
		$response = $this->make_request( 'POST', $endpoint, $cart_data );

		if ( is_wp_error( $response ) ) {
			$this->log_api_error( 'Cart Submit', $response->get_error_message(), $cart_data );
			return array(
				'success' => false,
				'message' => $response->get_error_message(),
			);
		}

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

		if ( in_array( $response_code, array( 200, 201 ), true ) ) {
			// Check if ActiveTrail actually accepted the cart (success field)
			if ( isset( $data['success'] ) && $data['success'] === 0 ) {
				// HTTP 200 but ActiveTrail rejected the cart
				$error_details = isset( $data['errors'] ) && ! empty( $data['errors'] ) 
					? wp_json_encode( $data['errors'] ) 
					: 'No specific error details provided by ActiveTrail';
				
				$this->log_api_error(
					'Cart Submit - Rejected by ActiveTrail',
					'Cart was rejected with success=0',
					array(
						'response_code'    => $response_code,
						'response_data'    => $data,
						'request_data'     => $cart_data,
						'error_details'    => $error_details,
						'validation_hints' => array(
							'check_externalId'   => isset( $cart_data['externalId'] ) ? 'present' : 'MISSING',
							'check_email'        => isset( $cart_data['email'] ) && ! empty( $cart_data['email'] ) ? 'present' : 'MISSING',
							'check_products'     => isset( $cart_data['products'] ) && ! empty( $cart_data['products'] ) ? count( $cart_data['products'] ) . ' products' : 'MISSING',
							'check_totalAmount'  => isset( $cart_data['totalAmount'] ) ? $cart_data['totalAmount'] : 'MISSING',
							'check_storeId'      => isset( $cart_data['storeId'] ) ? $cart_data['storeId'] : 'MISSING',
						),
					)
				);
				
				return array(
					'success' => false,
					'message' => 'ActiveTrail rejected cart: ' . $error_details,
					'data'    => $data,
				);
			}
			
			$this->log_api_success( 'Cart Submit', 'Cart submitted successfully', $data );
			return array(
				'success' => true,
				'message' => esc_html__( 'Cart submitted successfully', 'activetrail-for-woocommerce' ),
				'data'    => $data,
			);
		} else {
			$error_message = $this->extract_error_message( $data );
			$this->log_api_error(
				'Cart Submit',
				$error_message,
				array(
					'response_code' => $response_code,
					'response_data' => $data,
					'request_data'  => $cart_data,
				)
			);
			return array(
				'success' => false,
				'message' => $error_message,
				'data'    => $data,
			);
		}
	}

	/**
	 * Ensure customer exists in ActiveTrail before submitting cart
	 *
	 * @param array $cart_data Cart data with customer info.
	 * @return array
	 */
	private function ensure_customer_exists( $cart_data, $group_id = null ) {
		if ( empty( $cart_data['email'] ) ) {
			return array(
				'success' => false,
				'message' => esc_html__( 'Customer email is required', 'activetrail-for-woocommerce' ),
			);
		}

		// Use passed $group_id or fallback to the one in cart_data.
		if ( empty( $group_id ) && ! empty( $cart_data['group_id'] ) && is_numeric( $cart_data['group_id'] ) && intval( $cart_data['group_id'] ) > 0 ) {
			$group_id = intval( $cart_data['group_id'] );
		}

		// Generate proper external ID.
		$external_id = '';
		if ( function_exists( 'activetrail_external_id_for_contact' ) ) {
			$external_id = activetrail_external_id_for_contact( 0, $cart_data['email'] );
		} else {
			$external_id = 'WC-CONTACT-GUEST-' . md5( $cart_data['email'] );
		}

		// Prepare customer data.
		$customer_data = array(
			'email'        => $cart_data['email'],
			'first_name'   => isset( $cart_data['firstName'] ) ? $cart_data['firstName'] : ( isset( $cart_data['first_name'] ) ? $cart_data['first_name'] : '' ),
			'last_name'    => isset( $cart_data['lastName'] ) ? $cart_data['lastName'] : ( isset( $cart_data['last_name'] ) ? $cart_data['last_name'] : '' ),
			'externalId'   => $external_id,
			'externalName' => 'WooCommerce',
		);

		// Add phone/mobile (map mobile to sms for API).
		if ( ! empty( $cart_data['mobile'] ) ) {
			$customer_data['sms'] = $cart_data['mobile'];
		} elseif ( ! empty( $cart_data['sms'] ) ) {
			$customer_data['sms'] = $cart_data['sms'];
		} elseif ( ! empty( $cart_data['phone'] ) ) {
			$customer_data['sms'] = $cart_data['phone'];
		}

		// Add address (map address to street for API).
		if ( ! empty( $cart_data['address'] ) ) {
			$customer_data['street'] = $cart_data['address'];
		} elseif ( ! empty( $cart_data['street'] ) ) {
			$customer_data['street'] = $cart_data['street'];
		}

		// Add city.
		if ( ! empty( $cart_data['city'] ) ) {
			$customer_data['city'] = $cart_data['city'];
		}

		// Add zip code if available.
		if ( ! empty( $cart_data['zip_code'] ) ) {
			$customer_data['zip_code'] = $cart_data['zip_code'];
			$this->log_api_debug(
				'Added zip_code to customer_data in ensure_customer_exists',
				array(
					'zip_code' => $cart_data['zip_code'],
					'email'    => $cart_data['email'],
				)
			);
		} elseif ( ! empty( $cart_data['postcode'] ) ) {
			$customer_data['zip_code'] = $cart_data['postcode'];
			$this->log_api_debug(
				'Added postcode as zip_code to customer_data in ensure_customer_exists',
				array(
					'postcode' => $cart_data['postcode'],
					'email'    => $cart_data['email'],
				)
			);
		} else {
			$this->log_api_debug(
				'No zip_code or postcode found in cart_data',
				array(
					'cart_data_keys' => array_keys( $cart_data ),
					'email'          => $cart_data['email'],
				)
			);
		}

		// Add subscriptionStatus from cart_data if available.
		if ( isset( $cart_data['subscriptionStatus'] ) ) {
			$customer_data['subscriptionStatus'] = $cart_data['subscriptionStatus'];
			$this->log_api_debug(
				'Added subscriptionStatus to customer_data in ensure_customer_exists',
				array(
					'subscriptionStatus' => $cart_data['subscriptionStatus'],
					'email'              => $cart_data['email'],
				)
			);
		} else {
			$this->log_api_debug(
				'subscriptionStatus NOT found in cart_data',
				array(
					'cart_data_keys' => array_keys( $cart_data ),
					'email'          => $cart_data['email'],
				)
			);
		}

		// Inject groupId if available.
		if ( ! empty( $group_id ) ) {
			$customer_data['groupId'] = $group_id;
			$this->log_api_debug( 'Injected groupId in ensure_customer_exists', array( 'groupId' => $group_id ) );
		} else {
			$this->log_api_debug( 'No valid group_id passed to ensure_customer_exists - will import without group', array( 'group_id' => $group_id ) );
		}

		// Try to import customer (will create if doesn't exist).
		// Note: import_customer can work without group_id - it will just import the customer without assigning to a group.
		return $this->import_customer( $customer_data );
	}

	/**
	 * Submit products to ActiveTrail
	 *
	 * @param array $products_data Products data.
	 * @return array
	 */
	public function submit_products( $products_data ) {
		if ( empty( $this->api_key ) ) {
			return array(
				'success' => false,
				'message' => esc_html__( 'API key is required', 'activetrail-for-woocommerce' ),
			);
		}

		// Base URL already includes /api.
		$endpoint = '/commerce/product';
		$response = $this->make_request( 'POST', $endpoint, $products_data );

		if ( is_wp_error( $response ) ) {
			$this->log_api_error( 'Products Submit', $response->get_error_message(), $products_data );
			return array(
				'success' => false,
				'message' => $response->get_error_message(),
			);
		}

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

		if ( in_array( $response_code, array( 200, 201 ) ) ) {
			$this->log_api_success( 'Products Submit', 'Products submitted successfully', $data );
			return array(
				'success' => true,
				'message' => esc_html__( 'Products submitted successfully', 'activetrail-for-woocommerce' ),
				'data'    => $data,
			);
		} else {
			$error_message = $this->extract_error_message( $data );
			$this->log_api_error(
				'Products Submit',
				$error_message,
				array(
					'response_code' => $response_code,
					'response_data' => $data,
					'request_data'  => $products_data,
				)
			);

			return array(
				'success' => false,
				'message' => $error_message,
				'data'    => $data,
			);
		}
	}

	/**
	 * Update existing customer by external ID
	 *
	 * @param array $existing_data Existing customer data.
	 * @param array $new_data New customer data.
	 * @return array
	 */
	private function update_existing_customer_by_external_id( $existing_data, $new_data ) {
		// Customer exists - update them with new data using PUT request.
		// Prefer the externalId returned from ActiveTrail (existing_data) over the incoming one to avoid 404s.
		$existing_external_id = isset( $existing_data['externalId'] ) ? $existing_data['externalId'] : '';
		$incoming_external_id = isset( $new_data['externalId'] ) ? $new_data['externalId'] : '';

		$external_id = '';
		if ( ! empty( $existing_external_id ) ) {
			$external_id = $existing_external_id;
		} elseif ( ! empty( $incoming_external_id ) ) {
			$external_id = $incoming_external_id;
		}

		if ( empty( $external_id ) ) {
			$this->log_api_error(
				'Customer Update',
				'Cannot update customer: externalId is missing',
				array(
					'email'                => isset( $new_data['email'] ) ? $new_data['email'] : 'unknown',
					'existing_customer_id' => isset( $existing_data['id'] ) ? $existing_data['id'] : 'unknown',
				)
			);
			return array(
				'success' => false,
				'message' => esc_html__( 'Cannot update customer: externalId is missing', 'activetrail-for-woocommerce' ),
				'data'    => $existing_data,
			);
		}

		// If incoming externalId differs from existing, log and override to the existing one to prevent 404.
		if ( ! empty( $incoming_external_id ) && $incoming_external_id !== $external_id ) {
			$this->log_api_debug(
				'Incoming externalId differs from existing - overriding to existing',
				array(
					'incoming_externalId' => $incoming_external_id,
					'existing_externalId' => $external_id,
					'email'               => isset( $new_data['email'] ) ? $new_data['email'] : 'unknown',
				)
			);
		}

		// Keep new_data externalId in sync with the selected external_id.
		$new_data['externalId'] = $external_id;
		
		// Use update_customer to perform PUT request with all customer data.
		// This will update address, phone, city, zip_code regardless of subscription status.
		$update_result = $this->update_customer( $external_id, $new_data );
		
		if ( $update_result['success'] ) {
			$this->log_api_success(
				'Customer Update',
				'Customer updated successfully with all contact details',
				array(
					'email'                => isset( $new_data['email'] ) ? $new_data['email'] : 'unknown',
					'external_id'          => $external_id,
					'has_address'          => ! empty( $new_data['street'] ),
					'has_city'              => ! empty( $new_data['city'] ),
					'has_zip_code'         => ! empty( $new_data['zip_code'] ),
					'has_phone'            => ! empty( $new_data['sms'] ),
				)
			);
		}
		
		return $update_result;
	}

	/**
	 * Smart group addition - handles both regular and external groups
	 * Enhanced to gracefully handle group assignment failures
	 *
	 * @param string|int $group_id Group ID.
	 * @param array      $customer_data Customer data.
	 * @param array      $existing_data Existing customer data (optional)
	 * @return array
	 */
	private function add_customer_to_group_smart( $group_id, $customer_data, $existing_data = null ) {
		// Directly use regular group endpoint as external group endpoint is deprecated.
		return $this->add_customer_to_regular_group( $group_id, $customer_data );
	}



	/**
	 * Add customer to regular group using groups API
	 *
	 * @param string|int $group_id Group ID.
	 * @param array      $customer_data Customer data.
	 * @return array
	 */
	private function add_customer_to_regular_group( $group_id, $customer_data ) {
		$endpoint = '/groups/' . urlencode( $group_id ) . '/members';

		// Prepare data for regular group addition (different format).
		$member_data = array(
			'email'      => $customer_data['email'],
			'first_name' => isset( $customer_data['first_name'] ) ? $customer_data['first_name'] : '',
			'last_name'  => isset( $customer_data['last_name'] ) ? $customer_data['last_name'] : '',
			'sms'        => isset( $customer_data['sms'] ) ? $customer_data['sms'] : '',
			'city'       => isset( $customer_data['city'] ) ? $customer_data['city'] : '',
			'street'     => isset( $customer_data['street'] ) ? $customer_data['street'] : '',
			'zip_code'   => isset( $customer_data['zip_code'] ) ? $customer_data['zip_code'] : '',
			'status'     => 'Subscribed', // Default status for regular groups
			'sms_status' => 'None',
		);

		$response = $this->make_request_with_retry( 'POST', $endpoint, $member_data );

		if ( is_wp_error( $response ) ) {
			return array(
				'success'       => false,
				'message'       => $response->get_error_message(),
				'response_code' => 0,
			);
		}

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

		if ( in_array( $response_code, array( 200, 201 ) ) ) {
			$this->log_api_success( 'Regular Group Update', 'Customer added to regular group successfully', $data );
			return array(
				'success' => true,
				'message' => esc_html__( 'Customer added to regular group successfully', 'activetrail-for-woocommerce' ),
				'data'    => $data,
			);
		} else {
			$error_message = $this->extract_error_message( $data );

			// Check if customer is already in group or if endpoint is forbidden (405).
			// 405 Forbidden usually means the customer is already in the group or endpoint not available.
			if (
				$response_code === 405 ||
				strpos( $error_message, 'already exists' ) !== false ||
				strpos( $error_message, 'duplicate' ) !== false ||
				strpos( $error_message, 'already a member' ) !== false ||
				strpos( $error_message, 'Forbidden' ) !== false ||
				strpos( $error_message, 'not permitted' ) !== false
			) {
				// Treat as success - customer is likely already in the group or endpoint not accessible.
				$this->log_api_success(
					'Regular Group Update',
					'Customer already in group or endpoint not accessible (treating as success)',
					array(
						'response_code' => $response_code,
						'error_message' => $error_message,
					)
				);
				return array(
					'success' => true,
					'message' => esc_html__( 'Customer already in group or group assignment not needed', 'activetrail-for-woocommerce' ),
					'data'    => $data,
				);
			}

			return array(
				'success'       => false,
				'message'       => $error_message,
				'response_code' => $response_code,
				'data'          => $data,
			);
		}
	}

	/**
	 * Import single customer using the import endpoint
	 *
	 * @param array $customer_data Customer data.
	 * @return array
	 */
	private function import_single_customer( $customer_data ) {
		$group_id = isset( $customer_data['groupId'] ) ? $customer_data['groupId'] :
			( isset( $customer_data['group_id'] ) ? $customer_data['group_id'] : null );

		// Remove groupId from customer data as it's not part of the contact structure.
		$clean_customer_data = $customer_data;
		unset( $clean_customer_data['groupId'] );
		unset( $clean_customer_data['group_id'] );

		// Ensure externalId in contact matches the root externalId.
		$clean_customer_data['externalId']   = $customer_data['externalId'];
		$clean_customer_data['externalName'] = $customer_data['externalName'];

		// Rename subscriberStatus to subscriptionStatus if present.
		if ( isset( $clean_customer_data['subscriberStatus'] ) ) {
			$clean_customer_data['subscriptionStatus'] = $clean_customer_data['subscriberStatus'];
			unset( $clean_customer_data['subscriberStatus'] );
		}

		// Prepare the import data according to the API documentation.
		$import_payload = array(
			'contacts' => array(
				array(
					'externalId'   => $customer_data['externalId'],
					'externalName' => $customer_data['externalName'],
					'contact'      => $clean_customer_data,
				),
			),
		);

		// Add group ID if provided and ensure it's an integer greater than 0.
		if ( ! empty( $group_id ) && is_numeric( $group_id ) && intval( $group_id ) > 0 ) {
			$import_payload['group'] = intval( $group_id );
			$this->log_api_debug(
				'Import Customer - Group ID added to import payload',
				array(
					'group_id'    => intval( $group_id ),
					'external_id' => $customer_data['externalId'],
				)
			);
		} else {
			// Don't add group to import payload if not valid.
			$this->log_api_debug(
				'Import Customer - No valid group ID provided',
				array(
					'group_id_raw' => $group_id,
					'external_id'  => $customer_data['externalId'],
				)
			);
		}

		$response = $this->make_request_with_retry( 'POST', '/external/import', $import_payload );

		if ( is_wp_error( $response ) ) {
			$this->log_api_error( 'Customer Import', $response->get_error_message(), $customer_data );
			return array(
				'success' => false,
				'message' => $response->get_error_message(),
			);
		}

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

		if ( in_array( $response_code, array( 200, 201 ) ) ) {
			$this->log_api_success( 'Customer Import', 'Customer imported successfully', $data );

			// If import was successful but we still need to handle group assignment.
			if ( ! empty( $group_id ) && isset( $data['success_count'] ) && $data['success_count'] > 0 ) {
				// Customer was imported successfully, group should be assigned automatically.
				return array(
					'success' => true,
					'message' => esc_html__( 'Customer imported and added to group successfully', 'activetrail-for-woocommerce' ),
					'data'    => $data,
				);
			}

			return array(
				'success' => true,
				'message' => esc_html__( 'Customer imported successfully', 'activetrail-for-woocommerce' ),
				'data'    => $data,
			);
		} else {
			$error_message = $this->extract_error_message( $data );

			// Check if it's the import limit error.
			if ( strpos( $error_message, 'Imports contacts is limited to 1000' ) !== false ) {
				// Log the limit issue but don't fail completely.
				$this->log_api_error(
					'Customer Import - Limit Reached',
					'Import limit reached, customer sync skipped',
					array(
						'response_code' => $response_code,
						'response_data' => $data,
						'request_data'  => $import_payload,
					)
				);

				// Return a "success" but with a warning message.
				return array(
					'success' => true, // Don't fail the order sync because of this
					'message' => esc_html__( 'Customer sync skipped due to ActiveTrail import limits', 'activetrail-for-woocommerce' ),
					'data'    => array( 'warning' => 'import_limit_reached' ),
				);
			}

			// If customer already exists, try to update instead.
			if (
				strpos( $error_message, 'already exists' ) !== false ||
				strpos( $error_message, 'duplicate' ) !== false ||
				strpos( $error_message, 'exists' ) !== false
			) {

				$this->log_api_error(
					'Customer Import - Duplicate Found',
					'Customer exists, attempting to get and update',
					array(
						'response_code' => $response_code,
						'response_data' => $data,
						'request_data'  => $import_payload,
					)
				);

				// Try to get the existing customer and update. data may be array of contacts; use first.
				$existing_customer = $this->get_customer_by_email( $customer_data['email'] );
				if ( ! is_wp_error( $existing_customer ) && $existing_customer['exists'] ) {
					$data    = $existing_customer['data'];
					$contact = ( is_array( $data ) && isset( $data[0] ) ) ? $data[0] : $data;
					return $this->update_existing_customer_by_external_id( $contact, $customer_data );
				}
			}

			$this->log_api_error(
				'Customer Import',
				$error_message,
				array(
					'response_code' => $response_code,
					'response_data' => $data,
					'request_data'  => $import_payload,
				)
			);

			return array(
				'success' => false,
				'message' => $error_message,
				'data'    => $data,
			);
		}
	}

	/**
	 * Import multiple contacts in batch (up to 1000 contacts per request)
	 *
	 * @param array    $contacts_array Array of contact data arrays.
	 * @param int|null $group_id Group ID to assign contacts to.
	 * @return array
	 */
	public function import_contacts_batch( $contacts_array, $group_id = null ) {
		if ( empty( $this->api_key ) ) {
			return array(
				'success' => false,
				'message' => esc_html__( 'API key is required', 'activetrail-for-woocommerce' ),
			);
		}

		// Validate contacts array.
		if ( empty( $contacts_array ) || ! is_array( $contacts_array ) ) {
			return array(
				'success' => false,
				'message' => esc_html__( 'Contacts array is required', 'activetrail-for-woocommerce' ),
			);
		}

		// Limit to 1000 contacts per API request.
		if ( count( $contacts_array ) > 1000 ) {
			return array(
				'success' => false,
				'message' => esc_html__( 'Batch import is limited to 1000 contacts per request', 'activetrail-for-woocommerce' ),
			);
		}

		// Prepare contacts array in the format required by API.
		$formatted_contacts = array();
		foreach ( $contacts_array as $contact_data ) {
			// Validate required fields.
			if ( empty( $contact_data['email'] ) ) {
				continue; // Skip invalid contacts
			}

			// Generate external ID if not provided.
			$external_id = '';
			if ( ! empty( $contact_data['externalId'] ) ) {
				$external_id = $contact_data['externalId'];
			} elseif ( function_exists( 'activetrail_external_id_for_contact' ) ) {
				$user_id     = isset( $contact_data['user_id'] ) ? $contact_data['user_id'] : 0;
				$external_id = activetrail_external_id_for_contact( $user_id, $contact_data['email'] );
			} else {
				$user_id = isset( $contact_data['user_id'] ) ? $contact_data['user_id'] : 0;
				if ( $user_id ) {
					$external_id = 'WC-CONTACT-' . $user_id;
				} else {
					$external_id = 'WC-CONTACT-GUEST-' . md5( $contact_data['email'] );
				}
			}

			// Clean contact data - remove fields that are not part of contact structure.
			$clean_contact = $contact_data;
			unset( $clean_contact['groupId'] );
			unset( $clean_contact['group_id'] );
			unset( $clean_contact['externalId'] );
			unset( $clean_contact['externalName'] );
			unset( $clean_contact['user_id'] );
			unset( $clean_contact['order_id'] );
			unset( $clean_contact['consent_source'] );
			unset( $clean_contact['registration_date'] );
			unset( $clean_contact['source'] );

			// Rename subscriberStatus to subscriptionStatus if present.
			if ( isset( $clean_contact['subscriberStatus'] ) ) {
				$clean_contact['subscriptionStatus'] = $clean_contact['subscriberStatus'];
				unset( $clean_contact['subscriberStatus'] );
			}

			// Map phone to sms if needed.
			if ( empty( $clean_contact['sms'] ) && ! empty( $clean_contact['phone'] ) ) {
				$clean_contact['sms'] = $clean_contact['phone'];
				unset( $clean_contact['phone'] );
			}

			// Get external name.
			$external_name = ! empty( $contact_data['externalName'] ) ? $contact_data['externalName'] : ACTIVETRAIL_EXTERNAL_NAME;

			$formatted_contacts[] = array(
				'externalId'   => $external_id,
				'externalName' => $external_name,
				'contact'      => $clean_contact,
			);
		}

		if ( empty( $formatted_contacts ) ) {
			return array(
				'success' => false,
				'message' => esc_html__( 'No valid contacts to import', 'activetrail-for-woocommerce' ),
			);
		}

		// Prepare the import payload.
		$import_payload = array(
			'contacts' => $formatted_contacts,
		);

		// Add group ID if provided.
		if ( ! empty( $group_id ) && is_numeric( $group_id ) && intval( $group_id ) > 0 ) {
			$import_payload['group'] = intval( $group_id );
		}

		$this->log_api_debug(
			'Batch Import Contacts',
			'Sending batch import request',
			array(
				'contacts_count' => count( $formatted_contacts ),
				'group_id'       => $group_id,
			)
		);

		$response = $this->make_request_with_retry( 'POST', '/external/import', $import_payload );

		if ( is_wp_error( $response ) ) {
			$this->log_api_error(
				'Batch Import Contacts',
				$response->get_error_message(),
				array(
					'contacts_count' => count( $formatted_contacts ),
				)
			);
			return array(
				'success' => false,
				'message' => $response->get_error_message(),
			);
		}

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

		if ( in_array( $response_code, array( 200, 201 ) ) ) {
			$success_count = isset( $data['success_count'] ) ? intval( $data['success_count'] ) : count( $formatted_contacts );
			$failed_count  = isset( $data['failed_count'] ) ? intval( $data['failed_count'] ) : 0;

			$this->log_api_success(
				'Batch Import Contacts',
				sprintf( 'Imported %d contacts successfully', $success_count ),
				array(
					'success_count' => $success_count,
					'failed_count'  => $failed_count,
					'total'         => count( $formatted_contacts ),
				)
			);

			return array(
				'success'       => true,
				/* translators: %d: Number of successfully imported contacts */
				'message'       => sprintf( esc_html__( 'Successfully imported %d contacts', 'activetrail-for-woocommerce' ), $success_count ),
				'data'          => $data,
				'success_count' => $success_count,
				'failed_count'  => $failed_count,
			);
		} else {
			$error_message = $this->extract_error_message( $data );

			$this->log_api_error(
				'Batch Import Contacts',
				$error_message,
				array(
					'response_code'  => $response_code,
					'response_data'  => $data,
					'contacts_count' => count( $formatted_contacts ),
				)
			);

			return array(
				'success' => false,
				'message' => $error_message,
				'data'    => $data,
			);
		}
	}

	/**
	 * Update customer in ActiveTrail
	 *
	 * @param string            $external_id External customer ID.
	 * @param array|WC_Customer $customer_data Customer data or WC_Customer object.
	 * @return array
	 */
	public function update_customer( $external_id, $customer_data ) {
		$this->log( '========== API CLIENT: UPDATE_CUSTOMER START ==========', array(), 'info' );
		$this->log( 'Received external_id: ' . ( $external_id ? $external_id : 'EMPTY!!!' ), array(), 'debug' );
		$this->log( 'Received customer_data type: ' . gettype( $customer_data ), array(), 'debug' );

		if ( empty( $this->api_key ) ) {
			$this->log( '✗✗✗ FAILED: API key is empty', array(), 'error' );
			return array(
				'success' => false,
				'message' => esc_html__( 'API key is required', 'activetrail-for-woocommerce' ),
			);
		}
		$this->log( '✓ API key exists', array(), 'debug' );

		if ( empty( $external_id ) ) {
			$this->log( '✗✗✗ CRITICAL: External ID is EMPTY - cannot proceed with update!', array(), 'error' );
			return array(
				'success' => false,
				'message' => esc_html__( 'External ID is required for customer update', 'activetrail-for-woocommerce' ),
			);
		}
		$this->log( '✓ External ID validated: ' . $external_id, array(), 'debug' );

		// Transform WooCommerce customer to ActiveTrail format if needed.
		if ( is_object( $customer_data ) && method_exists( $customer_data, 'get_email' ) ) {
			$this->log( 'Customer data is WC object, transforming...', array(), 'debug' );
			$customer_data = $this->transform_wc_customer_to_activetrail( $customer_data );
			$this->log( '✓ Transformed to array', array(), 'debug' );
		} else {
			$this->log( 'Customer data is already array', array(), 'debug' );
		}

		// Validate required fields.
		$this->log( 'Validating customer data...', array(), 'debug' );
		$validation = $this->validate_customer_data( $customer_data );
		if ( ! $validation['valid'] ) {
			$this->log( '✗✗✗ VALIDATION FAILED: ' . $validation['message'], array(), 'error' );
			return array(
				'success' => false,
				'message' => $validation['message'],
			);
		}
		$this->log( '✓ Customer data validated successfully', array(), 'debug' );

		// Get externalName from customer_data or use default.
		$external_name = isset( $customer_data['externalName'] ) ? $customer_data['externalName'] : 'WooCommerce';
		$this->log( 'External name: ' . $external_name, array(), 'debug' );

		// Prepare contact data in the correct API format.
		$this->log( '========== PREPARING API PAYLOAD ==========', array(), 'debug' );
		$contact_update = array();

		// Add email (required).
		if ( ! empty( $customer_data['email'] ) ) {
			$contact_update['email'] = $customer_data['email'];
			$this->log( '✓ Added email: ' . $customer_data['email'], array(), 'debug' );
		} else {
			$this->log( '✗ Email is empty!', array(), 'warning' );
		}

		// Add SMS/phone - check multiple fields for phone number.
		if ( ! empty( $customer_data['sms'] ) ) {
			$contact_update['sms'] = $customer_data['sms'];
			$this->log( '✓ Added sms: ' . $customer_data['sms'], array(), 'debug' );
		} elseif ( ! empty( $customer_data['phone'] ) ) {
			$contact_update['sms'] = $customer_data['phone'];
			$this->log( '✓ Added phone as sms: ' . $customer_data['phone'], array(), 'debug' );
		} elseif ( ! empty( $customer_data['mobile'] ) ) {
			$contact_update['sms'] = $customer_data['mobile'];
			$this->log( '✓ Added mobile as sms: ' . $customer_data['mobile'], array(), 'debug' );
		} else {
			$this->log( '- SMS/phone is empty (optional)', array(), 'debug' );
		}

		// Add names - API expects first_name and last_name (snake_case) to match import format.
		if ( isset( $customer_data['first_name'] ) && ! empty( $customer_data['first_name'] ) ) {
			$contact_update['first_name'] = $customer_data['first_name'];
			$this->log( '✓ Added first_name: ' . $customer_data['first_name'], array(), 'debug' );
		} elseif ( isset( $customer_data['firstName'] ) && ! empty( $customer_data['firstName'] ) ) {
			$contact_update['first_name'] = $customer_data['firstName'];
			$this->log( '✓ Added first_name (from firstName): ' . $customer_data['firstName'], array(), 'debug' );
		}
		if ( isset( $customer_data['last_name'] ) && ! empty( $customer_data['last_name'] ) ) {
			$contact_update['last_name'] = $customer_data['last_name'];
			$this->log( '✓ Added last_name: ' . $customer_data['last_name'], array(), 'debug' );
		} elseif ( isset( $customer_data['lastName'] ) && ! empty( $customer_data['lastName'] ) ) {
			$contact_update['last_name'] = $customer_data['lastName'];
			$this->log( '✓ Added last_name (from lastName): ' . $customer_data['lastName'], array(), 'debug' );
		}

		// Add address fields - check multiple field names for compatibility.
		if ( ! empty( $customer_data['city'] ) ) {
			$contact_update['city'] = $customer_data['city'];
			$this->log( '✓ Added city: ' . $customer_data['city'], array(), 'debug' );
		}
		
		// Street/address - check both field names.
		if ( ! empty( $customer_data['street'] ) ) {
			$contact_update['street'] = $customer_data['street'];
			$this->log( '✓ Added street: ' . $customer_data['street'], array(), 'debug' );
		} elseif ( ! empty( $customer_data['address'] ) ) {
			$contact_update['street'] = $customer_data['address'];
			$this->log( '✓ Added address as street: ' . $customer_data['address'], array(), 'debug' );
		}
		
		// Zip code/postcode - check both field names.
		if ( ! empty( $customer_data['zip_code'] ) ) {
			$contact_update['zip_code'] = $customer_data['zip_code'];
			$this->log( '✓ Added zip_code: ' . $customer_data['zip_code'], array(), 'debug' );
		} elseif ( ! empty( $customer_data['postcode'] ) ) {
			$contact_update['zip_code'] = $customer_data['postcode'];
			$this->log( '✓ Added postcode as zip_code: ' . $customer_data['postcode'], array(), 'debug' );
		}

		// Convert subscriptionStatus to status enum.
		// status: None=0, Subscribed=1, Unsubscribed=2, Pending=3.
		$this->log( 'Processing subscriptionStatus...', array(), 'debug' );
		if ( isset( $customer_data['subscriptionStatus'] ) ) {
			$status_value             = $customer_data['subscriptionStatus'] ? 1 : 2; // 1=Subscribed, 2=Unsubscribed
			$contact_update['status'] = $status_value;
			$this->log( '✓ Set status to: ' . $status_value . ' (' . ( $status_value === 1 ? 'Subscribed' : 'Unsubscribed' ) . ')', array(), 'info' );
		} else {
			$this->log( '✗ subscriptionStatus not found in customer_data!', array(), 'warning' );
		}

		// Wrap in contact object as required by API.
		$update_payload = array(
			'contact' => $contact_update,
		);

		$this->log( 'Final payload prepared:', array(), 'debug' );
		$this->log( '  - contact fields count: ' . count( $contact_update ), array(), 'debug' );
		$this->log( '  - Full payload: ' . wp_json_encode( $update_payload ), array(), 'debug' );

		// Add externalName as query parameter.
		$endpoint = '/external/contact/' . urlencode( $external_id ) . '?externalName=' . urlencode( $external_name );
		$this->log( '========== MAKING API REQUEST ==========', array(), 'info' );
		$this->log( 'Endpoint: PUT ' . $endpoint, array(), 'info' );
		$this->log( 'Full URL: ' . self::API_BASE_URL . $endpoint, array(), 'debug' );

		$this->log_api_debug(
			'Customer Update Request Details',
			array(
				'external_id'   => $external_id,
				'external_name' => $external_name,
				'endpoint'      => $endpoint,
				'payload'       => $update_payload,
			)
		);

		$response = $this->make_request_with_retry( 'PUT', $endpoint, $update_payload );

		$this->log( '========== API RESPONSE RECEIVED ==========', array(), 'debug' );

		if ( is_wp_error( $response ) ) {
			$error_msg = $response->get_error_message();
			$this->log( '✗✗✗ WP_Error received: ' . $error_msg, array(), 'error' );
			$this->log_api_error( 'Customer Update', $error_msg, $update_payload );
			return array(
				'success' => false,
				'message' => $error_msg,
			);
		}

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

		$this->log( 'Response code: ' . $response_code, array(), 'info' );
		$this->log( 'Response body: ' . $body, array(), 'debug' );
		$this->log( 'Decoded data: ' . wp_json_encode( $data ), array(), 'debug' );

		if ( in_array( $response_code, array( 200, 201, 204 ) ) ) {
			$this->log( '✓✓✓ SUCCESS: Customer updated successfully (HTTP ' . $response_code . ')', array(), 'info' );
			$this->log_api_success( 'Customer Update', 'Customer updated successfully', $data );
			$this->log( '========== API CLIENT: UPDATE_CUSTOMER END (SUCCESS) ==========', array(), 'info' );
			return array(
				'success' => true,
				'message' => esc_html__( 'Customer updated successfully', 'activetrail-for-woocommerce' ),
				'data'    => $data,
			);
		} else {
			$error_message = $this->extract_error_message( $data );
			$this->log( '✗✗✗ FAILED: HTTP ' . $response_code . ' - ' . $error_message, array(), 'error' );
			$this->log_api_error(
				'Customer Update',
				$error_message,
				array(
					'external_id'   => $external_id,
					'response_code' => $response_code,
					'response_data' => $data,
					'request_data'  => $update_payload,
				)
			);
			$this->log( '========== API CLIENT: UPDATE_CUSTOMER END (FAILED) ==========', array(), 'error' );

			return array(
				'success' => false,
				'message' => $error_message,
				'data'    => $data,
			);
		}
	}

	/**
	 * Make a generic request to the ActiveTrail API
	 *
	 * @param string $method HTTP method (GET, POST, PUT, DELETE).
	 * @param string $endpoint API endpoint.
	 * @param array  $data Request data.
	 * @return array Response data
	 */
	public function request( $method, $endpoint, $data = array() ) {
		if ( empty( $this->api_key ) ) {
			return array(
				'success' => false,
				'message' => esc_html__( 'API key is required', 'activetrail-for-woocommerce' ),
			);
		}

		// Log the request for debugging.
		$this->log_api_debug( "{$method} {$endpoint}", $data );

		// Make the request.
		$response = $this->make_request_with_retry( $method, $endpoint, $data );

		if ( is_wp_error( $response ) ) {
			$this->log_api_error( "{$method} Request", $response->get_error_message(), $data );
			return array(
				'success' => false,
				'message' => $response->get_error_message(),
			);
		}

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

		if ( in_array( $response_code, array( 200, 201, 204 ) ) ) {
			$this->log_api_success( "{$method} Request", "Request to {$endpoint} successful", $response_data );
			return array(
				'success' => true,
				'message' => esc_html__( 'Request successful', 'activetrail-for-woocommerce' ),
				'data'    => $response_data,
			);
		} else {
			$error_message = $this->extract_error_message( $response_data );
			$this->log_api_error(
				"{$method} Request",
				$error_message,
				array(
					'response_code' => $response_code,
					'response_data' => $response_data,
					'request_data'  => $data,
				)
			);

			return array(
				'success' => false,
				'message' => $error_message,
				'data'    => $response_data,
			);
		}
	}

	/**
	 * Submit order to ActiveTrail
	 *
	 * @param array|WC_Order $order_data Order data or WC_Order object.
	 * @return array
	 */
	public function submit_order( $order_data ) {
		if ( empty( $this->api_key ) ) {
			return array(
				'success' => false,
				'message' => esc_html__( 'API key is required', 'activetrail-for-woocommerce' ),
			);
		}

		// Transform WooCommerce order to ActiveTrail format if needed.
		if ( is_object( $order_data ) && method_exists( $order_data, 'get_id' ) ) {
			$order_data = $this->transform_wc_order_to_activetrail( $order_data );
		}

		// Validate required fields.
		$validation = $this->validate_order_data( $order_data );
		if ( ! $validation['valid'] ) {
			return array(
				'success' => false,
				'message' => $validation['message'],
			);
		}

		// ActiveTrail expects an array of orders, not a single order.
		$order_array = array( $order_data );
		$response    = $this->make_request_with_retry( 'POST', '/commerce/Order', $order_array );

		if ( is_wp_error( $response ) ) {
			$this->log_api_error( 'Order Submit', $response->get_error_message(), $order_data );
			return array(
				'success' => false,
				'message' => $response->get_error_message(),
			);
		}

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

		if ( in_array( $response_code, array( 200, 201 ) ) ) {
			// Check if ActiveTrail API returned success.
			if ( isset( $data['success'] ) && $data['success'] > 0 ) {
				$this->log_api_success( 'Order Submit', 'Order submitted successfully', $data );
				return array(
					'success' => true,
					'message' => esc_html__( 'Order sent successfully', 'activetrail-for-woocommerce' ),
					'data'    => $data,
				);
			} else {
				// ActiveTrail returned HTTP 200 but with errors.
				$error_message = 'Order submission failed';
				if ( isset( $data['errors'] ) && is_array( $data['errors'] ) && ! empty( $data['errors'] ) ) {
					$errors = array();
					foreach ( $data['errors'] as $field => $error ) {
						$errors[] = $field . ': ' . $error;
					}
					$error_message = implode( ', ', $errors );
				} elseif ( isset( $data['success'] ) && $data['success'] == 0 ) {
					$error_message = 'ActiveTrail API returned success=0 - order was rejected';
				}

				$this->log_api_error(
					'Order Submit',
					$error_message,
					array(
						'response_code' => $response_code,
						'response_data' => $data,
						'request_data'  => $order_array,
					)
				);

				return array(
					'success' => false,
					'message' => $error_message,
					'data'    => $data,
				);
			}
		} else {
			$error_message = $this->extract_error_message( $data );
			$this->log_api_error(
				'Order Submit',
				$error_message,
				array(
					'response_code' => $response_code,
					'response_data' => $data,
					'request_data'  => $order_array,
				)
			);

			return array(
				'success' => false,
				'message' => $error_message,
				'data'    => $data,
			);
		}
	}

	// Legacy method aliases for backward compatibility.
	public function send_order( $order_data ) {
		return $this->submit_order( $order_data );
	}

	public function send_cart( $cart_data ) {
		return $this->submit_cart( $cart_data );
	}

	/**
	 * Create coupon via ActiveTrail API
	 *
	 * @param array $coupon_data Coupon data.
	 * @return array
	 */
	public function create_coupon( $coupon_data ) {
		if ( empty( $this->api_key ) ) {
			return array(
				'success' => false,
				'message' => esc_html__( 'API key is required', 'activetrail-for-woocommerce' ),
			);
		}

		$response = $this->make_request( 'POST', '/commerce/Coupon', $coupon_data );

		if ( is_wp_error( $response ) ) {
			$this->log_api_error( 'Coupon Create', $response->get_error_message(), $coupon_data );
			return array(
				'success' => false,
				'message' => $response->get_error_message(),
			);
		}

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

		if ( in_array( $response_code, array( 200, 201 ) ) ) {
			$this->log_api_success( 'Coupon Create', 'Coupon created successfully', $data );
			return array(
				'success' => true,
				'message' => esc_html__( 'Coupon created successfully', 'activetrail-for-woocommerce' ),
				'data'    => $data,
			);
		} else {
			$error_message = $this->extract_error_message( $data );
			$this->log_api_error(
				'Coupon Create',
				$error_message,
				array(
					'response_code' => $response_code,
					'response_data' => $data,
					'request_data'  => $coupon_data,
				)
			);

			return array(
				'success' => false,
				'message' => $error_message,
				'data'    => $data,
			);
		}
	}

	/**
	 * Make HTTP request to ActiveTrail API with improved error handling and timeouts
	 *
	 * @param string $method HTTP method (GET, POST, PUT, DELETE).
	 * @param string $endpoint API endpoint.
	 * @param array  $data Request data.
	 * @return array|WP_Error
	 */
	private function make_request( $method, $endpoint, $data = array() ) {
		$url = self::API_BASE_URL . $endpoint;

		// ActiveTrail uses simple token authentication, not Bearer token.
		$headers = array(
			'Authorization' => $this->api_key,
			'Content-Type'  => 'application/json',
			'Accept'        => 'application/json',
			'User-Agent'    => 'ActiveTrail-WooCommerce/' . ACTIVETRAIL_WC_VERSION,
		);

		$args = array(
			'method'          => $method,
			'headers'         => $headers,
			'timeout'         => 40, // Increased overall timeout
			'connect_timeout' => 10, // Specific connect timeout
			'sslverify'       => true,
		);

		if ( ! empty( $data ) && in_array( $method, array( 'POST', 'PUT', 'PATCH' ) ) ) {
			$args['body'] = wp_json_encode( $data );
		}

		// Log the request for debugging (with PII masking).
		$this->log_request( $method, $url, $this->mask_pii_in_data( $data ) );

		$response = wp_remote_request( $url, $args );

		// Log the response for debugging (with PII masking).
		$this->log_response( $response );

		return $response;
	}

	/**
	 * Log API request
	 *
	 * @param string $method HTTP method.
	 * @param string $url Request URL.
	 * @param array  $data Request data.
	 */
	private function log_request( $method, $url, $data ) {
		if ( function_exists( 'activetrail_logger' ) ) {
			$log_data = array(
				'method' => $method,
				'url'    => $url,
				'data'   => $data,
			);
			activetrail_logger()->api( 'API Request: ' . $method . ' ' . $url, 'DEBUG', $log_data );
		}
	}

	/**
	 * Log API response
	 *
	 * @param array|WP_Error $response API response.
	 */
	private function log_response( $response ) {
		if ( function_exists( 'activetrail_logger' ) ) {
			if ( is_wp_error( $response ) ) {
				activetrail_logger()->api( 'API Response Error: ' . $response->get_error_message(), 'ERROR' );
			} else {
				$log_data = array(
					'code' => wp_remote_retrieve_response_code( $response ),
					'body' => wp_remote_retrieve_body( $response ),
				);
				activetrail_logger()->api( 'API Response', 'INFO', $log_data );
			}
		}
	}

	/**
	 * Set API key
	 *
	 * @param string $api_key API key.
	 */
	public function set_api_key( $api_key ) {
		$this->api_key = $api_key;
	}

	/**
	 * Get API key
	 *
	 * @return string
	 */
	public function get_api_key() {
		return $this->api_key;
	}

	/**
	 * Log request error
	 *
	 * @param string $context Error context.
	 * @param string $error_message Error message.
	 */
	private function log_request_error( $context, $error_message ) {
		if ( function_exists( 'activetrail_logger' ) ) {
			activetrail_logger()->api( $context . ': ' . $error_message, 'ERROR' );
		}
	}

	/**
	 * Log request debug information
	 *
	 * @param string $context Debug context.
	 * @param array  $debug_data Debug data.
	 */
	private function log_request_debug( $context, $debug_data ) {
		if ( function_exists( 'activetrail_logger' ) ) {
			activetrail_logger()->api( $context, 'DEBUG', $debug_data );
		}
	}

	// ========================================.
	// ENHANCED METHODS - ENTERPRISE FEATURES.
	// ========================================.

	/**
	 * Make HTTP request with retry logic
	 *
	 * @param string $method HTTP method.
	 * @param string $endpoint API endpoint.
	 * @param array  $data Request data.
	 * @param int    $retry_count Current retry attempt.
	 * @return array|WP_Error
	 */
	private function make_request_with_retry( $method, $endpoint, $data = array(), $retry_count = 0 ) {
		$response = $this->make_request( $method, $endpoint, $data );

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

		$response_code = wp_remote_retrieve_response_code( $response );

		// Retry logic for specific error codes.
		if ( $retry_count < 1 && $this->should_retry( $response_code ) ) {
			$this->log_request_debug(
				'Retrying Request',
				array(
					'method'        => $method,
					'endpoint'      => $endpoint,
					'response_code' => $response_code,
					'retry_count'   => $retry_count + 1,
				)
			);

			// Wait before retry.
			sleep( self::RETRY_DELAY );

			return $this->make_request_with_retry( $method, $endpoint, $data, $retry_count + 1 );
		}

		return $response;
	}

	/**
	 * Determine if request should be retried
	 *
	 * @param int $response_code HTTP response code.
	 * @return bool
	 */
	private function should_retry( $response_code ) {
		// Retry for rate limiting and service unavailable.
		return in_array( $response_code, array( 429, 503 ) );
	}

	/**
	 * Extract error message from API response
	 *
	 * @param array $data Response data.
	 * @return string
	 */
	private function extract_error_message( $data ) {
		if ( isset( $data['Message'] ) ) {
			return $data['Message'];
		}

		if ( isset( $data['message'] ) ) {
			return $data['message'];
		}

		if ( isset( $data['error'] ) ) {
			return is_string( $data['error'] ) ? $data['error'] : wp_json_encode( $data['error'] );
		}

		return esc_html__( 'Unknown API error occurred', 'activetrail-for-woocommerce' );
	}

	/**
	 * Transform WooCommerce customer to ActiveTrail format
	 *
	 * @param WC_Customer|WC_Order $customer WooCommerce customer object or order object.
	 * @return array
	 */
	private function transform_wc_customer_to_activetrail( $customer ) {
		$settings = $this->get_plugin_settings();

		// Handle both WC_Customer and WC_Order objects.
		if ( method_exists( $customer, 'get_billing_email' ) ) {
			// This is a WC_Order object.
			$customer_id = $customer->get_customer_id();

			// Generate proper external ID.
			$external_id = '';
			if ( function_exists( 'activetrail_external_id_for_contact' ) ) {
				// Use the helper function if available.
				$external_id = activetrail_external_id_for_contact( $customer_id, $customer->get_billing_email() );
			} else {
				// Fallback to a safe format that doesn't use email directly.
				if ( $customer_id ) {
					$external_id = 'WC-CONTACT-' . $customer_id;
				} else {
					$external_id = 'WC-CONTACT-GUEST-' . md5( $customer->get_billing_email() );
				}
			}

			$data = array(
				'email'        => $customer->get_billing_email(),
				'sms'          => $customer->get_billing_phone(),
				'first_name'   => $customer->get_billing_first_name(),
				'last_name'    => $customer->get_billing_last_name(),
				'city'         => $customer->get_billing_city(),
				'street'       => trim( $customer->get_billing_address_1() . ' ' . $customer->get_billing_address_2() ),
				'zip_code'     => $customer->get_billing_postcode(),
				'externalId'   => $external_id,
				'externalName' => 'WooCommerce',
			);
		} else {
			// This is a WC_Customer object.
			$customer_id = $customer->get_id();

			// Generate proper external ID.
			$external_id = '';
			if ( function_exists( 'activetrail_external_id_for_contact' ) ) {
				// Use the helper function if available.
				$external_id = activetrail_external_id_for_contact( $customer_id, $customer->get_email() );
			} else {
				// Fallback to a safe format that doesn't use email directly.
				$external_id = 'WC-CONTACT-' . $customer_id;
			}

			$data = array(
				'email'        => $customer->get_email(),
				'sms'          => $customer->get_billing_phone(),
				'first_name'   => $customer->get_first_name(),
				'last_name'    => $customer->get_last_name(),
				'city'         => $customer->get_billing_city(),
				'street'       => $customer->get_billing_address_1(),
				'zip_code'     => $customer->get_billing_postcode(),
				'externalId'   => $external_id,
				'externalName' => 'WooCommerce',
			);
		}

		// Add group ID if configured.
		if ( ! empty( $settings['group_id'] ) ) {
			$data['groupId'] = intval( $settings['group_id'] );
		}

		// Add store ID if configured.
		if ( ! empty( $settings['store_id'] ) ) {
			$data['storeId'] = intval( $settings['store_id'] );
		}

		// Add birthday if available (custom field).
		if ( method_exists( $customer, 'get_meta' ) ) {
			$birthday = $customer->get_meta( 'billing_birthdate' );
			if ( ! empty( $birthday ) ) {
				$data['birthday'] = $birthday;
			}
		}

		return apply_filters( 'activetrail_customer_data', $data, $customer );
	}

	/**
	 * Check if order has an order-level coupon (fixed_cart type)
	 *
	 * @param WC_Order $order Order object.
	 * @return bool True if order has a fixed_cart coupon
	 */
	private function is_order_level_coupon( $order ) {
		foreach ( $order->get_items( 'coupon' ) as $coupon_item ) {
			$coupon = new WC_Coupon( $coupon_item->get_code() );
			if ( 'fixed_cart' === $coupon->get_discount_type() ) {
				return true; // Applies to entire order
			}
		}
		return false;
	}

	/**
	 * Get all coupon types used in an order
	 *
	 * @param WC_Order $order Order object.
	 * @return array Array of coupon codes and their discount types
	 */
	private function get_coupon_types( $order ) {
		$types = array();
		foreach ( $order->get_items( 'coupon' ) as $coupon_item ) {
			$coupon                       = new WC_Coupon( $coupon_item->get_code() );
			$types[ $coupon->get_code() ] = $coupon->get_discount_type(); // e.g. fixed_cart, percent_product, fixed_product
		}
		return $types;
	}

	/**
	 * Normalize order-like objects (refunds, overrides) to WC_Order instances that expose
	 * customer and billing information.
	 *
	 * @param WC_Order|WC_Order_Refund|object|null $order
	 * @return WC_Order|null
	 */
	private function resolve_order_with_customer_details( $order ) {
		if ( ! $order || ! is_object( $order ) ) {
			return null;
		}

		$has_customer_methods = method_exists( $order, 'get_customer_id' ) && method_exists( $order, 'get_billing_email' );
		if ( $has_customer_methods ) {
			return $order;
		}

		$parent_order = null;
		if ( method_exists( $order, 'get_parent_id' ) ) {
			$parent_id = $order->get_parent_id();
			if ( $parent_id ) {
				$parent_order = wc_get_order( $parent_id );
			}
		}

		if ( $parent_order && method_exists( $parent_order, 'get_customer_id' ) && method_exists( $parent_order, 'get_billing_email' ) ) {
			$this->log(
				'Resolved order object to parent order for customer details',
				array(
					'child_order_id'  => method_exists( $order, 'get_id' ) ? $order->get_id() : null,
					'parent_order_id' => $parent_order->get_id(),
					'child_type'      => get_class( $order ),
				),
				'debug'
			);
			return $parent_order;
		}

		$this->log(
			'Unable to resolve order object with customer details',
			array(
				'order_id'   => method_exists( $order, 'get_id' ) ? $order->get_id() : null,
				'order_type' => get_class( $order ),
			),
			'warning'
		);

		return null;
	}

	/**
	 * Transform WooCommerce order to ActiveTrail format
	 *
	 * @param WC_Order $order WooCommerce order object.
	 * @return array
	 */
	public function transform_wc_order_to_activetrail( $order ) {
		$order = $this->resolve_order_with_customer_details( $order );
		if ( ! $order ) {
			throw new \InvalidArgumentException( 'Invalid order provided for ActiveTrail order transformation.' );
		}

		// Get settings first to ensure they're available throughout the method.
		$settings = $this->get_plugin_settings();

		// Get all coupon types used in this order.
		$coupon_types = $this->get_coupon_types( $order );
		// Only consider it an order-level discount if there's a fixed_cart coupon.
		$is_order_level_discount = ! empty( $coupon_types ) && in_array( 'fixed_cart', $coupon_types, true );

		// Get all coupons used in this order with their details.
		$coupons = array();
		foreach ( $order->get_items( 'coupon' ) as $coupon_item ) {
			$coupon_code             = $coupon_item->get_code();
			$coupon_obj              = new WC_Coupon( $coupon_code );
			$coupons[ $coupon_code ] = array(
				'code'                        => $coupon_code,
				'type'                        => $coupon_obj->get_discount_type(),
				'product_ids'                 => $coupon_obj->get_product_ids(),
				'excluded_product_ids'        => $coupon_obj->get_excluded_product_ids(),
				'product_categories'          => $coupon_obj->get_product_categories(),
				'excluded_product_categories' => $coupon_obj->get_excluded_product_categories(),
				'amount'                      => $coupon_obj->get_amount(),
			);
		}

		// Enhanced logging for coupon handling.
		$this->log(
			'Coupon Type Handling',
			array(
				'order_id'       => $order->get_id(),
				'coupon_types'   => $coupon_types,
				'is_order_level' => $is_order_level_discount,
				'total_discount' => $order->get_discount_total(),
				'coupons'        => $coupons,
			)
		);

		// Prepare products array.
		$products       = array();
		$total_quantity = 0;
		$order_subtotal = $order->get_subtotal();

		foreach ( $order->get_items() as $item ) {
			$product = $item->get_product();
			if ( $product ) {
				$quantity        = $item->get_quantity();
				$total_quantity += $quantity;

				// Get product image.
				$image_id  = $product->get_image_id();
				$image_url = $image_id ? wp_get_attachment_url( $image_id ) : '';

				// Calculate proportional shipping allocation for this line item.
				$shipping_price = 0;
				if ( $order_subtotal > 0 && $order->get_shipping_total() > 0 ) {
					// Calculate this item's share of the shipping cost based on its proportion of the subtotal.
					$item_subtotal  = $item->get_subtotal();
					$shipping_price = $order->get_shipping_total() * ( $item_subtotal / $order_subtotal );

					// Round to 2 decimal places.
					$shipping_price = round( $shipping_price, 2 );
				}

				// WooCommerce already apportions discounts per line item, so capture the exact amount.
				$product_discount = floatval( max( 0, $item->get_subtotal() - $item->get_total() ) );

				// Determine if any product-specific coupons apply to this product.
				$product_coupon_codes = array();
				foreach ( $coupons as $coupon_code => $coupon_data ) {
					// Skip order-level coupons.
					if ( 'fixed_cart' === $coupon_data['type'] ) {
						continue;
					}

					$product_id   = $product->get_id();
					$product_cats = wp_get_post_terms( $product_id, 'product_cat', array( 'fields' => 'ids' ) );

					// Check if coupon applies to this product.
					$applies_to_product = false;

					// Check product IDs.
					if ( ! empty( $coupon_data['product_ids'] ) ) {
						if ( in_array( $product_id, $coupon_data['product_ids'], true ) ) {
							$applies_to_product = true;
						}
					}
					// Check product categories.
					elseif ( ! empty( $coupon_data['product_categories'] ) && ! empty( $product_cats ) ) {
						foreach ( $product_cats as $cat_id ) {
							if ( in_array( $cat_id, $coupon_data['product_categories'], true ) ) {
								$applies_to_product = true;
								break;
							}
						}
					}
					// If no specific products or categories are set, this is a cart-level coupon.
					// Cart-level coupons should NOT appear at product level, only at order level.
					// So we skip adding it to products here.

					// Check excluded products.
					if ( $applies_to_product && ! empty( $coupon_data['excluded_product_ids'] ) ) {
						if ( in_array( $product_id, $coupon_data['excluded_product_ids'], true ) ) {
							$applies_to_product = false;
						}
					}

					// Check excluded categories.
					if ( $applies_to_product && ! empty( $coupon_data['excluded_product_categories'] ) && ! empty( $product_cats ) ) {
						foreach ( $product_cats as $cat_id ) {
							if ( in_array( $cat_id, $coupon_data['excluded_product_categories'], true ) ) {
								$applies_to_product = false;
								break;
							}
						}
					}

					// If coupon applies to this product, add it to the list.
					if ( $applies_to_product ) {
						$product_coupon_codes[] = $coupon_code;
					}
				}

				// Create product data array.
				$product_data = array(
					'id'             => strval( $product->get_id() ),
					'name'           => $product->get_name(),
					'quantity'       => floatval( $quantity ),
					'totalamount'    => floatval( $item->get_total() ),
					'description'    => $product->get_description() ? $product->get_description() : ($product->get_short_description() ? $product->get_short_description() : $product->get_name()),
					'sku'            => $product->get_sku() ? $product->get_sku() : strval( $product->get_id() ),
					'type'           => $product->get_type(),
					'category'       => $this->get_product_categories( $product ),
					'unitprice'      => floatval( $item->get_subtotal() / $quantity ), // Unit price before discounts
					'discount'       => $product_discount,
					'tax'            => floatval( $item->get_total_tax() ),
					'totalprice'     => floatval( $item->get_total() ),
					'image'          => $image_url,
					'brand'          => $this->get_product_brand( $product ),
					'color'          => $product->get_attribute( 'pa_color' ),
					'size'           => $this->get_product_size( $product ), // Using the new helper method
					'weight'         => $product->get_weight(),
					'shipping_price' => max( 0, $shipping_price ), // Ensure shipping price is never negative
					'event_date'     => $this->format_date_field( $product->get_meta( '_event_date' ) ),
					'scheduled_date' => $this->format_date_field( $product->get_meta( '_scheduled_date' ) ),
					'pickup_date'    => $this->format_date_field( $product->get_meta( '_pickup_date' ) ),
					'ext2'           => $this->get_product_custom_field( $product, 'ext2' ), // Adding the .2 field
				);

				// Add product-specific coupon codes if any.
				if ( ! empty( $product_coupon_codes ) ) {
					$product_data['coupon'] = implode( ', ', $product_coupon_codes );
				}

				$products[] = $product_data;
			}
		}

		// Get coupon information - only cart-level coupons at order level.
		// Cart-level coupons are: fixed_cart OR percent/fixed_product without product restrictions.
		// Product-level coupons (percent/fixed_product WITH product restrictions) should only appear at product level.
		$cart_coupon_codes = array();
		foreach ( $order->get_items( 'coupon' ) as $coupon_item ) {
			$coupon_code = $coupon_item->get_code();
			$coupon_obj  = new WC_Coupon( $coupon_code );
			$coupon_type = $coupon_obj->get_discount_type();

			// fixed_cart coupons are always cart-level.
			if ( 'fixed_cart' === $coupon_type ) {
				$cart_coupon_codes[] = $coupon_code;
			}
			// For percent and fixed_product coupons, check if they have product restrictions.
			// If no restrictions, they're cart-level and should appear at order level.
			// If they have restrictions, they're product-level and should only appear at product level.
			elseif ( in_array( $coupon_type, array( 'percent', 'fixed_product' ), true ) ) {
				$has_product_restrictions = ! empty( $coupon_obj->get_product_ids() )
					|| ! empty( $coupon_obj->get_product_categories() );

				// If no product restrictions, it's a cart-level coupon.
				if ( ! $has_product_restrictions ) {
					$cart_coupon_codes[] = $coupon_code;
				}
			}
		}
		$coupon = ! empty( $cart_coupon_codes ) ? implode( ', ', $cart_coupon_codes ) : '';

		// Check if customer gave consent for marketing emails - always set explicitly.
		$consent             = get_post_meta( $order->get_id(), '_activetrail_consent', true );
		$subscription_status = ( $consent === 'yes' ) ? true : false;

		// Log consent status for debugging.
		if ( function_exists( 'activetrail_logger' ) ) {
			activetrail_logger()->api(
				sprintf( 'Order %s consent status: %s, subscriberStatus: %s', $order->get_id(), $consent, $subscription_status ? 'true' : 'false' ),
				'DEBUG'
			);
		}

		// Generate proper external ID for the customer.
		$customer_id        = $order->get_customer_id();
		$stored_external_id = $order->get_meta( '_activetrail_external_id', true );
		$external_id        = '';

		if ( ! empty( $stored_external_id ) ) {
			$external_id = $stored_external_id;
		} elseif ( function_exists( 'activetrail_external_id_for_contact' ) ) {
			// Use the helper function if available.
			$external_id = activetrail_external_id_for_contact( $customer_id, $order->get_billing_email() );
		} else {
			// Fallback to a safe format that doesn't use email directly.
			if ( $customer_id ) {
				$external_id = 'WC-CONTACT-' . $customer_id;
			} else {
				$external_id = 'WC-CONTACT-GUEST-' . $order->get_id();
			}
		}

		// Prepare main order data according to ActiveTrail API format (FULL FORMAT).
		$data = array(
			'orderId'            => strval( $order->get_id() ),
			'orderName'          => 'Order #' . $order->get_order_number(),
			'externalId'         => $external_id,
			'externalName'       => 'WooCommerce',
			'firstName'          => $order->get_billing_first_name(),
			'lastName'           => $order->get_billing_last_name(),
			'email'              => $order->get_billing_email(),
			'mobile'             => $order->get_billing_phone(),
			'netAmount'          => floatval( $order->get_subtotal() - $order->get_discount_total() ), // Subtotal minus discounts
			'tax'                => floatval( $order->get_total_tax() ),
			'discount'           => floatval( $order->get_discount_total() ),
			'coupon'             => $coupon,
			'quantity'           => floatval( $total_quantity ),
			'totalAmount'        => floatval( $order->get_total() ),
			'address'            => trim( $order->get_billing_address_1() . ' ' . $order->get_billing_address_2() ),
			'city'               => $order->get_billing_city(),
			'products'           => $products,
			'url'                => $order->get_view_order_url(),
			'purchaseDate'       => $order->get_date_created()->format( 'c' ),
			'lastUpdate'         => $order->get_date_modified()->format( 'c' ),
			'purchaseType'       => 'online',
			'storeId'            => 1, // Required field
			'locationType'       => 'Online', // Fixed value for the store location type
			'subscriptionStatus' => $subscription_status, // Always explicitly set to true or false
			'orderLocationType'  => $this->determine_order_location_type( $order ), // Dynamic based on order contents
			'status'             => $order->get_status(), // Added order status
		);

		// Add store ID if configured.
		if ( ! empty( $settings['store_id'] ) ) {
			$data['storeId'] = intval( $settings['store_id'] );
		}

		// Add group ID if configured - this ensures new contacts are added to the correct group.
		if ( ! empty( $settings['group_id'] ) ) {
			$data['groupId'] = intval( $settings['group_id'] );
		}

		// Add fallback check for empty groupId.
		if ( empty( $data['groupId'] ) && ! empty( $settings['group_id'] ) ) {
			$data['groupId'] = intval( $settings['group_id'] );
		}

		// Log group ID for debugging.
		$this->log(
			'Sync Order - Group ID: ' .
			'group_setting=' . ( $settings['group_id'] ?? 'none' ) . ', ' .
			'payload_group=' . ( $data['groupId'] ?? 'none' ),
			'debug'
		);

		return apply_filters( 'activetrail_order_data', $data, $order );
	}

	/**
	 * Transform WooCommerce cart to ActiveTrail format
	 *
	 * @param WC_Cart $cart WooCommerce cart object.
	 * @return array
	 */
	private function transform_wc_cart_to_activetrail( $cart ) {
		$settings = $this->get_plugin_settings();
		$customer = WC()->customer;

		$data = array(
			'cartId'      => 'WC-CART-' . $customer->get_id() . '-' . time(),
			'email'       => $customer->get_email(),
			'totalAmount' => floatval( $cart->get_total( 'edit' ) ),
			'currency'    => get_woocommerce_currency(),
			'url'         => wc_get_cart_url(),
			'products'    => array(),
		);

		// Add store ID if configured.
		if ( ! empty( $settings['store_id'] ) ) {
			$data['storeId'] = $settings['store_id'];
		}

		// Add products.
		foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
			$product = $cart_item['data'];
			if ( $product ) {
				$data['products'][] = array(
					'productId'   => $product->get_id(),
					'productName' => $product->get_name(),
					'price'       => floatval( $cart_item['line_total'] ),
					'quantity'    => $cart_item['quantity'],
					'category'    => $this->get_product_categories( $product ),
					'url'         => get_permalink( $product->get_id() ),
				);
			}
		}

		return apply_filters( 'activetrail_cart_data', $data, $cart );
	}

	/**
	 * Get product categories as comma-separated string
	 *
	 * @param WC_Product $product Product object.
	 * @return string
	 */
	private function get_product_categories( $product ) {
		$categories = wp_get_post_terms( $product->get_id(), 'product_cat', array( 'fields' => 'names' ) );
		return is_array( $categories ) ? implode( ', ', $categories ) : '';
	}

	/**
	 * Get product brand
	 *
	 * @param WC_Product $product Product object.
	 * @return string
	 */
	private function get_product_brand( $product ) {
		// Try to get brand from various sources.
		$brand = '';

		// First check product categories for brand information.
		$categories = wp_get_post_terms( $product->get_id(), 'product_cat', array( 'fields' => 'names' ) );
		if ( ! empty( $categories ) && is_array( $categories ) ) {
			// Use first category as brand if no other brand info is found.
			$potential_brand = $categories[0];
		}

		// Check for brand taxonomy (common in WooCommerce brand plugins).
		$brand_taxonomies = array( 'product_brand', 'pwb-brand', 'brand', 'pa_brand', 'product_brands', 'yith_product_brand' );

		foreach ( $brand_taxonomies as $taxonomy ) {
			if ( taxonomy_exists( $taxonomy ) ) {
				$terms = get_the_terms( $product->get_id(), $taxonomy );
				if ( $terms && ! is_wp_error( $terms ) && ! empty( $terms ) ) {
					$brand = $terms[0]->name;
					break;
				}
			}
		}

		// Check for brand attribute with expanded attribute names.
		if ( empty( $brand ) ) {
			$brand_attributes = array( 'pa_brand', 'pa_manufacturer', 'brand', 'manufacturer' );
			foreach ( $brand_attributes as $attr ) {
				$attr_value = $product->get_attribute( $attr );
				if ( ! empty( $attr_value ) ) {
					$brand = $attr_value;
					break;
				}
			}
		}

		// Check for brand meta field (common in custom implementations).
		if ( empty( $brand ) ) {
			$meta_keys = array( '_brand', 'brand', '_product_brand', 'product_brand', '_manufacturer', 'manufacturer' );
			foreach ( $meta_keys as $key ) {
				$meta_brand = $product->get_meta( $key );
				if ( ! empty( $meta_brand ) ) {
					$brand = $meta_brand;
					break;
				}
			}
		}

		// Use the potential brand from categories if still empty.
		if ( empty( $brand ) && isset( $potential_brand ) ) {
			$brand = $potential_brand;
		}

		// Fallback to site name as last resort.
		if ( empty( $brand ) ) {
			$brand = get_bloginfo( 'name' );
		}

		return $brand;
	}

	/**
	 * Get product size
	 *
	 * @param WC_Product $product Product object.
	 * @return string
	 */
	private function get_product_size( $product ) {
		// Try different size attribute variations.
		$size_attributes = array( 'pa_size', 'pa_sizes', 'size', 'sizes', 'pa_taille', 'taille' );

		foreach ( $size_attributes as $attr ) {
			$size = $product->get_attribute( $attr );
			if ( ! empty( $size ) ) {
				return $size;
			}
		}

		// Check for size in product variations.
		if ( $product->is_type( 'variable' ) ) {
			$variations = $product->get_available_variations();
			foreach ( $variations as $variation ) {
				if ( isset( $variation['attributes']['attribute_pa_size'] ) ) {
					return $variation['attributes']['attribute_pa_size'];
				}
			}
		}

		// Check meta fields.
		$size_meta_keys = array( '_size', 'size', 'product_size' );
		foreach ( $size_meta_keys as $key ) {
			$size = $product->get_meta( $key );
			if ( ! empty( $size ) ) {
				return $size;
			}
		}

		return '';
	}

	/**
	 * Format date field
	 *
	 * @param string $date_string Date string.
	 * @return string Formatted date
	 */
	private function format_date_field( $date_string ) {
		if ( empty( $date_string ) ) {
			return '';
		}

		// Try to parse the date.
		$timestamp = strtotime( $date_string );
		if ( $timestamp === false ) {
			return $date_string; // Return original if parsing fails
		}

		// Return formatted date.
		return gmdate( 'Y-m-d', $timestamp );
	}

	/**
	 * Get product custom field
	 *
	 * @param WC_Product $product Product object.
	 * @param string     $field_name Field name.
	 * @return string Field value
	 */
	private function get_product_custom_field( $product, $field_name ) {
		// Check meta data first.
		$meta_value = $product->get_meta( '_' . $field_name );
		if ( ! empty( $meta_value ) ) {
			return $meta_value;
		}

		// Check attributes.
		$attr_value = $product->get_attribute( 'pa_' . $field_name );
		if ( ! empty( $attr_value ) ) {
			return $attr_value;
		}

		return '';
	}

	/**
	 * Validate customer data
	 *
	 * @param array $data Customer data.
	 * @return array
	 */
	private function validate_customer_data( $data ) {
		$errors = array();

		if ( empty( $data['email'] ) || ! is_email( $data['email'] ) ) {
			$errors[] = esc_html__( 'Valid email address is required', 'activetrail-for-woocommerce' );
		}

		if ( empty( $data['externalId'] ) ) {
			$errors[] = esc_html__( 'External ID is required', 'activetrail-for-woocommerce' );
		}

		return array(
			'valid'   => empty( $errors ),
			'message' => empty( $errors ) ? '' : implode( ', ', $errors ),
		);
	}

	/**
	 * Validate order data
	 *
	 * @param array $data Order data.
	 * @return array
	 */
	public function validate_order_data( $data ) {
		$errors = array();

		if ( empty( $data['orderId'] ) ) {
			$errors[] = esc_html__( 'Order ID is required', 'activetrail-for-woocommerce' );
		}

		if ( empty( $data['email'] ) || ! is_email( $data['email'] ) ) {
			$errors[] = esc_html__( 'Valid email address is required', 'activetrail-for-woocommerce' );
		}

		if ( ! isset( $data['totalAmount'] ) || ! is_numeric( $data['totalAmount'] ) ) {
			$errors[] = esc_html__( 'Valid total amount is required', 'activetrail-for-woocommerce' );
		}

		return array(
			'valid'   => empty( $errors ),
			'message' => empty( $errors ) ? '' : implode( ', ', $errors ),
		);
	}

	/**
	 * Validate cart data
	 *
	 * @param array $data Cart data.
	 * @return array
	 */
	private function validate_cart_data( $data ) {
		$errors = array();

		if ( empty( $data['cartId'] ) ) {
			$errors[] = esc_html__( 'Cart ID is required', 'activetrail-for-woocommerce' );
		}

		if ( empty( $data['email'] ) || ! is_email( $data['email'] ) ) {
			$errors[] = esc_html__( 'Valid email address is required', 'activetrail-for-woocommerce' );
		}

		if ( ! isset( $data['totalAmount'] ) || ! is_numeric( $data['totalAmount'] ) ) {
			$errors[] = esc_html__( 'Valid total amount is required', 'activetrail-for-woocommerce' );
		}

		return array(
			'valid'   => empty( $errors ),
			'message' => empty( $errors ) ? '' : implode( ', ', $errors ),
		);
	}

	/**
	 * Log API success
	 *
	 * @param string $context Success context.
	 * @param string $message Success message.
	 * @param array  $data Response data.
	 */
	private function log_api_success( $context, $message, $data = array() ) {
		if ( $this->settings && method_exists( $this->settings, 'log_api_success' ) ) {
			$this->settings->log_api_success( $context, $message, $data );
		} elseif ( function_exists( 'activetrail_logger' ) ) {
			activetrail_logger()->api( $context . ': ' . $message, 'INFO', $data );
		}
	}

	/**
	 * Log API error
	 *
	 * @param string $context Error context.
	 * @param string $error_message Error message.
	 * @param array  $additional_data Additional debug data.
	 */
	private function log_api_error( $context, $error_message, $additional_data = array() ) {
		if ( $this->settings && method_exists( $this->settings, 'log_api_error' ) ) {
			$this->settings->log_api_error( $context, $error_message, $additional_data );
		} elseif ( function_exists( 'activetrail_logger' ) ) {
			activetrail_logger()->api( $context . ': ' . $error_message, 'ERROR', $additional_data );
		}
	}

	/**
	 * Log API debug information
	 *
	 * @param string $context Debug context.
	 * @param array  $debug_data Debug data.
	 */
	private function log_api_debug( $context, $debug_data = array() ) {
		if ( $this->settings && method_exists( $this->settings, 'log_api_debug' ) ) {
			$this->settings->log_api_debug( $context, $debug_data );
		} elseif ( function_exists( 'activetrail_logger' ) ) {
			$masked_data = $this->mask_pii_in_data( $debug_data );
			activetrail_logger()->api( $context, 'DEBUG', $masked_data );
		}
	}

	/**
	 * Get plugin settings
	 *
	 * @return array
	 */
	private function get_plugin_settings() {
		if ( $this->settings && method_exists( $this->settings, 'get_all' ) ) {
			return $this->settings->get_all();
		}

		// Fallback to direct option access.
		return get_option( 'activetrail_wc_settings', array() );
	}

	/**
	 * Get last API response for debugging
	 *
	 * @return array
	 */
	public function get_last_response() {
		return $this->last_response;
	}

	/**
	 * Check if API key is in test mode
	 *
	 * @return bool
	 */
	public function is_test_mode() {
		return $this->api_key === 'test_key_123';
	}

	/**
	 * Add contact to group via external ID
	 *
	 * @param string $external_id External contact ID.
	 * @param int    $group_id Group ID.
	 * @return array
	 */
	public function add_contact_to_group_via_external( $external_id, $group_id, $email = '' ) {
		// Check if API key is configured.
		if ( empty( $this->api_key ) ) {
			return array(
				'success' => false,
				'message' => esc_html__( 'API key is required', 'activetrail-for-woocommerce' ),
			);
		}

		// Check if required parameters are provided.
		if ( empty( $external_id ) || empty( $group_id ) ) {
			return array(
				'success' => false,
				'message' => esc_html__( 'External ID and Group ID are required', 'activetrail-for-woocommerce' ),
			);
		}

		// Fallback: Try to get contact by external ID first.
		$contact = $this->get_contact_by_identifier( $external_id, $email );

		if ( ! is_wp_error( $contact ) && $contact['exists'] ) {
			// If contact found, add to group using contact ID.
			$contact_id = $contact['data']['id'] ?? null;
			if ( $contact_id ) {
				return $this->add_contact_to_group( (int) $contact_id, $group_id );
			}
		}

		// If all else fails, log the error and return failure.
		$this->log( 'Failed to add contact to group: ' . $external_id . ' to group ' . $group_id, 'error' );
		return array(
			'success' => false,
			'message' => esc_html__( 'Contact not found by external ID', 'activetrail-for-woocommerce' ),
		);
	}

	/**
	 * Add contact to group
	 *
	 * @param int $contact_id Contact ID.
	 * @param int $group_id Group ID.
	 * @return array
	 */
	public function add_contact_to_group( $contact_id, $group_id ) {
		if ( empty( $this->api_key ) ) {
			return array(
				'success' => false,
				'message' => esc_html__( 'API key is required', 'activetrail-for-woocommerce' ),
			);
		}

		if ( empty( $contact_id ) || empty( $group_id ) ) {
			return array(
				'success' => false,
				'message' => esc_html__( 'Contact ID and Group ID are required', 'activetrail-for-woocommerce' ),
			);
		}

		// Try to add to group using the groups API.
		$endpoint = '/groups/' . urlencode( $group_id ) . '/members/' . urlencode( $contact_id );
		$response = $this->make_request( 'PUT', $endpoint );

		if ( is_wp_error( $response ) ) {
			return array(
				'success' => false,
				'message' => $response->get_error_message(),
			);
		}

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

		if ( in_array( $response_code, array( 200, 201, 204 ) ) ) {
			$this->log( 'Contact added to group successfully: ' . $contact_id . ' to group ' . $group_id );
			return array(
				'success' => true,
				'message' => esc_html__( 'Contact added to group successfully', 'activetrail-for-woocommerce' ),
				'data'    => $data,
			);
		} else {
			$error_message = $this->extract_error_message( $data );

			// Check if contact is already in group (consider this a success).
			if (
				strpos( $error_message, 'already exists' ) !== false ||
				strpos( $error_message, 'duplicate' ) !== false ||
				strpos( $error_message, 'already a member' ) !== false
			) {

				$this->log( 'Contact already in group: ' . $contact_id . ' in group ' . $group_id );
				return array(
					'success' => true,
					'message' => esc_html__( 'Contact already in group', 'activetrail-for-woocommerce' ),
					'data'    => $data,
				);
			}

			$this->log( 'Failed to add contact to group: ' . $error_message, 'error' );
			return array(
				'success' => false,
				'message' => $error_message,
				'data'    => $data,
			);
		}
	}

	/**
	 * Get API client version
	 *
	 * @return string
	 */
	public function get_version() {
		return self::API_VERSION;
	}

	/**
	 * Connect store to ActiveTrail
	 *
	 * @return array Result with success/failure status
	 */
	public function connect_store() {
		if ( empty( $this->api_key ) ) {
			return array(
				'success' => false,
				'message' => esc_html__( 'API key is required', 'activetrail-for-woocommerce' ),
			);
		}

		// Get the coupon token.
		$coupon_token = $this->get_coupon_token();

		// Get the catalog URL (may be empty if feed not accessible).
		$catalog_url = $this->get_catalog_url();

		// Prepare the request data.
		$data = array(
			'storeType'   => 'woocommerce',
			'storeUrl'    => get_site_url(),
			'couponToken' => $coupon_token,
			'action'      => 'connect',
		);

		// Only include catalogUrl if it's not empty (API may require it or allow omission).
		if ( ! empty( $catalog_url ) ) {
			$data['catalogUrl'] = $catalog_url;
		}

		// Make the request.
		$response = $this->make_request( 'POST', '/commerce/store', $data );

		if ( is_wp_error( $response ) ) {
			$this->log_api_error( 'Store Connection', $response->get_error_message(), $data );
			return array(
				'success' => false,
				'message' => $response->get_error_message(),
			);
		}

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

		if ( in_array( $response_code, array( 200, 201 ) ) ) {
			// Check if the response contains the expected data.
			if ( isset( $data['status'] ) ) {
				$status       = $data['status'];
				$is_connected = ( $status === 'connected' );

				// Store the connection status.
				update_option( 'activetrail_store_status', $status );

				$this->log_api_success( 'Store Connection', 'Store ' . ( $is_connected ? 'connected' : 'disconnected' ) . ' successfully', $data );

				$status_text = $is_connected
					? esc_html__( 'connected', 'activetrail-for-woocommerce' )
					: esc_html__( 'disconnected', 'activetrail-for-woocommerce' );
				return array(
					'success' => true,
					/* translators: %s: Connection status (connected or disconnected) */
					'message' => sprintf( esc_html__( 'Store %s successfully', 'activetrail-for-woocommerce' ), $status_text ),
					'data'    => $data,
				);
			} else {
				$this->log_api_error( 'Store Connection', 'Unexpected response format', $data );
				return array(
					'success' => false,
					'message' => esc_html__( 'Unexpected response format', 'activetrail-for-woocommerce' ),
					'data'    => $data,
				);
			}
		} else {
			$error_message = $this->extract_error_message( $data );
			$this->log_api_error(
				'Store Connection',
				$error_message,
				array(
					'response_code' => $response_code,
					'response_data' => $data,
					'request_data'  => $data,
				)
			);

			return array(
				'success' => false,
				'message' => $error_message,
				'data'    => $data,
			);
		}
	}

	/**
	 * Get coupon token for store connection
	 *
	 * @return string
	 */
	private function get_coupon_token() {
		// Check if we already have a stored token.
		$token = get_option( 'activetrail_coupon_token', '' );

		if ( ! empty( $token ) ) {
			return $token;
		}

		// Generate a new token.
		$token = wp_generate_password( 32, false );

		// Store the token for future use.
		update_option( 'activetrail_coupon_token', $token );

		return $token;
	}

	/**
	 * Disconnect store from ActiveTrail
	 * Called on plugin deactivation/uninstall
	 *
	 * @return array Result with success/failure status
	 */
	public function disconnect_store() {
		if ( empty( $this->api_key ) ) {
			// No API key configured, nothing to disconnect.
			return array(
				'success' => true,
				'message' => esc_html__( 'No API key configured, nothing to disconnect', 'activetrail-for-woocommerce' ),
			);
		}

		// Get the coupon token.
		$coupon_token = get_option( 'activetrail_coupon_token', '' );

		// Get the catalog URL (may be empty).
		$catalog_url = $this->get_catalog_url();

		// Prepare the request data.
		$data = array(
			'storeType'   => 'woocommerce',
			'storeUrl'    => get_site_url(),
			'action'      => 'disconnect',
		);

		// Include optional fields if available.
		if ( ! empty( $coupon_token ) ) {
			$data['couponToken'] = $coupon_token;
		}

		// Only include catalogUrl if it's not empty.
		if ( ! empty( $catalog_url ) ) {
			$data['catalogUrl'] = $catalog_url;
		}

		// Log the payload (without headers / API key) for debugging.
		if ( function_exists( 'activetrail_logger' ) ) {
			$log_data = $data;

			// Optionally mask coupon token a bit so שלא נרשום אותו מלא בלוגים.
			if ( isset( $log_data['couponToken'] ) && is_string( $log_data['couponToken'] ) ) {
				$token = $log_data['couponToken'];
				if ( strlen( $token ) > 8 ) {
					$log_data['couponToken'] = substr( $token, 0, 4 ) . '****' . substr( $token, -4 );
				}
			}

			activetrail_logger()->info(
				'Store disconnect payload (sanitized): ' . wp_json_encode( $log_data ),
				'SETTINGS'
			);
		}

		// Make the request with short timeout to avoid blocking admin UX.
		// Use wp_remote_request directly to set custom timeout.
		$url = self::API_BASE_URL . '/commerce/store';
		$headers = array(
			'Authorization' => $this->api_key, // ActiveTrail uses simple token, not Bearer.
			'Content-Type'  => 'application/json',
			'Accept'        => 'application/json',
			'User-Agent'    => 'ActiveTrail-WooCommerce/' . ACTIVETRAIL_WC_VERSION,
		);

		$response = wp_remote_request(
			$url,
			array(
				'method'          => 'POST',
				'headers'         => $headers,
				'body'            => wp_json_encode( $data ),
				'timeout'         => 5, // Short timeout to avoid blocking admin UX.
				'connect_timeout' => 3, // Short connect timeout.
				'sslverify'       => true,
			)
		);

		if ( is_wp_error( $response ) ) {
			// Log minimal error without secrets, fail silently.
			$error_message = $response->get_error_message();
			if ( function_exists( 'activetrail_logger' ) ) {
				activetrail_logger()->error( 'Store disconnect failed: ' . $error_message, 'SETTINGS' );
			}
			// Return success to avoid blocking admin UX.
			return array(
				'success' => true,
				'message' => esc_html__( 'Disconnect attempted (API call failed silently)', 'activetrail-for-woocommerce' ),
			);
		}

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

		if ( in_array( $response_code, array( 200, 201 ) ) ) {
			if ( function_exists( 'activetrail_logger' ) ) {
				activetrail_logger()->info( 'Store disconnected successfully', 'SETTINGS' );
			}
			return array(
				'success' => true,
				'message' => esc_html__( 'Store disconnected successfully', 'activetrail-for-woocommerce' ),
			);
		} else {
			// Log minimal error, fail silently.
			$error_message = $this->extract_error_message( $response_data );
			if ( function_exists( 'activetrail_logger' ) ) {
				activetrail_logger()->error( 'Store disconnect failed: ' . $error_message, 'SETTINGS' );
			}
			// Return success to avoid blocking admin UX.
			return array(
				'success' => true,
				'message' => esc_html__( 'Disconnect attempted (API returned error, but proceeding)', 'activetrail-for-woocommerce' ),
			);
		}
	}

	/**
	 * Get catalog URL for store connection
	 * Returns accessible feed URL or empty string if feed is not available
	 *
	 * @return string Feed URL or empty string if not accessible
	 */
	private function get_catalog_url() {
		return home_url( 'activetrail-feed.xml' );
	}

	/**
	 * Get API base URL
	 *
	 * @return string
	 */
	public function get_api_base_url() {
		return self::API_BASE_URL;
	}

	/**
	 * Mask PII (Personally Identifiable Information) in data for logging
	 *
	 * @param array $data Data to mask.
	 * @return array Masked data
	 */
	private function mask_pii_in_data( $data ) {
		if ( ! is_array( $data ) ) {
			return $data;
		}

		$masked_data = $data;

		// Fields to mask.
		$pii_fields = array(
			'email',
			'sms',
			'mobile',
			'phone',
			'billing_email',
			'billing_phone',
			'first_name',
			'last_name',
			'billing_first_name',
			'billing_last_name',
		);

		// Recursively mask PII fields.
		foreach ( $masked_data as $key => $value ) {
			if ( is_array( $value ) ) {
				$masked_data[ $key ] = $this->mask_pii_in_data( $value );
			} elseif ( in_array( $key, $pii_fields, true ) && is_string( $value ) && ! empty( $value ) ) {
				if ( strpos( $key, 'email' ) !== false && is_email( $value ) ) {
					// Mask email: user***@domain.com
					$parts = explode( '@', $value );
					if ( count( $parts ) === 2 ) {
						$username            = $parts[0];
						$domain              = $parts[1];
						$masked_username     = substr( $username, 0, min( 4, strlen( $username ) ) ) . str_repeat( '*', max( 3, strlen( $username ) - 4 ) );
						$masked_data[ $key ] = $masked_username . '@' . $domain;
					}
				} elseif ( strpos( $key, 'phone' ) !== false || strpos( $key, 'mobile' ) !== false || strpos( $key, 'sms' ) !== false ) {
					// Mask phone: ***-***-1234 (last 4 digits visible).
					$digits = preg_replace( '/\D/', '', $value );
					$length = strlen( $digits );
					if ( $length > 4 ) {
						$visible             = substr( $digits, -4 );
						$masked              = str_repeat( '*', $length - 4 );
						$masked_data[ $key ] = $masked . $visible;
					} else {
						$masked_data[ $key ] = str_repeat( '*', $length );
					}
				} else {
					// Mask names: Jo** Do*.
					$masked_data[ $key ] = substr( $value, 0, 2 ) . str_repeat( '*', max( 1, strlen( $value ) - 2 ) );
				}
			}
		}

		return $masked_data;
	}

	/**
	 * Calculate order subtotal before discounts
	 *
	 * @param WC_Order $order Order object.
	 * @return float
	 */
	private function calculate_order_subtotal_before_discounts( $order ) {
		$subtotal = 0;
		foreach ( $order->get_items() as $item ) {
			$subtotal += (float) $item->get_subtotal();
		}
		return $subtotal;
	}

	/**
	 * Determine order location type based on order contents
	 *
	 * Logic:
	 * - All items virtual → Virtual
	 * - Has local_pickup shipping → Pickup
	 * - Else → Delivery
	 *
	 * @param WC_Order $order Order object.
	 * @return string Location type (Virtual, Pickup, or Delivery)
	 */
	private function determine_order_location_type( $order ) {
		$items = $order->get_items( 'line_item' );
		if ( ! $items ) {
			return 'Delivery'; // Default to delivery if no items
		}

		// Check if all items are virtual.
		$all_virtual = true;
		foreach ( $items as $item ) {
			$product = $item->get_product();
			if ( ! $product || ! $product->is_virtual() ) {
				$all_virtual = false;
				break;
			}
		}

		if ( $all_virtual ) {
			return 'Virtual';
		}

		// Check if order has local pickup shipping.
		foreach ( $order->get_shipping_methods() as $shipping_method ) {
			$method_id = $shipping_method->get_method_id();
			if ( $method_id && false !== strpos( $method_id, 'local_pickup' ) ) {
				return 'Pickup';
			}
		}

		// Default to delivery.
		return 'Delivery';
	}

	/**
	 * Get order refund date
	 *
	 * @param WC_Order $order Order object.
	 * @return string
	 */
	private function get_order_refund_date( $order ) {
		$refunds = $order->get_refunds();
		if ( ! empty( $refunds ) ) {
			$refund = current( $refunds );
			if ( $refund && $refund->get_date_created() ) {
				return $refund->get_date_created()->format( 'c' );
			}
		}
		return '';
	}

	/**
	 * Log message
	 *
	 * @param string       $message Log message.
	 * @param array|string $data Additional data to log.
	 * @param string       $level Log level.
	 */
	private function log( $message, $data = array(), $level = 'info' ) {
		if ( function_exists( 'activetrail_logger' ) ) {
			$level_map    = array(
				'info'    => 'INFO',
				'debug'   => 'DEBUG',
				'warning' => 'WARNING',
				'error'   => 'ERROR',
			);
			$mapped_level = isset( $level_map[ $level ] ) ? $level_map[ $level ] : 'INFO';
			activetrail_logger()->api( $message, $mapped_level, $data );
		}
	}
}
