<?php
/**
 * ActiveTrail Order Handler
 *
 * @package ActiveTrail_WooCommerce
 * @since 1.0.0
 */

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

/**
 * ActiveTrail Order Handler Class
 */
class ActiveTrail_Order_Handler
{

    /**
     * API Client instance
     *
     * @var ActiveTrail_API_Client
     */
    private $api_client;

    /**
     * Plugin settings
     *
     * @var array
     */
    private $settings;

    /**
     * Keep track of orders syncing within the current request to avoid duplicates.
     *
     * @var array<int, float>
     */
    private $orders_currently_syncing = array();

    /**
     * Constructor
     *
     * @param ActiveTrail_API_Client $api_client API client instance.
     * @param array $settings Plugin settings.
     */
    public function __construct($api_client, $settings)
    {
        $this->api_client = $api_client;
        $this->settings = $settings;

        $this->init_hooks();
    }

    /**
     * Initialize WordPress hooks
     */
    private function init_hooks()
    {
        // Order status change hooks.
        add_action('woocommerce_order_status_changed', array($this, 'handle_order_status_change'), 10, 4);

        // New order hooks - multiple hooks to ensure we catch all orders.
        add_action('woocommerce_new_order', array($this, 'handle_new_order'), 10, 2);
        add_action('woocommerce_checkout_order_processed', array($this, 'handle_new_order'), 10, 3);
        add_action('woocommerce_payment_complete', array($this, 'handle_payment_complete'), 10, 1);

        // Checkout completed hook.
        add_action('woocommerce_checkout_order_processed', array($this, 'handle_checkout_completed'), 10, 3);

        // Customer registration hook.
        add_action('woocommerce_created_customer', array($this, 'handle_customer_registration'), 10, 3);

        // Additional hooks for order creation from admin.
        add_action('woocommerce_admin_order_data_after_billing_address', array($this, 'handle_admin_order_created'), 10, 1);
        add_action('woocommerce_order_created', array($this, 'handle_order_created'), 10, 1);
        add_action('woocommerce_order_status_processing', array($this, 'handle_order_status_processing'), 10, 1);
    }

    /**
     * Check if order status should trigger events
     *
     * @param string $status Order status (with or without 'wc-' prefix).
     * @return bool True if status should trigger events.
     */
    private function should_trigger_status_event($status)
    {
        // Get settings from ActiveTrail_Settings class.
        $settings = ActiveTrail_Settings::get_all();
        
        // Get enabled statuses.
        $enabled_statuses = isset($settings['order_statuses']) ? $settings['order_statuses'] : array();
        
        // Backward compatibility: if empty array, treat as all enabled.
        if (empty($enabled_statuses)) {
            return true;
        }
        
        // Ensure status comparison works correctly by removing 'wc-' prefix if present.
        $normalized_status = str_replace('wc-', '', $status);
        
        return in_array($normalized_status, $enabled_statuses, true);
    }

    /**
     * Check if order status event was already sent (deduplication)
     * Uses transient with 5-second time bucket to prevent duplicate events from multiple hooks
     *
     * @param int $order_id Order ID.
     * @param string $status Order status (normalized, without 'wc-' prefix).
     * @return bool True if event was already sent (duplicate), false if new.
     */
    private function is_order_status_event_duplicate($order_id, $status)
    {
        // Normalize status.
        $normalized_status = str_replace('wc-', '', $status);
        
        // Create time bucket: round to nearest 5 seconds to catch rapid duplicate calls.
        $time_bucket = floor(time() / 5) * 5;
        
        // Create transient key.
        $transient_key = 'activetrail_order_dedup_' . $order_id . '_' . $normalized_status . '_' . $time_bucket;
        
        // Check if transient exists (event already sent).
        $existing = get_transient($transient_key);
        
        if ($existing !== false) {
            // Event already sent in this time bucket.
            return true;
        }
        
        // Mark as sent by setting transient (expires in 10 seconds).
        set_transient($transient_key, time(), 10);
        
        return false;
    }

    /**
     * Handle order status change
     *
     * @param int $order_id Order ID.
     * @param string $old_status Old status.
     * @param string $new_status New status.
     * @param WC_Order $order Order object.
     */
    public function handle_order_status_change($order_id, $old_status, $new_status, $order)
    {
        // Check if this status should trigger events.
        if (!$this->should_trigger_status_event($new_status)) {
            $this->log('Order status change skipped - status not enabled: ' . $new_status, 'debug');
            return;
        }

        // Deduplication: Check if this event was already sent (prevents duplicate events from multiple hooks).
        $normalized_status = str_replace('wc-', '', $new_status);
        if ($this->is_order_status_event_duplicate($order_id, $normalized_status)) {
            $this->log('Order status change skipped - duplicate event detected for order ' . $order_id . ' status ' . $new_status, 'debug');
            return;
        }

        // Get settings from ActiveTrail_Settings class.
        $settings = ActiveTrail_Settings::get_all();

        // Debug log to help diagnose status comparison issues.
        $this->log('Checking order status change: ' . $new_status . ' against sync statuses: ' . implode(', ', $settings['order_statuses'] ?? array()), 'debug');

        // Check consent if required.
        if (!$this->should_sync_order($order)) {
            return;
        }

        // Check if order was already synced to ActiveTrail.
        $synced = $order->get_meta('_activetrail_synced', true);

        if ( 'yes' === $synced ) {
            // Order exists in ActiveTrail - update its status.
            $this->log('Order ' . $order_id . ' already synced, updating status from ' . $old_status . ' to ' . $new_status);
            $this->update_order_status_in_activetrail($order, $old_status, $new_status);
        } else {
            // Order doesn't exist in ActiveTrail yet - create it (customer data is included in the order).
            $this->log('Order ' . $order_id . ' not yet synced, creating new order with status ' . $new_status);
            $this->sync_order($order);
        }
    }

