<?php
/**
 * Teleship Webhook Receiver
 *
 * Handles incoming webhooks from Teleship for classification updates,
 * tracking updates, label generation, and other events.
 *
 * @package Teleship
 * @since 1.1.0
 */

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

/**
 * Class Teleship_Webhook_Receiver
 *
 * Receives and processes webhooks from Teleship platform.
 * Webhook endpoint: /wp-json/teleship/v1/webhook
 */
class Teleship_Webhook_Receiver {

    /**
     * The namespace for REST API routes
     *
     * @var string
     */
    private $namespace = 'teleship/v1';

    /**
     * Webhook secret for signature verification
     *
     * @var string
     */
    private $webhook_secret;

    /**
     * Carrier name to WC tracking code mapping
     *
     * @var array
     */
    private static $carrier_map = array(
        'ups'         => 'ups',
        'usps'        => 'usps',
        'fedex'       => 'fedex',
        'dhl'         => 'dhl',
        'dhl express' => 'dhl',
        'royal mail'  => 'royal_mail',
        'royalmail'   => 'royal_mail',
        'canada post' => 'canada_post',
        'canadapost'  => 'canada_post',
        'dpd'         => 'dpd',
        'hermes'      => 'hermes',
        'evri'        => 'evri',
        'yodel'       => 'yodel',
        'tnt'         => 'tnt',
        'aramex'      => 'aramex',
        'sf express'  => 'sf_express',
        'australia post' => 'australia_post',
    );

    /**
     * Initialize webhook receiver
     */
    public function __construct() {
        // Get webhook secret from OAuth manager (v2.0+) or legacy option
        if (function_exists('teleship_oauth')) {
            $this->webhook_secret = teleship_oauth()->get_webhook_secret();
        }
        // Fall back to legacy option if OAuth secret not set
        if (empty($this->webhook_secret)) {
            $this->webhook_secret = get_option('teleship_webhook_secret', '');
        }

        add_action('rest_api_init', array($this, 'register_webhook_endpoint'));
    }

    /**
     * Register the webhook endpoint
     */
    public function register_webhook_endpoint() {
        // Main webhook endpoint
        register_rest_route($this->namespace, '/webhook', array(
            'methods'             => WP_REST_Server::CREATABLE,
            'callback'            => array($this, 'handle_webhook'),
            'permission_callback' => array($this, 'verify_webhook_signature'),
        ));

        // Classification webhook endpoint (specific)
        register_rest_route($this->namespace, '/webhook/classification', array(
            'methods'             => WP_REST_Server::CREATABLE,
            'callback'            => array($this, 'handle_classification_webhook'),
            'permission_callback' => array($this, 'verify_webhook_signature'),
        ));

        // Tracking webhook endpoint (specific)
        register_rest_route($this->namespace, '/webhook/tracking', array(
            'methods'             => WP_REST_Server::CREATABLE,
            'callback'            => array($this, 'handle_tracking_webhook'),
            'permission_callback' => array($this, 'verify_webhook_signature'),
        ));
    }

    /**
     * Verify webhook signature
     *
     * @param WP_REST_Request $request Full data about the request.
     * @return bool|WP_Error True if valid, WP_Error otherwise.
     */
    public function verify_webhook_signature($request) {
        // Get the signature from header
        $signature = $request->get_header('X-Teleship-Signature');

        // If no webhook secret configured, skip verification but log warning
        if (empty($this->webhook_secret)) {
            teleship_log('Warning: Webhook received without signature verification (no secret configured)');
            return true;
        }

        // Signature is required if secret is configured
        if (empty($signature)) {
            return new WP_Error(
                'missing_signature',
                esc_html__('Missing webhook signature', 'teleship-global-shipping'),
                array('status' => 401)
            );
        }

        // Verify signature
        $payload = $request->get_body();
        $expected_signature = hash_hmac('sha256', $payload, $this->webhook_secret);

        if (!hash_equals($expected_signature, $signature)) {
            teleship_log('Webhook signature verification failed');
            return new WP_Error(
                'invalid_signature',
                esc_html__('Invalid webhook signature', 'teleship-global-shipping'),
                array('status' => 401)
            );
        }

        return true;
    }