    /**
     * Update order status in ActiveTrail
     *
     * @param WC_Order $order Order object.
     * @param string $old_status Old status.
     * @param string $new_status New status.
     * @return bool Success or failure
     */
    private function update_order_status_in_activetrail($order, $old_status, $new_status)
    {
        $order_id = $order->get_id();

        $this->log('========== START UPDATE ORDER STATUS IN ACTIVETRAIL ==========', 'debug');
        $this->log('Order ID: ' . $order_id . ', Old Status: ' . $old_status . ', New Status: ' . $new_status, 'debug');

        // Get order data in ActiveTrail format.
        $this->log('Transforming order to ActiveTrail format...', 'debug');
        $order_data = $this->api_client->transform_wc_order_to_activetrail($order);
        $this->log('Order data transformed successfully', 'debug');

        $override_external_id = $order->get_meta('_activetrail_external_id', true);
        $this->log('Override external_id from order meta: ' . ($override_external_id ? $override_external_id : 'EMPTY'), 'debug');

        if (!empty($override_external_id)) {
            $order_data['externalId'] = $override_external_id;
            $this->log('Applied stored externalId for status update on order ' . $order_id . ': ' . $override_external_id, 'debug');
        }

        // Update contact subscription in ActiveTrail when consent is given (independent of order status sync).
        $this->update_contact_subscription_in_activetrail($order);

        // Log the update operation.
        $this->log('Order Status Update', [
            'order_id' => $order_id,
            'old_status' => $old_status,
            'new_status' => $new_status,
        ], 'info');

        // Use PUT method to update existing order.
        $response = $this->api_client->request('PUT', '/commerce/order/' . $order_id, $order_data);

        // Log the API response.
        $this->log('Order Status Update API Response', [
            'order_id' => $order_id,
            'new_status' => $new_status,
            'api_response' => $response,
        ], 'debug');

        if (isset($response['success']) && $response['success']) {
            $this->log('Successfully updated order ' . $order_id . ' status in ActiveTrail from ' . $old_status . ' to ' . $new_status);
            return true;
        } else {
            $error_message = isset($response['message']) ? $response['message'] : 'Unknown error';
            $this->log('Failed to update order ' . $order_id . ' status in ActiveTrail: ' . $error_message, 'error');
            return false;
        }
    }

    /**
     * Update contact subscription status in ActiveTrail when the order has consent.
     * Used both on order status change and on checkout completed, so that a customer
     * who was previously Unsubscribed gets updated to Subscribed when they give consent on a later order.
     *
     * @param WC_Order $order Order object.
     */
    private function update_contact_subscription_in_activetrail($order)
    {
        if (!$order || !is_object($order)) {
            return;
        }

        if (!$this->order_has_consent($order)) {
            $this->log('Consent not given (order_has_consent=false), skipping contact subscription update', 'debug');
            return;
        }

        $order_id = $order->get_id();
        $this->log('========== STARTING CONTACT SUBSCRIPTION UPDATE ==========', 'info');
        $this->log('Order ' . $order_id . ' has consent=yes, proceeding with contact subscription update', 'info');

        $external_id = '';
        $customer_id = $order->get_customer_id();
        $billing_email = $order->get_billing_email();

        $this->log('Customer ID: ' . ($customer_id ? $customer_id : 'GUEST (0)'), 'debug');
        $this->log('Billing Email: ' . $billing_email, 'debug');

        $this->log('STEP 1: Checking for existing contact...', 'debug');
        $existing_contact_external_id = null;

        if (!empty($billing_email) && !empty($this->api_client)) {
            try {
                $existing_check = $this->api_client->get_customer_by_email($billing_email);
                if (!is_wp_error($existing_check) && !empty($existing_check['exists'])) {
                    $existing_data = $existing_check['data'];
                    $contacts_to_check = is_array($existing_data) && isset($existing_data[0])
                        ? $existing_data
                        : array($existing_data);

                    $preferred_registered_contact = null;
                    $preferred_guest_contact = null;
                    $any_existing_contact = null;

                    foreach ($contacts_to_check as $contact_data) {
                        $contact_external_id = isset($contact_data['externalId']) ? $contact_data['externalId'] : null;
                        if (empty($contact_external_id)) {
                            continue;
                        }
                        if (empty($any_existing_contact)) {
                            $any_existing_contact = $contact_external_id;
                        }
                        if ($customer_id && strpos($contact_external_id, 'WC-CONTACT-' . $customer_id) === 0) {
                            $preferred_registered_contact = $contact_external_id;
                            $this->log('✓ Found registered contact for customer ID ' . $customer_id . ': ' . $contact_external_id, 'info');
                            break;
                        }
                        if (strpos($contact_external_id, 'WC-CONTACT-GUEST-') === 0 && empty($preferred_guest_contact)) {
                            $preferred_guest_contact = $contact_external_id;
                        }
                    }

                    if (!empty($preferred_registered_contact)) {
                        $existing_contact_external_id = $preferred_registered_contact;
                        $this->log('✓ Using registered contact: ' . $existing_contact_external_id, 'info');
                    } elseif (!empty($preferred_guest_contact)) {
                        $existing_contact_external_id = $preferred_guest_contact;
                        $this->log('✓ Using guest contact: ' . $existing_contact_external_id, 'info');
                    } elseif (!empty($any_existing_contact)) {
                        $existing_contact_external_id = $any_existing_contact;
                        $this->log('✓ Using existing contact (fallback): ' . $existing_contact_external_id, 'info');
                    }

                    if (!empty($existing_contact_external_id)) {
                        $order->update_meta_data('_activetrail_external_id', $existing_contact_external_id);
                        $order->save();
                        $this->log('✓ Updated order meta with correct external_id: ' . $existing_contact_external_id, 'info');
                    }
                }
            } catch (Exception $check_e) {
                $this->log('Could not check for existing contact: ' . $check_e->getMessage(), 'debug');
            }
        }

        if (!empty($existing_contact_external_id)) {
            $external_id = $existing_contact_external_id;
            $this->log('✓ Using existing contact from API: ' . $external_id, 'info');
        } else {
            $this->log('STEP 2: Getting external_id from order meta...', 'debug');
            $stored_external_id = $order->get_meta('_activetrail_external_id', true);
            if (!empty($stored_external_id)) {
                $external_id = $stored_external_id;
                $this->log('✓ Using external_id from order meta: ' . $external_id, 'info');
            } else {
                $this->log('✗ WARNING: No external_id stored in order meta, will attempt fallback', 'warning');
            }
        }

        $this->log('STEP 3: Checking if fallback needed...', 'debug');
        if (empty($external_id)) {
            if ($customer_id && function_exists('activetrail_external_id_for_contact')) {
                $external_id = activetrail_external_id_for_contact($customer_id, $billing_email);
                $this->log('✓ Generated external_id from customer ID: ' . $external_id, 'info');
            } else {
                $external_id = $this->generate_guest_external_id($order);
                $this->log('✓ Generated guest external_id: ' . $external_id, 'info');
            }
        }

        $this->log('FINAL EXTERNAL ID: ' . ($external_id ? $external_id : 'STILL EMPTY!!!'), 'info');

        if (empty($external_id)) {
            $this->log('✗✗✗ CRITICAL ERROR: Cannot update contact - no external ID available for order ' . $order_id, 'error');
            $this->log('========== END CONTACT SUBSCRIPTION UPDATE ==========', 'debug');
            return;
        }

        $this->log('========== PREPARING CONTACT UPDATE DATA ==========', 'debug');
        $customer_data = array(
            'externalId' => $external_id,
            'externalName' => 'WooCommerce',
            'email' => $billing_email,
            'first_name' => $order->get_billing_first_name(),
            'last_name' => $order->get_billing_last_name(),
            'sms' => $order->get_billing_phone(),
            'city' => $order->get_billing_city(),
            'street' => trim($order->get_billing_address_1() . ' ' . $order->get_billing_address_2()),
            'zip_code' => $order->get_billing_postcode(),
            'subscriptionStatus' => true
        );

        $this->log('========== CALLING API CLIENT UPDATE_CUSTOMER ==========', 'info');
        $contact_result = $this->api_client->update_customer($external_id, $customer_data);

        $this->log('Response success: ' . (!empty($contact_result['success']) ? 'TRUE' : 'FALSE'), 'debug');

        if (!empty($contact_result['success'])) {
            $this->log('✓✓✓ SUCCESS: Contact subscription status updated successfully for order ' . $order_id, 'info');
            $this->log('========== END CONTACT SUBSCRIPTION UPDATE ==========', 'debug');
            return;
        }

        $error_msg = isset($contact_result['message']) ? $contact_result['message'] : 'Unknown error';
        $response_code = isset($contact_result['data']['Message']) ? json_decode($contact_result['data']['Message'], true) : null;
        $is_404 = !empty($response_code['status']) && $response_code['status'] === '404';

        if ($is_404) {
            $this->log('✗✗✗ FAILED: Contact not found (404) - attempting to find correct contact...', 'error');
        } else {
            $this->log('✗✗✗ FAILED: Contact subscription status update failed for order ' . $order_id . ': ' . $error_msg, 'error');
        }

        if ($is_404 && !empty($billing_email) && !empty($this->api_client)) {
            try {
                $retry_check = $this->api_client->get_customer_by_email($billing_email);
                if (!is_wp_error($retry_check) && !empty($retry_check['exists'])) {
                    $retry_data = $retry_check['data'];
                    $retry_contacts = is_array($retry_data) && isset($retry_data[0]) ? $retry_data : array($retry_data);
                    $correct_external_id = null;
                    foreach ($retry_contacts as $retry_contact) {
                        $retry_external_id = isset($retry_contact['externalId']) ? $retry_contact['externalId'] : null;
                        if (empty($retry_external_id)) {
                            continue;
                        }
                        if ($customer_id && strpos($retry_external_id, 'WC-CONTACT-' . $customer_id) === 0) {
                            $correct_external_id = $retry_external_id;
                            break;
                        }
                        if (empty($correct_external_id)) {
                            $correct_external_id = $retry_external_id;
                        }
                    }
                    if (!empty($correct_external_id) && $correct_external_id !== $external_id) {
                        $this->log('✓ Found correct contact: ' . $correct_external_id . ', retrying update...', 'info');
                        $order->update_meta_data('_activetrail_external_id', $correct_external_id);
                        $order->save();
                        $customer_data['externalId'] = $correct_external_id;
                        $retry_result = $this->api_client->update_customer($correct_external_id, $customer_data);
                        if (!empty($retry_result['success'])) {
                            $this->log('✓✓✓ SUCCESS: Contact subscription status updated successfully after retry for order ' . $order_id, 'info');
                        } else {
                            $this->log('✗✗✗ FAILED: Contact update still failed after retry', 'error');
                        }
                    }
                }
            } catch (Exception $retry_e) {
                $this->log('Could not retry contact update: ' . $retry_e->getMessage(), 'error');
            }
        }

        $this->log('========== END CONTACT SUBSCRIPTION UPDATE ==========', 'debug');
    }

    /**
     * Handle new order
     *
     * @param int $order_id Order ID.
     * @param WC_Order|array $order_or_data Order object or posted data.
     * @param WC_Order $order Order object (optional, for checkout_order_processed hook).
     */
    public function handle_new_order($order_id, $order_or_data, $order = null)
    {
        // Handle different parameter combinations from different hooks.
        if ($order === null) {
            // From woocommerce_new_order hook.
            $order = $order_or_data;
        } else {
            // From woocommerce_checkout_order_processed hook.
            $order = $order;
        }

        if (!$order || !is_object($order)) {
            $this->log('Invalid order object for order: ' . $order_id, 'error');
            return;
        }

        // Check if order was already synced - HPOS compatible.
        $order = wc_get_order($order_id);
        if (!$order) {
            $this->log('Invalid order ID: ' . $order_id, 'error');
            return;
        }

        $synced = $order->get_meta('_activetrail_synced', true);
        if ( 'yes' === $synced ) {
            $this->log('Order already synced: ' . $order_id);
            return;
        }

        // Get settings from ActiveTrail_Settings class.
        $settings = ActiveTrail_Settings::get_all();

        // Log new order.
        $this->log('New order created: ' . $order_id . ' (Status: ' . $order->get_status() . ')');

        // Check if this status should trigger events.
        $current_status = $order->get_status();
        if (!$this->should_trigger_status_event($current_status)) {
            $this->log('New order skipped - status not enabled: ' . $current_status . ' for order: ' . $order_id, 'debug');
            return;
        }

        // Debug log to help diagnose status comparison issues.
        $this->log('Checking order status: ' . $current_status . ' against sync statuses: ' . implode(', ', $settings['order_statuses'] ?? array()), 'debug');

        // Check consent if required.
        if ($this->should_sync_order($order)) {
            // Send order data to ActiveTrail (customer data is included in the order).
            $this->sync_order($order);
        } else {
            $this->log('Order sync skipped due to consent check: ' . $order_id);
        }
    }