    /**
     * Handle incoming webhook
     *
     * @param WP_REST_Request $request Full data about the request.
     * @return WP_REST_Response|WP_Error Response object on success, or WP_Error on failure.
     */
    public function handle_webhook($request) {
        $body = $request->get_json_params();

        if (empty($body)) {
            return new WP_Error(
                'invalid_payload',
                esc_html__('Invalid webhook payload', 'teleship-global-shipping'),
                array('status' => 400)
            );
        }

        // Get webhook type from payload (eventName is the standard field) or header fallback
        $webhook_type = isset($body['eventName']) ? sanitize_text_field($body['eventName']) : '';
        if (empty($webhook_type)) {
            $webhook_type = isset($body['type']) ? sanitize_text_field($body['type']) : '';
        }
        $webhook_type = $webhook_type ?: $request->get_header('X-Teleship-Event');

        teleship_log('Received webhook: ' . $webhook_type);

        // Route to appropriate handler
        switch ($webhook_type) {
            case 'classification.completed':
            case 'product.classified':
            case 'product.classification.complete':
                return $this->handle_classification_webhook($request);

            case 'tracking.updated':
            case 'shipment.updated':
                return $this->handle_tracking_webhook($request);

            case 'product.updated':
                return $this->handle_product_update_webhook($request);

            case 'order.shipped':
                return $this->handle_order_shipped_webhook($request);

            case 'label.generated':
            case 'shipment.created':
                return $this->handle_label_generated($request);

            default:
                teleship_log('Unknown webhook type: ' . $webhook_type);
                return rest_ensure_response(array(
                    'success' => true,
                    'message' => 'Webhook received but not processed (unknown type)',
                ));
        }
    }

    /**
     * Handle classification webhook
     *
     * Updates WooCommerce products with classification data from Teleship.
     *
     * @param WP_REST_Request $request Full data about the request.
     * @return WP_REST_Response|WP_Error Response object on success, or WP_Error on failure.
     */
    public function handle_classification_webhook($request) {
        $body = $request->get_json_params();

        // Webhook payload structure: { eventName, objectType, objectId, data: { ... } }
        $data = isset($body['data']) ? $body['data'] : $body;

        // Support both single product and batch formats
        $products = isset($data['products']) ? $data['products'] : array($data);
        $updated  = 0;
        $errors   = array();

        foreach ($products as $product_data) {
            $result = $this->update_product_classification($product_data);

            if (is_wp_error($result)) {
                $errors[] = array(
                    'productId' => isset($product_data['productId']) ? $product_data['productId'] : 'unknown',
                    'error'     => $result->get_error_message(),
                );
            } else {
                $updated++;
            }
        }

        teleship_log('Classification webhook processed: ' . $updated . ' products updated');

        return rest_ensure_response(array(
            'success' => empty($errors),
            'updated' => $updated,
            'errors'  => $errors,
        ));
    }

    /**
     * Handle tracking webhook
     *
     * Updates WooCommerce orders with tracking information from Teleship.
     *
     * @param WP_REST_Request $request Full data about the request.
     * @return WP_REST_Response|WP_Error Response object on success, or WP_Error on failure.
     */
    public function handle_tracking_webhook($request) {
        $body = $request->get_json_params();

        // Webhook payload structure: { eventName, objectType, objectId, data: { ... } }
        $data = isset($body['data']) ? $body['data'] : $body;

        $order_id        = isset($data['customerReference']) ? absint($data['customerReference']) : 0;
        $tracking_number = isset($data['trackingNumber']) ? sanitize_text_field($data['trackingNumber']) : '';
        $tracking_url    = isset($data['trackingURL']) ? esc_url_raw($data['trackingURL']) : (isset($data['trackingUrl']) ? esc_url_raw($data['trackingUrl']) : '');
        $carrier         = isset($data['firstMile']['carrier']) ? sanitize_text_field($data['firstMile']['carrier']) : '';
        $status          = isset($data['status']) ? sanitize_text_field($data['status']) : '';

        // If shipment was cancelled/voided, handle as cancellation instead of tracking update
        if (in_array($status, array('cancelled', 'voided'), true)) {
            return $this->handle_shipment_cancellation($request);
        }

        if (!$order_id) {
            return new WP_Error(
                'missing_order_id',
                esc_html__('Order ID is required', 'teleship-global-shipping'),
                array('status' => 400)
            );
        }

        $order = wc_get_order($order_id);
        if (!$order) {
            return new WP_Error(
                'order_not_found',
                esc_html__('Order not found', 'teleship-global-shipping'),
                array('status' => 404)
            );
        }

        // Update tracking meta (using standardized keys)
        if (!empty($tracking_number)) {
            $order->update_meta_data('_teleship_tracking_number', $tracking_number);
        }
        if (!empty($tracking_url)) {
            $order->update_meta_data('_teleship_tracking_url', $tracking_url);
        }
        if (!empty($carrier)) {
            $order->update_meta_data('_teleship_tracking_carrier', $carrier);
        }
        if (!empty($status)) {
            $order->update_meta_data('_teleship_tracking_status', $status);
        }

        $order->update_meta_data('_teleship_tracking_updated', current_time('mysql'));
        $order->save();

        // Optionally update order status based on tracking status
        $this->maybe_update_order_status($order, $status);

        teleship_log('Tracking webhook processed for order: ' . $order_id);

        return rest_ensure_response(array(
            'success' => true,
            'orderId' => $order_id,
        ));
    }