    /**
     * Handle checkout completed
     *
     * @param int $order_id Order ID.
     * @param array $posted_data Posted data.
     * @param WC_Order $order Order object.
     */
    public function handle_checkout_completed($order_id, $posted_data, $order)
    {
        // Store consent information - HPOS compatible.
        $order = wc_get_order($order_id);
        if ($order) {
            $customer_id = $order->get_customer_id();
            $has_consent = ActiveTrail_Consent_Helper::has_consent_from_data( $posted_data );

            // Save to order meta.
            if ($has_consent) {
                $order->update_meta_data('_activetrail_consent', 'yes');
                // Add standard meta keys for compatibility with other plugins.
                $order->update_meta_data('activetrail_optin', 'yes');
                $order->update_meta_data('activetrail_subscribe', '1');
            } else {
                // Always store a value for _activetrail_consent, even when not checked.
                $order->update_meta_data('_activetrail_consent', 'no');
                // Add standard meta keys for compatibility with other plugins.
                $order->update_meta_data('activetrail_optin', 'no');
                $order->update_meta_data('activetrail_subscribe', '0');
            }
            $order->save();

            // Also save to user meta if customer is logged in.
            if ($customer_id > 0) {
                if ($has_consent) {
                    update_user_meta($customer_id, '_activetrail_consent', 'yes');
                    update_user_meta($customer_id, '_activetrail_consent_date', current_time('mysql'));
                    // Add standard meta keys for compatibility with other plugins.
                    update_user_meta($customer_id, 'activetrail_optin', 'yes');
                    update_user_meta($customer_id, 'activetrail_subscribe', '1');

                    $this->log('Consent saved to user meta during checkout', 'debug', array(
                        'user_id' => $customer_id,
                        'order_id' => $order_id
                    ));
                } else {
                    update_user_meta($customer_id, '_activetrail_consent', 'no');
                    // Add standard meta keys for compatibility with other plugins.
                    update_user_meta($customer_id, 'activetrail_optin', 'no');
                    update_user_meta($customer_id, 'activetrail_subscribe', '0');
                }
            }

            // When consent was given, update the contact's subscription status in ActiveTrail immediately.
            // This ensures a customer who was previously Unsubscribed becomes Subscribed on a later order with consent.
            if ($has_consent) {
                $this->update_contact_subscription_in_activetrail($order);
            }
        }

        $this->log('Checkout completed for order: ' . $order_id);
    }

    /**
     * Handle payment complete
     *
     * @param int $order_id Order ID.
     */
    public function handle_payment_complete($order_id)
    {
        $order = wc_get_order($order_id);
        if (!$order) {
            return;
        }

        $this->log('Payment completed for order: ' . $order_id);

        // Check if order was already synced - HPOS compatible.
        $synced = $order->get_meta('_activetrail_synced', true);
        if ( 'yes' === $synced ) {
            return;
        }

        // Check if this status should trigger events.
        $current_status = $order->get_status();
        if (!$this->should_trigger_status_event($current_status)) {
            $this->log('Payment complete skipped - status not enabled: ' . $current_status . ' for order: ' . $order_id, 'debug');
            return;
        }

        // Get settings from ActiveTrail_Settings class.
        $settings = ActiveTrail_Settings::get_all();

        // Debug log to help diagnose status comparison issues.
        $this->log('Checking order status: ' . $current_status . ' against sync statuses: ' . implode(', ', $settings['order_statuses'] ?? array()), 'debug');

        // Check consent if required.
        if ($this->should_sync_order($order)) {
            // Send order data to ActiveTrail (customer data is included in the order).
            $this->sync_order($order);
        }
    }

    /**
     * Handle admin order created
     *
     * @param WC_Order $order Order object.
     */
    public function handle_admin_order_created($order)
    {
        if (!$order) {
            return;
        }

        $order_id = $order->get_id();
        $this->log('Admin order created: ' . $order_id);

        // Check if order was already synced - HPOS compatible.
        $synced = $order->get_meta('_activetrail_synced', true);
        if ( 'yes' === $synced ) {
            return;
        }

        // Check if this status should trigger events.
        $current_status = $order->get_status();
        if (!$this->should_trigger_status_event($current_status)) {
            $this->log('Payment complete skipped - status not enabled: ' . $current_status . ' for order: ' . $order_id, 'debug');
            return;
        }

        // Get settings from ActiveTrail_Settings class.
        $settings = ActiveTrail_Settings::get_all();

        // Debug log to help diagnose status comparison issues.
        $this->log('Checking order status: ' . $current_status . ' against sync statuses: ' . implode(', ', $settings['order_statuses'] ?? array()), 'debug');

        // Check consent if required.
        if ($this->should_sync_order($order)) {
            // Send order data to ActiveTrail (customer data is included in the order).
            $this->sync_order($order);
        }
    }

    /**
     * Handle order created (general hook)
     *
     * @param WC_Order $order Order object.
     */
    public function handle_order_created($order)
    {
        if (!$order) {
            return;
        }

        $order_id = $order->get_id();
        $this->log('Order created hook triggered: ' . $order_id);

        // Check if order was already synced - HPOS compatible.
        $synced = $order->get_meta('_activetrail_synced', true);
        if ( 'yes' === $synced ) {
            return;
        }

        // Check if this status should trigger events.
        $current_status = $order->get_status();
        if (!$this->should_trigger_status_event($current_status)) {
            $this->log('Payment complete skipped - status not enabled: ' . $current_status . ' for order: ' . $order_id, 'debug');
            return;
        }

        // Get settings from ActiveTrail_Settings class.
        $settings = ActiveTrail_Settings::get_all();

        // Debug log to help diagnose status comparison issues.
        $this->log('Checking order status: ' . $current_status . ' against sync statuses: ' . implode(', ', $settings['order_statuses'] ?? array()), 'debug');

        // Check consent if required.
        if ($this->should_sync_order($order)) {
            // Send order data to ActiveTrail (customer data is included in the order).
            $this->sync_order($order);
        }
    }

    /**
     * Handle order status processing
     *
     * @param int $order_id Order ID.
     */
    public function handle_order_status_processing($order_id)
    {
        $order = wc_get_order($order_id);
        if (!$order) {
            return;
        }

        $this->log('Order status changed to processing: ' . $order_id);

        // Check if order was already synced - HPOS compatible.
        $synced = $order->get_meta('_activetrail_synced', true);
        if ( 'yes' === $synced ) {
            return;
        }

        // Check if this status should trigger events.
        $current_status = $order->get_status();
        if (!$this->should_trigger_status_event($current_status)) {
            $this->log('Payment complete skipped - status not enabled: ' . $current_status . ' for order: ' . $order_id, 'debug');
            return;
        }

        // Get settings from ActiveTrail_Settings class.
        $settings = ActiveTrail_Settings::get_all();

        // Debug log to help diagnose status comparison issues.
        $this->log('Checking order status: ' . $current_status . ' against sync statuses: ' . implode(', ', $settings['order_statuses'] ?? array()), 'debug');

        // Check consent if required.
        if ($this->should_sync_order($order)) {
            // Send order data to ActiveTrail (customer data is included in the order).
            $this->sync_order($order);
        }
    }