    /**
     * Handle product update webhook
     *
     * @param WP_REST_Request $request Full data about the request.
     * @return WP_REST_Response Response object.
     */
    public function handle_product_update_webhook($request) {
        // This is similar to classification but might include more fields
        return $this->handle_classification_webhook($request);
    }

    /**
     * Handle order shipped webhook (legacy)
     *
     * @param WP_REST_Request $request Full data about the request.
     * @return WP_REST_Response Response object.
     */
    public function handle_order_shipped_webhook($request) {
        $body = $request->get_json_params();

        // Webhook payload structure: { eventName, objectType, objectId, data: { ... } }
        $data = isset($body['data']) ? $body['data'] : $body;

        $order_id      = isset($data['customerReference']) ? absint($data['customerReference']) : 0;
        $shipment_id   = isset($data['shipmentId']) ? sanitize_text_field($data['shipmentId']) : '';
        $shipped_date  = isset($data['shippedAt']) ? sanitize_text_field($data['shippedAt']) : current_time('mysql');

        if (!$order_id) {
            return new WP_Error(
                'missing_order_id',
                esc_html__('Order ID is required', 'teleship-global-shipping'),
                array('status' => 400)
            );
        }

        $order = wc_get_order($order_id);
        if (!$order) {
            return new WP_Error(
                'order_not_found',
                esc_html__('Order not found', 'teleship-global-shipping'),
                array('status' => 404)
            );
        }

        // Update shipment meta
        $order->update_meta_data('_teleship_shipment_id', $shipment_id);
        $order->update_meta_data('_teleship_shipped_date', $shipped_date);

        // Store Teleship's consolidated tracking as primary
        if (isset($data['trackingNumber'])) {
            $order->update_meta_data('_teleship_tracking_number', sanitize_text_field($data['trackingNumber']));
        }
        if (isset($data['trackingURL'])) {
            $order->update_meta_data('_teleship_tracking_url', esc_url_raw($data['trackingURL']));
        } elseif (isset($data['trackingUrl'])) {
            $order->update_meta_data('_teleship_tracking_url', esc_url_raw($data['trackingUrl']));
        }
        // Carrier from firstMile for display
        if (isset($data['firstMile']['carrier'])) {
            $order->update_meta_data('_teleship_tracking_carrier', sanitize_text_field($data['firstMile']['carrier']));
        }

        $order->save();

        // Set loop prevention flag before updating status
        $order->update_meta_data(Teleship_Order_Sync::META_WEBHOOK_PROCESSING, 'yes');
        $order->save();

        try {
            // Mark as fulfilled and update order status to completed
            if ($order->get_status() !== 'completed') {
                $order->update_status('completed', __('Order shipped via Teleship', 'teleship-global-shipping'));
            }
        } finally {
            // Clear flag
            $order->delete_meta_data(Teleship_Order_Sync::META_WEBHOOK_PROCESSING);
            $order->save();
        }

        teleship_log('Order shipped webhook processed for order: ' . $order_id);

        return rest_ensure_response(array(
            'success' => true,
            'orderId' => $order_id,
        ));
    }