    /**
     * Handle customer registration
     *
     * @param int $customer_id Customer ID.
     * @param array $new_customer_data Customer data.
     * @param string $password_generated Whether password was generated.
     */
    public function handle_customer_registration($customer_id, $new_customer_data, $password_generated)
    {
        $this->log('New customer registered: ' . $customer_id);

        // Customer registration is now handled by User_Registration_Handler.
        // We'll sync the customer when they place their first order.
    }

    /**
     * Check if order should be synced
     *
     * @param WC_Order $order Order object.
     * @return bool
     */
    private function should_sync_order($order)
    {
        // Check if order status should trigger events (status filtering is handled by should_trigger_status_event).
        $current_status = $order->get_status();
        if (!$this->should_trigger_status_event($current_status)) {
            $this->log('Order status not in sync list: ' . $current_status . ' for order ' . $order->get_id());
            return false;
        }

        // Always return true - we'll sync all orders but set subscriberStatus based on consent.
        return true;
    }

    /**
     * Whether the order/customer has consent for marketing (checkout consent OR registration consent).
     *
     * @param WC_Order $order Order object.
     * @return bool
     */
    private function order_has_consent($order)
    {
        $consent = $order->get_meta('_activetrail_consent', true);
        if ($consent === 'yes') {
            return true;
        }
        $customer_id = $order->get_customer_id();
        if ($customer_id > 0) {
            $reg_consent    = get_user_meta($customer_id, '_activetrail_registration_consent', true);
            $checkout_meta  = get_user_meta($customer_id, '_activetrail_consent', true);
            if ($reg_consent === 'yes' || $checkout_meta === 'yes') {
                return true;
            }
        }
        return false;
    }

    /**
     * Sync order to ActiveTrail
     *
     * @param WC_Order $order Order object.
     * @return bool
     */
    public function sync_order($order)
    {
        $order_id = $order->get_id();

        if ($this->is_order_currently_syncing($order_id)) {
            $this->log('Order ' . $order_id . ' sync already running in this request, skipping duplicate trigger', 'debug');
            return false;
        }

        $this->lock_order_sync($order_id);

        try {
            // Get settings from ActiveTrail_Settings class.
            $settings = ActiveTrail_Settings::get_all();

            if (empty($settings['api_key'])) {
                $this->log('API key not configured', 'error');
                return false;
            }

            $this->log('Starting order sync for order: ' . $order_id);

            $customer_id = $order->get_customer_id();
            $stored_external_id = $order->get_meta('_activetrail_external_id', true);

            if (empty($stored_external_id)) {
                $this->log('Order ' . $order_id . ' missing externalId meta, ensuring contact exists before commerce sync', 'info');
                $this->ensure_guest_contact_synced($order, $settings);
                $stored_external_id = $order->get_meta('_activetrail_external_id', true);
            }

            // Check if order has items.
            if ($order->get_item_count() == 0) {
                $this->log('Order ' . $order_id . ' has no items, skipping sync', 'warning');
                return false;
            }

            // Check if order has valid customer email.
            $customer_email = $order->get_billing_email();
            if (empty($customer_email) || !is_email($customer_email)) {
                $this->log('Order ' . $order_id . ' has invalid customer email: ' . $customer_email, 'error');
                return false;
            }

            // Check if order has valid total (allow 0 for 100% discount coupons).
            $order_total = $order->get_total();
            if (empty($order_total) && $order_total !== 0) {
                $this->log('Order ' . $order_id . ' has invalid total: ' . $order_total, 'error');
                return false;
            }

            // Log order details for debugging.
            $this->log('Order ' . $order_id . ' total: ' . $order_total . ', subtotal: ' . $order->get_subtotal() . ', discount: ' . $order->get_discount_total(), 'debug');

            // Check consent status to set subscriberStatus (checkout OR registration consent).
            $subscriber_status = $this->order_has_consent($order);

            // Log consent status for debugging.
            $this->log('Order ' . $order_id . ' consent (order_has_consent): ' . ($subscriber_status ? 'true' : 'false'), 'debug');

            // Use the API client's transform method instead of prepare_order_data.
            $order_data = $this->api_client->transform_wc_order_to_activetrail($order);

            // CRITICAL FIX: Always use the external ID stored in the order meta!
            // This preserves the original identity (guest or registered) from when the order was created.
            $this->log('Checking for stored external_id in order meta...', 'debug');
            $stored_external_id = $order->get_meta('_activetrail_external_id', true);

            if (!empty($stored_external_id)) {
                // Use the stored external ID - this is the source of truth.
                $order_data['externalId'] = $stored_external_id;
                $this->log('Using stored external_id from order meta: ' . $stored_external_id, 'info');
                $this->log('This preserves the original contact identity from order creation', 'debug');
            } else {
                // Fallback: Generate external ID if not stored (shouldn't happen after ensure_guest_contact_synced).
                $this->log('WARNING: No stored external_id found, generating fallback...', 'warning');
                if ($customer_id && function_exists('activetrail_external_id_for_contact')) {
                    $fallback_external_id = activetrail_external_id_for_contact($customer_id, $order->get_billing_email());
                    $order_data['externalId'] = $fallback_external_id;
                    $this->log('Generated fallback external_id from customer ID: ' . $fallback_external_id, 'info');
                } else {
                    $fallback_external_id = $stored_external_id; // Should already be set by ensure_guest_contact_synced
                    if (!empty($fallback_external_id)) {
                        $order_data['externalId'] = $fallback_external_id;
                        $this->log('Using fallback external_id: ' . $fallback_external_id, 'info');
                    }
                }
            }

            // Always set subscriptionStatus explicitly based on consent.
            // This ensures it's always present in the payload with the correct value.
            $order_data['subscriptionStatus'] = $subscriber_status;

            // Add group ID - ensure it's always set if configured, regardless of consent status.
            $customer_id = $order->get_customer_id();
            $configured_group_id = !empty($settings['group_id']) ? (int) $settings['group_id'] : 0;
            if ($configured_group_id > 0) {
                $order_data['groupId'] = $configured_group_id;
            }

            // Log the order data being sent (for debugging).
            $this->log('Order data prepared for order ' . $order_id . ': ' . wp_json_encode($order_data), 'debug');

            $result = $this->api_client->submit_order($order_data);

            // Log the API response (for debugging).
            $this->log('API response for order ' . $order_id . ': ' . wp_json_encode($result), 'debug');

            if ($result['success']) {
                // Mark order as synced - HPOS compatible.
                $order->update_meta_data('_activetrail_synced', 'yes');
                $order->update_meta_data('_activetrail_sync_date', current_time('mysql'));
                $order->save();

                // Ensure contact is in the configured group regardless of consent (group membership controls automations).
                if ($configured_group_id > 0) {
                    $contact_external_id = $order->get_meta('_activetrail_external_id', true);

                    if (empty($contact_external_id)) {
                        if (function_exists('activetrail_external_id_for_contact')) {
                            $contact_external_id = activetrail_external_id_for_contact($customer_id, $order->get_billing_email());
                        } elseif (!empty($customer_id)) {
                            $contact_external_id = 'WC-CONTACT-' . $customer_id;
                        } else {
                            $contact_external_id = $this->generate_guest_external_id($order);
                        }
                    }

                    if (!empty($contact_external_id)) {
                        try {
                            $group_result = $this->api_client->add_contact_to_group_via_external(
                                $contact_external_id,
                                $configured_group_id,
                                $order->get_billing_email()
                            );

                            if (is_array($group_result) && !empty($group_result['success'])) {
                                $this->log('Contact added to group successfully: ' . $contact_external_id . ' -> ' . $configured_group_id, 'info');
                            } elseif (is_array($group_result)) {
                                $this->log('Add to group returned error for ' . $contact_external_id . ': ' . ($group_result['message'] ?? 'unknown'), 'error');
                            }
                        } catch (\Throwable $e) {
                            $this->log('Add to group failed: ' . $e->getMessage(), 'error');
                        }
                    }
                } else {
                    $this->log('Skipping group assignment because no valid group_id is configured', 'debug');
                }

                $this->log('Order synced successfully: ' . $order_id);
                return true;
            } else {
                $this->log('Failed to sync order ' . $order_id . ': ' . $result['message'], 'error');

                // Log additional error details if available.
                if (isset($result['data'])) {
                    $this->log('Error details for order ' . $order_id . ': ' . wp_json_encode($result['data']), 'error');
                }

                return false;
            }
        } finally {
            $this->unlock_order_sync($order_id);
        }
    }