    /**
     * Handle label.generated / shipment.created webhook
     *
     * Full fulfillment handler: stores tracking, applies partial fulfillment,
     * integrates with WC Shipment Tracking plugin, completes order.
     * Mirrors Magento's ShipmentCreator behavior.
     *
     * @param WP_REST_Request $request Full data about the request.
     * @return WP_REST_Response|WP_Error Response object.
     */
    public function handle_label_generated($request) {
        $body = $request->get_json_params();

        // Webhook payload structure: { eventName, objectType, objectId, data: { ... } }
        // All shipment details are nested under 'data'
        $data = isset($body['data']) ? $body['data'] : $body;

        // Order ID comes from customerReference (WC order number stored during sync)
        $order_id        = isset($data['customerReference']) ? absint($data['customerReference']) : 0;
        $shipment_id     = isset($data['shipmentId']) ? sanitize_text_field($data['shipmentId']) : '';
        // Teleship's consolidated tracking number and URL are the primary values
        $tracking_number = isset($data['trackingNumber']) ? sanitize_text_field($data['trackingNumber']) : '';
        // API sends trackingURL (uppercase), handle both casings
        $tracking_url    = isset($data['trackingURL']) ? esc_url_raw($data['trackingURL']) : (isset($data['trackingUrl']) ? esc_url_raw($data['trackingUrl']) : '');
        // Carrier from firstMile for display purposes
        $carrier         = isset($data['firstMile']['carrier']) ? sanitize_text_field($data['firstMile']['carrier']) : 'Teleship';
        $webhook_items   = isset($data['items']) && is_array($data['items']) ? $data['items'] : array();

        if (!$order_id) {
            return new WP_Error(
                'missing_order_id',
                esc_html__('Order ID is required', 'teleship-global-shipping'),
                array('status' => 400)
            );
        }

        $order = wc_get_order($order_id);
        if (!$order) {
            return new WP_Error(
                'order_not_found',
                esc_html__('Order not found', 'teleship-global-shipping'),
                array('status' => 404)
            );
        }

        teleship_log("[Webhook] Processing label.generated for order {$order_id}");

        // Set loop prevention flag
        $order->update_meta_data(Teleship_Order_Sync::META_WEBHOOK_PROCESSING, 'yes');
        $order->save();

        try {
            // Store Teleship tracking meta (primary tracking info for customer emails)
            $order->update_meta_data('_teleship_tracking_number', $tracking_number);
            $order->update_meta_data('_teleship_tracking_url', $tracking_url);
            $order->update_meta_data('_teleship_tracking_carrier', $carrier);
            $order->update_meta_data('_teleship_shipment_id', $shipment_id);
            $order->update_meta_data('_teleship_shipped_date', current_time('mysql'));

            // Store firstMile carrier details as supplementary info
            if (isset($data['firstMile'])) {
                $order->update_meta_data('_teleship_firstmile_carrier', sanitize_text_field($data['firstMile']['carrier'] ?? ''));
                $order->update_meta_data('_teleship_firstmile_tracking', sanitize_text_field($data['firstMile']['trackingNumber'] ?? ''));
                $order->update_meta_data('_teleship_firstmile_tracking_url', esc_url_raw($data['firstMile']['trackingUrl'] ?? ''));
            }

            // Apply partial fulfillment if items are provided
            if (!empty($webhook_items)) {
                $this->apply_partial_fulfillment($order, $webhook_items);
            }

            // Mark as fulfilled by Teleship (adds order note + saves)
            $order_sync = teleship_order_sync();
            $order_sync->mark_as_fulfilled($order, $tracking_number, $tracking_url, $carrier, $shipment_id);

            // Integrate with WC Shipment Tracking plugin if available
            $this->add_wc_shipment_tracking($order, $tracking_number, $tracking_url, $carrier);

            // Calculate overall fulfillment status
            $fulfillment_status = Teleship_Fulfillment_Calculator::calculate($order);

            // Only complete the order if fully fulfilled
            if ($fulfillment_status === Teleship_Fulfillment_Calculator::STATUS_FULFILLED) {
                if ($order->get_status() !== 'completed') {
                    $order->update_status('completed', __('Order fully fulfilled via Teleship', 'teleship-global-shipping'));
                }
            } else {
                // Partially fulfilled - add note but don't complete
                $order->add_order_note(
                    sprintf(
                        /* translators: %s: fulfillment status of the shipment */
                        __('Teleship partial shipment created. Status: %s', 'teleship-global-shipping'),
                        $fulfillment_status
                    ),
                    false,
                    true
                );
                $order->save();
            }

            teleship_log("[Webhook] label.generated processed for order {$order_id}, status: {$fulfillment_status}");

        } finally {
            // Always clear the webhook processing flag
            $order->delete_meta_data(Teleship_Order_Sync::META_WEBHOOK_PROCESSING);
            $order->save();
        }

        return rest_ensure_response(array(
            'success' => true,
            'orderId' => $order_id,
        ));
    }

    /**
     * Handle shipment cancellation webhook
     *
     * Resets fulfillment state when a shipment is cancelled in Teleship.
     *
     * @param WP_REST_Request $request Full data about the request.
     * @return WP_REST_Response|WP_Error Response object.
     */
    private function handle_shipment_cancellation($request) {
        $body = $request->get_json_params();
        $data = isset($body['data']) ? $body['data'] : $body;

        $order_id    = isset($data['customerReference']) ? absint($data['customerReference']) : 0;
        $shipment_id = isset($data['shipmentId']) ? sanitize_text_field($data['shipmentId']) : '';

        if (!$order_id) {
            return new WP_Error(
                'missing_order_id',
                esc_html__('Order ID is required', 'teleship-global-shipping'),
                array('status' => 400)
            );
        }

        $order = wc_get_order($order_id);
        if (!$order) {
            return new WP_Error(
                'order_not_found',
                esc_html__('Order not found', 'teleship-global-shipping'),
                array('status' => 404)
            );
        }

        teleship_log("[Webhook] Processing shipment cancellation for order {$order_id}, shipment {$shipment_id}");

        // Set loop prevention flag
        $order->update_meta_data(Teleship_Order_Sync::META_WEBHOOK_PROCESSING, 'yes');
        $order->save();

        try {
            // Reset per-item shipped quantities
            foreach ($order->get_items() as $item_id => $item) {
                Teleship_Fulfillment_Calculator::update_shipped_quantity($item_id, 0);
            }

            // Clear fulfillment metadata
            $order->delete_meta_data(Teleship_Order_Sync::META_TELESHIP_FULFILLED);
            $order->delete_meta_data(Teleship_Order_Sync::META_SHIPMENT_ID);
            $order->delete_meta_data(Teleship_Order_Sync::META_TRACKING_NUMBER);
            $order->delete_meta_data(Teleship_Order_Sync::META_TRACKING_URL);
            $order->delete_meta_data(Teleship_Order_Sync::META_CARRIER);
            $order->delete_meta_data('_teleship_shipped_date');
            $order->delete_meta_data('_teleship_firstmile_carrier');
            $order->delete_meta_data('_teleship_firstmile_tracking');
            $order->delete_meta_data('_teleship_firstmile_tracking_url');
            $order->save();

            // Revert order status to processing if completed
            if ($order->get_status() === 'completed') {
                $order->update_status(
                    'processing',
                    sprintf(
                        /* translators: %s: shipment ID from Teleship */
                        __('Shipment %s cancelled via Teleship. Order reverted to processing.', 'teleship-global-shipping'),
                        $shipment_id
                    )
                );
            } else {
                $order->add_order_note(
                    sprintf(
                        /* translators: %s: shipment ID from Teleship */
                        __('Shipment %s cancelled via Teleship.', 'teleship-global-shipping'),
                        $shipment_id
                    ),
                    false,
                    true
                );
                $order->save();
            }

            teleship_log("[Webhook] Shipment cancellation processed for order {$order_id}");

        } finally {
            // Always clear the webhook processing flag
            $order->delete_meta_data(Teleship_Order_Sync::META_WEBHOOK_PROCESSING);
            $order->save();
        }

        return rest_ensure_response(array(
            'success' => true,
            'orderId' => $order_id,
            'message' => 'Shipment cancellation processed',
        ));
    }