    /**
     * Sync customer to ActiveTrail
     *
     * @param WC_Order $order Order object.
     * @return bool
     */
    public function sync_customer($order)
    {
        // Get settings from ActiveTrail_Settings class.
        $settings = ActiveTrail_Settings::get_all();

        if (empty($settings['api_key'])) {
            $this->log('API key not configured', 'error');
            return false;
        }

        // Check if customer already synced.
        $customer_id = $order->get_customer_id();
        if ( 'yes' === $customer_id && get_user_meta($customer_id, '_activetrail_synced', true) ) {
            $this->log('Customer already synced: ' . $order->get_billing_email());
            return true;
        }

        $this->log('Starting customer sync for: ' . $order->get_billing_email());

        // Prepare customer data.
        $customer_data = $this->prepare_customer_data($order);

        // Check consent status to set subscriberStatus (checkout OR registration consent).
        $subscriber_status = $this->order_has_consent($order);

        // Log consent status for debugging.
        $this->log('Customer ' . $order->get_billing_email() . ' consent (order_has_consent): ' . ($subscriber_status ? 'true' : 'false'), 'debug');

        // Always set subscriptionStatus explicitly based on consent.
        $customer_data['subscriptionStatus'] = $subscriber_status;

        // Ensure group ID is always set in the payload to fix mandatory field error.
        if (!empty($settings['group_id'])) {
            $customer_data['groupId'] = intval($settings['group_id']);
            $this->log('Set groupId in customer sync data to: ' . $settings['group_id'], 'debug');
        } else {
            $this->log('No group_id configured in settings for customer sync', 'warning');
        }

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

        // Log the customer data being sent (for debugging).
        $this->log('Customer data prepared for order ' . $order->get_id() . ': ' . wp_json_encode($customer_data), 'debug');

        // Send to ActiveTrail using the prepared customer data.
        $result = $this->api_client->import_customer($customer_data);

        if ($result['success']) {
            // Mark customer as synced.
            if ($customer_id) {
                update_user_meta($customer_id, '_activetrail_synced', 'yes');
                update_user_meta($customer_id, '_activetrail_sync_date', current_time('mysql'));
            }

            $this->log('Customer synced successfully: ' . $order->get_billing_email());
            return true;
        } else {
            $this->log('Failed to sync customer ' . $order->get_billing_email() . ': ' . $result['message'], 'error');
            return false;
        }
    }