    /**
     * Apply partial fulfillment from webhook items
     *
     * Matches webhook items to order items by SKU (primary) or title (fallback),
     * then updates per-item _qty_shipped meta.
     * Mirrors Magento's ShipmentCreator.addItemsBySkuMatch().
     *
     * @param WC_Order $order         Order object.
     * @param array    $webhook_items Items from webhook (sku, title, quantity).
     */
    private function apply_partial_fulfillment($order, $webhook_items) {
        // Build lookup maps (SKU and title) from webhook items
        $items_by_sku   = array();
        $items_by_title = array();

        foreach ($webhook_items as $item) {
            $sku      = isset($item['sku']) ? $item['sku'] : null;
            $title    = isset($item['title']) ? $item['title'] : null;
            $quantity = isset($item['quantity']) ? (int) $item['quantity'] : 0;

            if ($sku) {
                if (isset($items_by_sku[$sku])) {
                    $items_by_sku[$sku] += $quantity;
                } else {
                    $items_by_sku[$sku] = $quantity;
                }
            }
            if ($title) {
                if (isset($items_by_title[$title])) {
                    $items_by_title[$title] += $quantity;
                } else {
                    $items_by_title[$title] = $quantity;
                }
            }
        }

        $matched = 0;

        foreach ($order->get_items() as $item_id => $item) {
            $product = $item->get_product();
            if (!$product) {
                continue;
            }

            $order_sku  = $product->get_sku();
            $order_name = $item->get_name();
            $qty_available = $item->get_quantity();
            $shipped_qty = 0;

            // Match by SKU first
            if ($order_sku && isset($items_by_sku[$order_sku]) && $items_by_sku[$order_sku] > 0) {
                $shipped_qty = min($items_by_sku[$order_sku], $qty_available);
                $items_by_sku[$order_sku] -= $shipped_qty;
            }
            // Fallback: match by title
            elseif ($order_name && isset($items_by_title[$order_name]) && $items_by_title[$order_name] > 0) {
                $shipped_qty = min($items_by_title[$order_name], $qty_available);
                $items_by_title[$order_name] -= $shipped_qty;
            }

            if ($shipped_qty > 0) {
                // Get existing shipped qty and add new
                $existing_shipped = (float) wc_get_order_item_meta($item_id, Teleship_Fulfillment_Calculator::META_QTY_SHIPPED, true);
                $new_total = min($qty_available, $existing_shipped + $shipped_qty); // Clamp to available

                Teleship_Fulfillment_Calculator::update_shipped_quantity($item_id, $new_total);
                $matched++;

                teleship_log("[Webhook] Matched item '{$order_name}' (SKU: {$order_sku}): shipped {$shipped_qty}, total now {$new_total}");
            }
        }

        teleship_log("[Webhook] Partial fulfillment: matched {$matched} items");
    }

    /**
     * Add tracking to WC Shipment Tracking plugin if available
     *
     * @param WC_Order $order           Order object.
     * @param string   $tracking_number Tracking number.
     * @param string   $tracking_url    Tracking URL.
     * @param string   $carrier         Carrier name.
     */
    private function add_wc_shipment_tracking($order, $tracking_number, $tracking_url, $carrier) {
        // Check if WC Shipment Tracking plugin is active
        if (!class_exists('WC_Shipment_Tracking_Actions')) {
            return;
        }

        try {
            $st = WC_Shipment_Tracking_Actions::get_instance();
            $wc_carrier = self::map_carrier($carrier);

            $st->add_tracking_item($order->get_id(), array(
                'tracking_provider'        => $wc_carrier,
                'custom_tracking_provider' => $carrier,
                'custom_tracking_link'     => $tracking_url,
                'tracking_number'          => $tracking_number,
                'date_shipped'             => current_time('Y-m-d'),
            ));

            teleship_log("[Webhook] Added tracking to WC Shipment Tracking plugin for order {$order->get_id()}");
        } catch (Exception $e) {
            teleship_log("[Webhook] Failed to add WC Shipment Tracking: " . $e->getMessage());
        }
    }

    /**
     * Map carrier name to WooCommerce tracking code
     *
     * @param string $carrier Carrier name from Teleship.
     * @return string WC tracking carrier code.
     */
    public static function map_carrier($carrier) {
        if (empty($carrier)) {
            return 'teleship_tracking';
        }

        $normalized = strtolower(trim($carrier));

        // Check exact match in map
        if (isset(self::$carrier_map[$normalized])) {
            return self::$carrier_map[$normalized];
        }

        // Check partial matches
        foreach (self::$carrier_map as $key => $code) {
            if (strpos($normalized, $key) !== false) {
                return $code;
            }
        }

        // Default
        return 'teleship_tracking';
    }

    /**
     * Update product classification from webhook data
     *
     * @param array $product_data Product data from webhook.
     * @return bool|WP_Error True on success, WP_Error on failure.
     */
    private function update_product_classification($product_data) {
        $product_id = isset($product_data['productId']) ? $product_data['productId'] : null;
        $variant_id = isset($product_data['variantId']) ? $product_data['variantId'] : null;

        // Determine target ID - prefer variant_id, fallback to product_id
        $target_id = !empty($variant_id) ? $variant_id : $product_id;

        if (empty($target_id)) {
            return new WP_Error('missing_id', 'Product ID or Variant ID is required');
        }

        $product = wc_get_product($target_id);

        if (!$product) {
            // Try to find by product ID if variant ID didn't work
            if (!empty($product_id) && $target_id !== $product_id) {
                $product = wc_get_product($product_id);
            }

            if (!$product) {
                return new WP_Error('product_not_found', 'Product not found: ' . $target_id);
            }
        }

        // Update Teleship meta data
        if (isset($product_data['hsCode'])) {
            $product->update_meta_data('_teleship_hs_code', sanitize_text_field($product_data['hsCode']));
        }

        if (isset($product_data['countryOfOrigin'])) {
            $product->update_meta_data('_teleship_country_of_origin', sanitize_text_field($product_data['countryOfOrigin']));
        }

        if (isset($product_data['classification'])) {
            $product->update_meta_data('_teleship_classification', $product_data['classification']);
        }

        // Check for defaultPackageId in metadata (preferred) or root level (legacy)
        $default_package_id = null;
        if (isset($product_data['metadata']['defaultPackageId'])) {
            $default_package_id = $product_data['metadata']['defaultPackageId'];
        } elseif (isset($product_data['defaultPackageId'])) {
            $default_package_id = $product_data['defaultPackageId'];
        }
        if ($default_package_id) {
            $product->update_meta_data('_teleship_default_package_id', sanitize_text_field($default_package_id));
        }

        if (isset($product_data['contentHash'])) {
            $product->update_meta_data('_teleship_content_hash', sanitize_text_field($product_data['contentHash']));
        }

        // Store restrictions if provided
        if (isset($product_data['restrictions'])) {
            $product->update_meta_data('_teleship_restrictions', $product_data['restrictions']);
        }

        // Timestamp the update
        $product->update_meta_data('_teleship_classification_updated', current_time('mysql'));

        Teleship_Product_Sync::suspend_sync();
        try {
            $product->save();
        } finally {
            Teleship_Product_Sync::resume_sync();
        }

        return true;
    }

    /**
     * Maybe update order status based on tracking status
     *
     * @param WC_Order $order  The order to update.
     * @param string   $status Tracking status.
     */
    private function maybe_update_order_status($order, $status) {
        $status_mapping = array(
            'delivered'   => 'completed',
            'in_transit'  => 'processing', // Or a custom status
            'out_for_delivery' => 'processing',
            'exception'   => 'processing', // Keep as processing, admin should review
            'returned'    => 'cancelled', // Or a custom status
        );

        $status = strtolower($status);

        if (isset($status_mapping[$status])) {
            $target_status = $status_mapping[$status];
            $current_status = $order->get_status();

            // Only update if the status is changing and it makes sense
            if ($current_status !== $target_status) {
                // Don't downgrade from completed
                if ($current_status === 'completed' && $target_status !== 'completed') {
                    return;
                }

                // Set loop prevention flag
                $order->update_meta_data(Teleship_Order_Sync::META_WEBHOOK_PROCESSING, 'yes');
                $order->save();

                try {
                    $order->update_status(
                        $target_status,
                        /* translators: %s: tracking status from Teleship */
                        sprintf(__('Status updated via Teleship tracking: %s', 'teleship-global-shipping'), $status)
                    );
                } finally {
                    $order->delete_meta_data(Teleship_Order_Sync::META_WEBHOOK_PROCESSING);
                    $order->save();
                }
            }
        }
    }
}

// Initialize webhook receiver
new Teleship_Webhook_Receiver();