    /**
     * Prepare order data for ActiveTrail API
     *
     * @param WC_Order $order Order object.
     * @return array
     */
    private function prepare_order_data($order)
    {
        // Get items.
        $items = array();
        foreach ($order->get_items() as $item) {
            $product = $item->get_product();
            if ($product) {
                $items[] = array(
                    'product_id' => $product->get_id(),
                    'name' => $product->get_name(),
                    'quantity' => $item->get_quantity(),
                    'price' => $product->get_price(),
                    'total' => $item->get_total(),
                    'categories' => $this->get_product_categories($product),
                );
            }
        }

        $data = array(
            'order_id' => $order->get_id(),
            'order_number' => $order->get_order_number(),
            'status' => $order->get_status(),
            'total' => $order->get_total(),
            'subtotal' => $order->get_subtotal(),
            'tax_total' => $order->get_total_tax(),
            'shipping_total' => $order->get_shipping_total(),
            'currency' => $order->get_currency(),
            'date_created' => $order->get_date_created()->format('Y-m-d H:i:s'),
            'customer' => array(
                'email' => $order->get_billing_email(),
                'first_name' => $order->get_billing_first_name(),
                'last_name' => $order->get_billing_last_name(),
                'phone' => $order->get_billing_phone(),
            ),
            'billing_address' => array(
                'first_name' => $order->get_billing_first_name(),
                'last_name' => $order->get_billing_last_name(),
                'company' => $order->get_billing_company(),
                'address_1' => $order->get_billing_address_1(),
                'address_2' => $order->get_billing_address_2(),
                'city' => $order->get_billing_city(),
                'state' => $order->get_billing_state(),
                'postcode' => $order->get_billing_postcode(),
                'country' => $order->get_billing_country(),
                'email' => $order->get_billing_email(),
                'phone' => $order->get_billing_phone(),
            ),
            'shipping_address' => array(
                'first_name' => $order->get_shipping_first_name(),
                'last_name' => $order->get_shipping_last_name(),
                'company' => $order->get_shipping_company(),
                'address_1' => $order->get_shipping_address_1(),
                'address_2' => $order->get_shipping_address_2(),
                'city' => $order->get_shipping_city(),
                'state' => $order->get_shipping_state(),
                'postcode' => $order->get_shipping_postcode(),
                'country' => $order->get_shipping_country(),
            ),
            'items' => $items,
            'coupons' => $this->get_order_coupons($order),
        );

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

    /**
     * Prepare customer data for ActiveTrail API
     *
     * @param WC_Order $order Order object.
     * @return array
     */
    private function prepare_customer_data($order)
    {
        // Get settings from ActiveTrail_Settings class.
        $settings = ActiveTrail_Settings::get_all();

        // Check consent status to set subscriptionStatus (checkout OR registration consent).
        $subscriber_status = $this->order_has_consent($order);

        $data = array(
            'email' => $order->get_billing_email(),
            'sms' => $order->get_billing_phone(),
            'first_name' => $order->get_billing_first_name(),
            'last_name' => $order->get_billing_last_name(),
            'city' => $order->get_billing_city(),
            'street' => trim($order->get_billing_address_1() . ' ' . $order->get_billing_address_2()),
            'zip_code' => $order->get_billing_postcode(),
            'externalId' => activetrail_external_id_for_contact($order->get_customer_id(), $order->get_billing_email()),
            'externalName' => ACTIVETRAIL_EXTERNAL_NAME,
            'subscriptionStatus' => $subscriber_status
        );

        // Add group ID - use 'groupId' key to match what import_customer expects.
        if (!empty($settings['group_id'])) {
            $data['groupId'] = intval($settings['group_id']);
            $this->log('Set groupId in prepare_customer_data to: ' . $settings['group_id'], 'debug');
        } else {
            $this->log('No group_id configured in settings for prepare_customer_data', 'warning');
        }

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

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

    /**
     * Get product categories
     *
     * @param WC_Product|null $product Product object.
     * @return array
     */
    private function get_product_categories($product)
    {
        if (!$product) {
            return array();
        }

        $categories = array();
        $terms = get_the_terms($product->get_id(), 'product_cat');

        if ($terms && !is_wp_error($terms)) {
            foreach ($terms as $term) {
                $categories[] = $term->name;
            }
        }

        return $categories;
    }

    /**
     * Get order coupons
     *
     * @param WC_Order $order Order object.
     * @return array
     */
    private function get_order_coupons($order)
    {
        $coupons = array();

        foreach ($order->get_coupon_codes() as $coupon_code) {
            $coupon = new WC_Coupon($coupon_code);

            $coupons[] = array(
                'code' => $coupon_code,
                'discount' => $order->get_discount_total(),
                'type' => $coupon->get_discount_type(),
            );
        }

        return $coupons;
    }

    /**
     * Manually retry failed order sync
     *
     * @param int $order_id Order ID.
     * @return array Result
     */
    public function retry_order_sync($order_id)
    {
        $order = wc_get_order($order_id);
        if (!$order) {
            return array(
                'success' => false,
                'message' => 'Order not found'
            );
        }

        // Remove sync flag to force retry - HPOS compatible.
        $order->delete_meta_data('_activetrail_synced');
        $order->delete_meta_data('_activetrail_sync_date');
        $order->save();

        $this->log('Manually retrying order sync for order: ' . $order_id);

        $result = $this->sync_order($order);

        return array(
            'success' => $result,
            'message' => $result ? 'Order sync retried successfully' : 'Order sync retry failed'
        );
    }

    /**
     * Retry all failed orders
     *
     * @param int $limit Maximum number of orders to retry.
     * @return array Results
     */
    public function retry_all_failed_orders($limit = 50)
    {
        $results = array();
        $retried_count = 0;

        // Get orders that should be synced but haven't been.
        $orders = wc_get_orders(array(
            'limit' => $limit,
            'orderby' => 'date',
            'order' => 'DESC',
            'status' => array('processing', 'completed'),
            // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Meta query necessary for filtering synced orders
            'meta_query' => array(
                array(
                    'key' => '_activetrail_synced',
                    'compare' => 'NOT EXISTS'
                )
            )
        ));

        foreach ($orders as $order) {
            $order_id = $order->get_id();

            // Check if order should be synced.
            $current_status = $order->get_status();
            if (!$this->should_trigger_status_event($current_status)) {
                continue;
            }
            
            $settings = ActiveTrail_Settings::get_all();

            // Check consent if required (checkout OR registration consent).
            if ($settings['enable_checkout_consent'] ?? false) {
                if ( ! $this->order_has_consent($order) ) {
                    $results[] = array(
                        'order_id' => $order_id,
                        'success' => false,
                        'message' => 'No consent given'
                    );
                    continue;
                }
            }

            $result = $this->retry_order_sync($order_id);
            $results[] = array_merge(array('order_id' => $order_id), $result);

            if ($result['success']) {
                $retried_count++;
            }
        }

        $this->log("Retried $retried_count orders out of " . count($orders) . " total");

        return array(
            'total_orders' => count($orders),
            'retried_count' => $retried_count,
            'results' => $results
        );
    }

    /**
     * Ensure a guest customer's contact exists in ActiveTrail before syncing commerce data.
     *
     * @param WC_Order $order Order object.
     * @param array    $settings Plugin settings.
     * @return bool
     */
    private function ensure_guest_contact_synced($order, $settings)
    {
        if (!$order) {
            return false;
        }

        $needs_save = false;
        $customer_id = $order->get_customer_id();

        $stored_external_id = $order->get_meta('_activetrail_external_id', true);
        $billing_email = $order->get_billing_email();

        // CRITICAL: ALWAYS check if there's already a guest contact from cart/checkout.
        // Even if order meta already has an external ID, we should check for guest contact first.
        // If user registered during checkout, we MUST use the existing guest contact.
        // to avoid creating duplicate contacts and ensure order links to the correct contact.
        $existing_guest_contact = null;

        if (!empty($billing_email) && !empty($this->api_client)) {
            try {
                $existing_check = $this->api_client->get_customer_by_email($billing_email);
                if (!is_wp_error($existing_check) && !empty($existing_check['exists'])) {
                    // Check all contacts with this email - might be multiple.
                    $existing_contacts = is_array($existing_check['data']) && isset($existing_check['data'][0])
                        ? $existing_check['data']
                        : array($existing_check['data']);

                    foreach ($existing_contacts as $existing_contact) {
                        $existing_external_id = isset($existing_contact['externalId'])
                            ? $existing_contact['externalId']
                            : null;

                        // If existing contact is a guest contact (from cart/checkout), ALWAYS use it.
                        // Even if user registered during checkout, we want to keep using the guest contact.
                        if (!empty($existing_external_id) && strpos($existing_external_id, 'WC-CONTACT-GUEST-') === 0) {
                            $existing_guest_contact = $existing_external_id;
                            $this->log('Found existing guest contact from checkout: ' . $existing_guest_contact . ' - will use it to avoid duplicate (even if user registered)', 'info');
                            break; // Use first guest contact found
                        }
                    }
                }
            } catch (Exception $check_e) {
                // If check fails, continue with normal logic.
                $this->log('Could not check for existing guest contact: ' . $check_e->getMessage(), 'debug');
            }
        }

        // PRIORITY: If we found an existing guest contact, ALWAYS use it (override any stored ID).
        if (!empty($existing_guest_contact)) {
            if ($stored_external_id !== $existing_guest_contact) {
                $stored_external_id = $existing_guest_contact;
                $order->update_meta_data('_activetrail_external_id', $stored_external_id);
                $needs_save = true;
                $this->log('Overriding stored external_id with existing guest contact (user registered during checkout): ' . $stored_external_id, 'info');
            }
        } elseif (empty($stored_external_id)) {
            // No existing guest contact found, generate new external ID.
            if ($customer_id > 0) {
                // Customer is registered - use their registered external ID.
                if (function_exists('activetrail_external_id_for_contact')) {
                    $stored_external_id = activetrail_external_id_for_contact($customer_id, $billing_email);
                } else {
                    $stored_external_id = 'WC-CONTACT-' . $customer_id;
                }
                $this->log('Registered customer checkout - using registered external_id: ' . $stored_external_id . ' (customer_id=' . $customer_id . ')', 'info');
            } else {
                // True guest checkout - use guest external ID.
                $stored_external_id = $this->generate_guest_external_id($order);
                $this->log('Guest checkout - using guest external_id: ' . $stored_external_id, 'info');
            }

            if (!empty($stored_external_id)) {
                $order->update_meta_data('_activetrail_external_id', $stored_external_id);
                $needs_save = true;
            }
        }

        if ($needs_save) {
            $order->save();
            $needs_save = false;
        }

        // Skip if contact already marked as imported (but ensure meta persisted if created now).
        $guest_meta = $order->get_meta('_activetrail_guest_imported');
        if ( 'yes' === $guest_meta ) {
            return true;
        }

        // If we're using an existing guest contact (even if user registered), contact already exists.
        // No need to import them again - just mark as synced and return.
        if (!empty($existing_guest_contact)) {
            $this->log('Using existing guest contact (ID: ' . $existing_guest_contact . ') - already exists in ActiveTrail, skipping import', 'info');
            $order->update_meta_data('_activetrail_guest_imported', 'yes');
            $order->save();
            return true;
        }

        // If customer is registered (customer_id > 0) and no guest contact exists,.
        // they already exist in ActiveTrail from registration - no need to import again.
        if ($customer_id > 0) {
            $this->log('Registered customer (ID: ' . $customer_id . ') - already exists in ActiveTrail from registration, skipping import', 'info');
            $order->update_meta_data('_activetrail_guest_imported', 'yes');
            $order->save();
            return true;
        }

        // Ensure we have a group to assign guests to.
        if (empty($settings['group_id'])) {
            $this->log('Guest contact sync skipped: no group_id configured', 'warning');
            return false;
        }

        // CRITICAL FIX: Build customer data manually WITHOUT using prepare_customer_data.
        // to avoid recalculating the external ID from customer_id.
        $subscriber_status = $this->order_has_consent($order);

        $customer_data = array(
            'email' => $order->get_billing_email(),
            'sms' => $order->get_billing_phone(),
            'first_name' => $order->get_billing_first_name(),
            'last_name' => $order->get_billing_last_name(),
            'city' => $order->get_billing_city(),
            'street' => trim($order->get_billing_address_1() . ' ' . $order->get_billing_address_2()),
            'zip_code' => $order->get_billing_postcode(),
            'externalId' => $stored_external_id,  // CRITICAL: Use the stored external ID, not recalculated!
            'externalName' => ACTIVETRAIL_EXTERNAL_NAME,
            'subscriptionStatus' => $subscriber_status,
            'groupId' => intval($settings['group_id'])
        );

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

        $this->log('Importing guest contact to ActiveTrail before order sync: ' . $order->get_id(), 'info');
        $this->log('Using external_id: ' . $stored_external_id . ' (keeping guest identity, customer_id=' . $customer_id . ')', 'debug');

        $result = $this->api_client->import_customer($customer_data);

        if (!empty($result['success'])) {
            $order->update_meta_data('_activetrail_external_id', $stored_external_id);
            $order->update_meta_data('_activetrail_guest_imported', 'yes');
            $order->save();

            // Allow ActiveTrail a brief moment to process the import before the commerce call.
            usleep(300000); // 0.3 seconds

            $this->log('Guest contact imported successfully for order: ' . $order->get_id(), 'info');
            return true;
        }

        $message = isset($result['message']) ? $result['message'] : 'Unknown error';
        $this->log('Failed to import guest contact before order sync: ' . $order->get_id() . ' - ' . $message, 'warning');

        return false;
    }

    /**
     * Check if an order is already syncing in the current request.
     *
     * @param int $order_id
     * @return bool
     */
    private function is_order_currently_syncing($order_id)
    {
        return isset($this->orders_currently_syncing[$order_id]);
    }

    /**
     * Lock an order sync to avoid duplicate submissions.
     *
     * @param int $order_id
     */
    private function lock_order_sync($order_id)
    {
        $this->orders_currently_syncing[$order_id] = microtime(true);
    }

    /**
     * Release the sync lock for an order.
     *
     * @param int $order_id
     */
    private function unlock_order_sync($order_id)
    {
        unset($this->orders_currently_syncing[$order_id]);
    }

    /**
     * Generate a stable external ID for guest orders based on their email.
     *
     * @param WC_Order $order
     * @return string
     */
    private function generate_guest_external_id($order)
    {
        if (!$order) {
            return '';
        }

        $email = $order->get_billing_email();

        if (function_exists('activetrail_external_id_for_contact')) {
            return activetrail_external_id_for_contact(0, $email);
        }

        if (!empty($email)) {
            return 'WC-CONTACT-GUEST-' . md5(strtolower(trim($email)));
        }

        return 'WC-CONTACT-GUEST-' . $order->get_id();
    }

    /**
     * Log message
     *
     * @param string $message Log message.
     * @param string $level Log level.
     * @param array  $data Optional data to log.
     */
    private function log($message, $level = 'info', $data = array())
    {
        // Use enhanced logger if available.
        if (class_exists('ActiveTrail_Logger')) {
            $logger = ActiveTrail_Logger::get_instance();
            $logger->log($message, $level, 'ORDER', $data);
        } else {
            // Fallback - no logging if logger unavailable.
        }
    }
}
