<?php
/**
 * Teleship REST API Controller
 *
 * Exposes REST API endpoints for products that can be called from the embedded portal.
 * These endpoints bypass CloudFlare restrictions by being called directly from the same origin.
 *
 * @package Teleship
 * @since 1.1.0
 */

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

/**
 * Class Teleship_REST_API
 *
 * Handles REST API endpoints for Teleship integration.
 * Endpoints are available at /wp-json/teleship/v1/
 */
class Teleship_REST_API {

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

    /**
     * Initialize the REST API
     */
    public function __construct() {
        add_action('rest_api_init', array($this, 'register_routes'));
        add_action('rest_api_init', array($this, 'add_cors_headers'));
    }

    /**
     * Add CORS headers for cross-origin requests from the embedded portal.
     *
     * The portal runs on a different domain (e.g., dev.teleship.com:3004)
     * and is embedded via iframe in WP admin. It needs to call this REST API
     * directly for product data, so we allow cross-origin requests from
     * the configured app URL.
     */
    public function add_cors_headers() {
        $app_url = teleship_get_app_url();
        $origin = isset($_SERVER['HTTP_ORIGIN']) ? esc_url_raw(wp_unslash($_SERVER['HTTP_ORIGIN'])) : '';

        // Only allow CORS from the configured Teleship app URL
        if (!empty($origin) && !empty($app_url) && strpos($origin, wp_parse_url($app_url, PHP_URL_HOST)) !== false) {
            header('Access-Control-Allow-Origin: ' . $origin);
            header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
            header('Access-Control-Allow-Headers: Authorization, Content-Type, X-WP-Nonce');
            header('Access-Control-Allow-Credentials: true');
            header('Access-Control-Max-Age: 3600');
        }

        // Handle preflight OPTIONS requests
        if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
            status_header(200);
            exit;
        }
    }

    /**
     * Register REST API routes
     */
    public function register_routes() {
        // GET /products - List products with pagination
        register_rest_route($this->namespace, '/products', array(
            array(
                'methods'             => WP_REST_Server::READABLE,
                'callback'            => array($this, 'get_products'),
                'permission_callback' => array($this, 'check_permission'),
                'args'                => $this->get_products_args(),
            ),
            // POST /products - Create or update a product
            array(
                'methods'             => WP_REST_Server::CREATABLE,
                'callback'            => array($this, 'create_product'),
                'permission_callback' => array($this, 'check_permission'),
                'args'                => $this->get_create_product_args(),
            ),
        ));

        // GET/PUT/DELETE /products/{id}
        register_rest_route($this->namespace, '/products/(?P<id>[\d]+)', array(
            array(
                'methods'             => WP_REST_Server::READABLE,
                'callback'            => array($this, 'get_product'),
                'permission_callback' => array($this, 'check_permission'),
            ),
            array(
                'methods'             => WP_REST_Server::EDITABLE,
                'callback'            => array($this, 'update_product'),
                'permission_callback' => array($this, 'check_permission'),
                'args'                => $this->get_update_product_args(),
            ),
            array(
                'methods'             => WP_REST_Server::DELETABLE,
                'callback'            => array($this, 'delete_product'),
                'permission_callback' => array($this, 'check_permission'),
            ),
        ));

        // POST /products/sync - Trigger a full product sync
        register_rest_route($this->namespace, '/products/sync', array(
            'methods'             => WP_REST_Server::CREATABLE,
            'callback'            => array($this, 'sync_products'),
            'permission_callback' => array($this, 'check_permission'),
            'args'                => array(
                'product_ids' => array(
                    'required'    => false,
                    'type'        => 'array',
                    'description' => 'Optional array of product IDs to sync. If not provided, syncs all products.',
                ),
            ),
        ));

        // POST /products/bulk - Bulk update products (for classification results)
        register_rest_route($this->namespace, '/products/bulk', array(
            'methods'             => WP_REST_Server::CREATABLE,
            'callback'            => array($this, 'bulk_update_products'),
            'permission_callback' => array($this, 'check_permission'),
            'args'                => array(
                'products' => array(
                    'required'    => true,
                    'type'        => 'array',
                    'description' => 'Array of products to update with classification data.',
                ),
            ),
        ));

        // POST /products/{id}/classify - Classify a product via Teleship AI
        register_rest_route($this->namespace, '/products/(?P<id>[\d]+)/classify', array(
            'methods'             => WP_REST_Server::CREATABLE,
            'callback'            => array($this, 'classify_product'),
            'permission_callback' => array($this, 'check_permission'),
        ));

        // POST /webhook/classification - Receive classification webhooks from Teleship
        register_rest_route($this->namespace, '/webhook/classification', array(
            'methods'             => WP_REST_Server::CREATABLE,
            'callback'            => array($this, 'receive_webhook'),
            'permission_callback' => '__return_true', // Webhook auth via signature
        ));

        // GET /debug/order-mappings - View order sync mapping table (admin only)
        register_rest_route($this->namespace, '/debug/order-mappings', array(
            'methods'             => WP_REST_Server::READABLE,
            'callback'            => array($this, 'get_order_mappings'),
            'permission_callback' => function () {
                return current_user_can('manage_woocommerce');
            },
        ));
    }

    /**
     * Check if the request has permission to access the endpoints.
     *
     * Supports two authentication methods:
     * 1. Bearer JWT token (for embedded portal cross-origin requests)
     *    - Verifies token against Teleship API
     *    - Confirms accountId matches this store's integration
     *    - Caches verification for 5 minutes to reduce API calls
     * 2. WordPress cookie auth (for WP admin same-origin AJAX requests)
     *    - Falls back to current_user_can('manage_woocommerce')
     *
     * @param WP_REST_Request $request Full data about the request.
     * @return bool|WP_Error True if authorized, WP_Error otherwise.
     */
    public function check_permission($request) {
        // Method 1: Bearer token authentication (embedded portal)
        $auth_header = $request->get_header('Authorization');
        if (!empty($auth_header) && strpos($auth_header, 'Bearer ') === 0) {
            return $this->verify_bearer_token(substr($auth_header, 7));
        }

        // Method 2: WordPress cookie authentication (WP admin AJAX)
        if (current_user_can('manage_woocommerce')) {
            return true;
        }

        return new WP_Error(
            'rest_forbidden',
            esc_html__('You do not have permission to access this resource.', 'teleship-global-shipping'),
            array('status' => 403)
        );
    }

    /**
     * Verify a Bearer token from the embedded Teleship portal.
     *
     * Calls Teleship API to validate the token and confirms the accountId
     * matches this store's integration. Results are cached for 5 minutes
     * using WP transients to avoid hitting Teleship API on every request.
     *
     * @param string $token The Bearer token to verify.
     * @return bool|WP_Error True if valid, WP_Error otherwise.
     */
    private function verify_bearer_token($token) {
        if (empty($token)) {
            return new WP_Error(
                'rest_forbidden',
                esc_html__('Invalid authorization token.', 'teleship-global-shipping'),
                array('status' => 401)
            );
        }

        // Check if store is connected (has accountId to verify against)
        $expected_account_id = teleship_oauth()->get_account_id();
        if (empty($expected_account_id)) {
            return new WP_Error(
                'rest_forbidden',
                esc_html__('Store is not connected to Teleship.', 'teleship-global-shipping'),
                array('status' => 403)
            );
        }

        // Check transient cache to avoid repeated API calls
        $cache_key = 'teleship_token_' . substr(hash('sha256', $token), 0, 16);
        $cached = get_transient($cache_key);

        if ($cached === 'valid') {
            return true;
        }
        if ($cached === 'invalid') {
            return new WP_Error(
                'rest_forbidden',
                esc_html__('Invalid or expired authorization token.', 'teleship-global-shipping'),
                array('status' => 401)
            );
        }

        // Verify token against Teleship API by requesting the expected account
        // If the token has access to this account, the API returns 200
        $api_url = teleship_get_api_url() . '/api/accounts/' . $expected_account_id;

        $response = wp_remote_get($api_url, array(
            'timeout' => 15,
            'headers' => array(
                'Authorization' => 'Bearer ' . $token,
                'Content-Type'  => 'application/json',
            ),
            'sslverify' => false,
        ));

        if (is_wp_error($response)) {
            teleship_log('[REST API] Token verification failed: ' . $response->get_error_message());
            return new WP_Error(
                'rest_forbidden',
                esc_html__('Failed to verify authorization token.', 'teleship-global-shipping'),
                array('status' => 500)
            );
        }

        $http_code = wp_remote_retrieve_response_code($response);

        if ($http_code !== 200) {
            set_transient($cache_key, 'invalid', 60); // Cache invalid for 1 minute
            return new WP_Error(
                'rest_forbidden',
                esc_html__('Invalid or expired authorization token.', 'teleship-global-shipping'),
                array('status' => 401)
            );
        }

        $body = json_decode(wp_remote_retrieve_body($response), true);
        $token_account_id = isset($body['id']) ? $body['id'] : null;

        // Verify the returned account ID matches (defense in depth)
        if ($token_account_id !== $expected_account_id) {
            teleship_log('[REST API] Token account mismatch: expected ' . $expected_account_id . ', got ' . $token_account_id);
            set_transient($cache_key, 'invalid', 60);
            return new WP_Error(
                'rest_forbidden',
                esc_html__('Token not authorized for this store.', 'teleship-global-shipping'),
                array('status' => 403)
            );
        }

        // Cache valid result for 5 minutes
        set_transient($cache_key, 'valid', 5 * MINUTE_IN_SECONDS);

        return true;
    }

    /**
     * Get arguments for the get_products endpoint
     *
     * @return array Arguments for the endpoint.
     */
    private function get_products_args() {
        return array(
            'page' => array(
                'required'          => false,
                'default'           => 1,
                'type'              => 'integer',
                'sanitize_callback' => 'absint',
                'description'       => 'Current page of the collection.',
            ),
            'per_page' => array(
                'required'          => false,
                'default'           => 20,
                'type'              => 'integer',
                'sanitize_callback' => 'absint',
                'description'       => 'Maximum number of items to be returned in result set.',
            ),
            'search' => array(
                'required'          => false,
                'type'              => 'string',
                'sanitize_callback' => 'sanitize_text_field',
                'description'       => 'Limit results to those matching a string.',
            ),
            'product_ids' => array(
                'required'          => false,
                'type'              => 'array',
                'description'       => 'Limit to specific product IDs.',
            ),
            'variant_ids' => array(
                'required'          => false,
                'type'              => 'array',
                'description'       => 'Limit to specific variant IDs.',
            ),
            'has_hs_code' => array(
                'required'          => false,
                'type'              => 'boolean',
                'description'       => 'Filter by products that have HS code.',
            ),
        );
    }

    /**
     * Get arguments for the create_product endpoint
     *
     * @return array Arguments for the endpoint.
     */
    private function get_create_product_args() {
        return array(
            'product_id' => array(
                'required'          => true,
                'type'              => 'integer',
                'sanitize_callback' => 'absint',
                'description'       => 'WooCommerce product ID.',
            ),
            'variant_id' => array(
                'required'          => false,
                'type'              => 'integer',
                'sanitize_callback' => 'absint',
                'description'       => 'WooCommerce variation ID (for variable products).',
            ),
            'hs_code' => array(
                'required'          => false,
                'type'              => 'string',
                'sanitize_callback' => 'sanitize_text_field',
                'description'       => 'HS Code for the product.',
            ),
            'country_of_origin' => array(
                'required'          => false,
                'type'              => 'string',
                'sanitize_callback' => 'sanitize_text_field',
                'description'       => 'Country of origin (ISO 2-letter code).',
            ),
            'classification' => array(
                'required'          => false,
                'type'              => 'object',
                'description'       => 'Classification data including regional HS codes.',
            ),
        );
    }

    /**
     * Get arguments for the update_product endpoint
     *
     * @return array Arguments for the endpoint.
     */
    private function get_update_product_args() {
        return array(
            'hs_code' => array(
                'required'          => false,
                'type'              => 'string',
                'sanitize_callback' => 'sanitize_text_field',
                'description'       => 'HS Code for the product.',
            ),
            'country_of_origin' => array(
                'required'          => false,
                'type'              => 'string',
                'sanitize_callback' => 'sanitize_text_field',
                'description'       => 'Country of origin (ISO 2-letter code).',
            ),
            'classification' => array(
                'required'          => false,
                'type'              => 'object',
                'description'       => 'Classification data including regional HS codes.',
            ),
            'default_package_id' => array(
                'required'          => false,
                'type'              => 'string',
                'sanitize_callback' => 'sanitize_text_field',
                'description'       => 'Default package configuration ID.',
            ),
        );
    }

    /**
     * Get products with optional filtering and pagination
     *
     * @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 get_products($request) {
        $page       = $request->get_param('page');
        $per_page   = min($request->get_param('per_page'), 100); // Max 100 per page
        $search     = $request->get_param('search');
        $product_ids = $request->get_param('product_ids');
        $variant_ids = $request->get_param('variant_ids');
        $has_hs_code = $request->get_param('has_hs_code');

        // Build WooCommerce product query args
        $args = array(
            'status'  => 'publish',
            'limit'   => $per_page,
            'page'    => $page,
            'orderby' => 'date',
            'order'   => 'DESC',
        );

        // Add search filter
        if (!empty($search)) {
            $args['s'] = $search;
        }

        // Add specific product IDs filter
        if (!empty($product_ids)) {
            $args['include'] = array_map('absint', $product_ids);
        }

        // Get products
        $products = wc_get_products($args);
        $total    = wc_get_products(array_merge($args, array('limit' => -1, 'return' => 'ids')));
        $total_count = count($total);

        // Format products for response
        $formatted_products = array();
        foreach ($products as $product) {
            // For variable products, include variations
            if ($product->is_type('variable')) {
                $variations = $product->get_children();

                // If variant_ids filter is provided, only include matching variations
                if (!empty($variant_ids)) {
                    $variations = array_intersect($variations, array_map('absint', $variant_ids));
                }

                foreach ($variations as $variation_id) {
                    $variation = wc_get_product($variation_id);
                    if ($variation) {
                        $formatted = $this->format_product_for_response($variation, $product->get_id());

                        // Filter by has_hs_code if specified
                        if ($has_hs_code !== null) {
                            $has_code = !empty($formatted['hsCode']);
                            if ($has_hs_code !== $has_code) {
                                continue;
                            }
                        }

                        $formatted_products[] = $formatted;
                    }
                }
            } else {
                // Simple product
                $formatted = $this->format_product_for_response($product);

                // Filter by has_hs_code if specified
                if ($has_hs_code !== null) {
                    $has_code = !empty($formatted['hsCode']);
                    if ($has_hs_code !== $has_code) {
                        continue;
                    }
                }

                $formatted_products[] = $formatted;
            }
        }

        $response = rest_ensure_response($formatted_products);

        // Add pagination headers
        $response->header('X-Total-Count', $total_count);
        $response->header('X-WP-Total', $total_count);
        $response->header('X-WP-TotalPages', ceil($total_count / $per_page));

        return $response;
    }

    /**
     * Get a single product
     *
     * @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 get_product($request) {
        $product_id = $request->get_param('id');
        $product    = wc_get_product($product_id);

        if (!$product) {
            return new WP_Error(
                'product_not_found',
                esc_html__('Product not found.', 'teleship-global-shipping'),
                array('status' => 404)
            );
        }

        return rest_ensure_response($this->format_product_for_response($product));
    }

    /**
     * Create or update a product with Teleship classification data
     *
     * @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 create_product($request) {
        $product_id = $request->get_param('product_id');
        $variant_id = $request->get_param('variant_id');

        // Determine which ID to use
        $target_id = !empty($variant_id) ? $variant_id : $product_id;
        $product   = wc_get_product($target_id);

        if (!$product) {
            return new WP_Error(
                'product_not_found',
                esc_html__('Product not found.', 'teleship-global-shipping'),
                array('status' => 404)
            );
        }

        // Update product with Teleship data
        return $this->update_product_data($product, $request);
    }

    /**
     * Update a product with Teleship classification data
     *
     * @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 update_product($request) {
        $product_id = $request->get_param('id');
        $product    = wc_get_product($product_id);

        if (!$product) {
            return new WP_Error(
                'product_not_found',
                esc_html__('Product not found.', 'teleship-global-shipping'),
                array('status' => 404)
            );
        }

        return $this->update_product_data($product, $request);
    }

    /**
     * Delete Teleship data from a product
     *
     * @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 delete_product($request) {
        $product_id = $request->get_param('id');
        $product    = wc_get_product($product_id);

        if (!$product) {
            return new WP_Error(
                'product_not_found',
                esc_html__('Product not found.', 'teleship-global-shipping'),
                array('status' => 404)
            );
        }

        // Clear Teleship meta data
        $product->delete_meta_data('_teleship_hs_code');
        $product->delete_meta_data('_teleship_country_of_origin');
        $product->delete_meta_data('_teleship_classification');
        $product->delete_meta_data('_teleship_default_package_id');
        $product->delete_meta_data('_teleship_content_hash');
        Teleship_Product_Sync::suspend_sync();
        try {
            $product->save();
        } finally {
            Teleship_Product_Sync::resume_sync();
        }

        return rest_ensure_response(array(
            'success' => true,
            'message' => esc_html__('Teleship data cleared from product.', 'teleship-global-shipping'),
        ));
    }

    /**
     * Sync products to/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 sync_products($request) {
        $product_ids = $request->get_param('product_ids');

        // Get products to sync
        $args = array(
            'status' => 'publish',
            'limit'  => -1,
        );

        if (!empty($product_ids)) {
            $args['include'] = array_map('absint', $product_ids);
        }

        $products = wc_get_products($args);
        $synced   = array();
        $errors   = array();

        foreach ($products as $product) {
            try {
                // For variable products, sync each variation
                if ($product->is_type('variable')) {
                    foreach ($product->get_children() as $variation_id) {
                        $variation = wc_get_product($variation_id);
                        if ($variation) {
                            $result = $this->sync_single_product($variation, $product->get_id());
                            if (is_wp_error($result)) {
                                $errors[] = array(
                                    'id'    => $variation_id,
                                    'error' => $result->get_error_message(),
                                );
                            } else {
                                $synced[] = $result;
                            }
                        }
                    }
                } else {
                    $result = $this->sync_single_product($product);
                    if (is_wp_error($result)) {
                        $errors[] = array(
                            'id'    => $product->get_id(),
                            'error' => $result->get_error_message(),
                        );
                    } else {
                        $synced[] = $result;
                    }
                }
            } catch (Exception $e) {
                $errors[] = array(
                    'id'    => $product->get_id(),
                    'error' => $e->getMessage(),
                );
            }
        }

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

    /**
     * Bulk update products with classification data (webhook receiver)
     *
     * @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 bulk_update_products($request) {
        $products = $request->get_param('products');
        $updated  = array();
        $errors   = array();

        foreach ($products as $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
            $target_id = !empty($variant_id) ? $variant_id : $product_id;

            if (empty($target_id)) {
                $errors[] = array(
                    'productId' => $product_id,
                    'error'     => 'Missing product or variant ID',
                );
                continue;
            }

            $product = wc_get_product($target_id);

            if (!$product) {
                $errors[] = array(
                    'productId' => $product_id,
                    'variantId' => $variant_id,
                    'error'     => 'Product not found',
                );
                continue;
            }

            // 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']);
            }

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

            // 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']));
            }

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

            $updated[] = array(
                'productId' => $product_id,
                'variantId' => $variant_id,
                'id'        => $product->get_id(),
            );
        }

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

    /**
     * Receive classification webhook from Teleship API.
     *
     * Handles both "type" (current) and "event" (legacy) payload key names.
     * The payload contains a products array with classification results.
     *
     * @param WP_REST_Request $request Full data about the request.
     * @return WP_REST_Response|WP_Error Response object.
     */
    public function receive_webhook($request) {
        $body = $request->get_json_params();

        // Support both "type" (current) and "event" (legacy) key names
        $event_name = isset($body['type']) ? $body['type'] : (isset($body['event']) ? $body['event'] : '');

        if (!in_array($event_name, array('product.classification.complete', 'product.updated'), true)) {
            return new WP_Error(
                'unknown_event',
                /* translators: %s: webhook event name */
                sprintf(esc_html__('Unknown webhook event: %s', 'teleship-global-shipping'), $event_name),
                array('status' => 400)
            );
        }

        // Products array can be at top level (current) or nested in data (legacy)
        $products = isset($body['products']) ? $body['products'] : array();
        if (empty($products) && isset($body['data']['products'])) {
            $products = $body['data']['products'];
        }

        // If no products array, try flat single-product format (legacy)
        if (empty($products) && isset($body['data']['productId'])) {
            $products = array($body['data']);
        }

        if (empty($products)) {
            return new WP_Error(
                'no_products',
                esc_html__('No products in webhook payload.', 'teleship-global-shipping'),
                array('status' => 400)
            );
        }

        $updated = array();
        $errors  = array();

        foreach ($products as $product_data) {
            $product_id = isset($product_data['productId']) ? $product_data['productId'] : null;
            $variant_id = isset($product_data['variantId']) ? $product_data['variantId'] : null;
            $target_id  = !empty($variant_id) ? $variant_id : $product_id;

            if (empty($target_id)) {
                $errors[] = array('error' => 'Missing product ID');
                continue;
            }

            $product = wc_get_product($target_id);
            if (!$product) {
                $errors[] = array('productId' => $target_id, 'error' => 'Product not found');
                continue;
            }

            if (!empty($product_data['hsCode'])) {
                $product->update_meta_data('_teleship_hs_code', sanitize_text_field($product_data['hsCode']));
            }
            if (!empty($product_data['countryOfOrigin'])) {
                $product->update_meta_data('_teleship_country_of_origin', sanitize_text_field($product_data['countryOfOrigin']));
            }
            if (!empty($product_data['classification'])) {
                $product->update_meta_data('_teleship_classification', $product_data['classification']);
            }
            if (!empty($product_data['restrictions'])) {
                $product->update_meta_data('_teleship_restrictions', $product_data['restrictions']);
            }
            if (!empty($product_data['contentHash'])) {
                $product->update_meta_data('_teleship_content_hash', sanitize_text_field($product_data['contentHash']));
            }

            Teleship_Product_Sync::suspend_sync();
            try {
                $product->save();
            } finally {
                Teleship_Product_Sync::resume_sync();
            }
            $updated[] = array('productId' => $target_id);
        }

        teleship_log('[REST API] Webhook received: ' . $event_name . ' - updated ' . count($updated) . ' products');

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

    /**
     * Classify a product via Teleship AI
     *
     * Syncs the product to Teleship first (to ensure it exists), then triggers
     * classification on the Teleship API. Updates local WC meta with the results.
     *
     * @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 classify_product($request) {
        $product_id = $request->get_param('id');
        $product    = wc_get_product($product_id);

        if (!$product) {
            return new WP_Error(
                'product_not_found',
                esc_html__('Product not found.', 'teleship-global-shipping'),
                array('status' => 404)
            );
        }

        $api_url    = teleship_get_api_url();
        $account_id = get_option('teleship_account_id');

        if (empty($account_id)) {
            return new WP_Error(
                'no_account',
                esc_html__('Teleship account not configured.', 'teleship-global-shipping'),
                array('status' => 400)
            );
        }

        $auth_header = teleship_oauth()->get_authorization_header();
        if (empty($auth_header)) {
            return new WP_Error(
                'no_auth',
                esc_html__('No authentication token available. Please reconnect Teleship.', 'teleship-global-shipping'),
                array('status' => 401)
            );
        }

        // Step 1: Resolve the Teleship UUID (search first → upsert fallback)
        $parent_id = $product->get_parent_id();
        $teleship_uuid = $this->resolve_teleship_uuid($product, $parent_id ?: null);

        if (is_wp_error($teleship_uuid)) {
            teleship_log('[REST API] Failed to resolve Teleship UUID: ' . $teleship_uuid->get_error_message());
            return new WP_Error(
                'sync_failed',
                esc_html__('Failed to sync product to Teleship.', 'teleship-global-shipping'),
                array('status' => 500)
            );
        }

        $headers = array(
            'Authorization' => $auth_header,
            'x-account-id'  => $account_id,
        );

        // Step 2: Call classify on the Teleship API (blocking — we need the result)
        // 90s timeout to exceed backend's 60s OpenAI timeout; max_retries=1 to avoid 276s worst case
        $classify_response = Teleship_HTTP_Client::post(
            $api_url . '/api/products/' . $teleship_uuid . '/classify',
            array(),
            array(
                'headers'     => $headers,
                'timeout'     => 90,
                'max_retries' => 1,
            )
        );

        if (is_wp_error($classify_response)) {
            teleship_log('[REST API] Classification failed: ' . $classify_response->get_error_message());
            return new WP_Error(
                'classify_failed',
                esc_html__('Failed to classify product.', 'teleship-global-shipping'),
                array('status' => 500)
            );
        }

        $classify_code = wp_remote_retrieve_response_code($classify_response);
        if ($classify_code < 200 || $classify_code >= 300) {
            $classify_body = wp_remote_retrieve_body($classify_response);
            teleship_log('[REST API] Classification returned HTTP ' . $classify_code . ': ' . $classify_body);
            return new WP_Error(
                'classify_failed',
                esc_html__('Classification request failed.', 'teleship-global-shipping'),
                array('status' => $classify_code)
            );
        }

        $classify_data = json_decode(wp_remote_retrieve_body($classify_response), true);
        $classification = isset($classify_data['classification']) ? $classify_data['classification'] : null;

        // Step 3: Save classification results to local WC product meta
        if (!empty($classification)) {
            $product->update_meta_data('_teleship_classification', $classification);

            // Extract HS code for the origin country (e.g. GB) from regionalCodes
            $origin_country = get_option('teleship_account_country', 'GB');
            $hs_code = null;

            // First try regionalCodes for the origin country match
            if (!empty($classification['regionalCodes']) && is_array($classification['regionalCodes'])) {
                foreach ($classification['regionalCodes'] as $code) {
                    if (isset($code['region']) && $code['region'] === $origin_country && !empty($code['hsCode'])) {
                        $hs_code = $code['hsCode'];
                        break;
                    }
                }
            }

            // Fallback to originCode, then originHSCode
            if (empty($hs_code) && !empty($classification['originCode']['hsCode'])) {
                $hs_code = $classification['originCode']['hsCode'];
            }
            if (empty($hs_code) && !empty($classification['originHSCode'])) {
                $hs_code = $classification['originHSCode'];
            }

            if (!empty($hs_code)) {
                $product->update_meta_data('_teleship_hs_code', sanitize_text_field($hs_code));
            }
        }

        if (!empty($classify_data['countryOfOrigin'])) {
            $product->update_meta_data('_teleship_country_of_origin', sanitize_text_field($classify_data['countryOfOrigin']));
        }
        if (!empty($classify_data['restrictions'])) {
            $product->update_meta_data('_teleship_restrictions', $classify_data['restrictions']);
        }
        if (!empty($classify_data['contentHash'])) {
            $product->update_meta_data('_teleship_content_hash', sanitize_text_field($classify_data['contentHash']));
        }
        Teleship_Product_Sync::suspend_sync();
        try {
            $product->save();
        } finally {
            Teleship_Product_Sync::resume_sync();
        }

        // Return the updated product with classification data
        return rest_ensure_response($this->format_product_for_response($product, $parent_id ?: null));
    }

    /**
     * Update product with Teleship data
     *
     * @param WC_Product      $product The product to update.
     * @param WP_REST_Request $request Full data about the request.
     * @return WP_REST_Response Response object.
     */
    private function update_product_data($product, $request) {
        // Update HS Code
        $hs_code = $request->get_param('hs_code');
        if ($hs_code !== null) {
            $product->update_meta_data('_teleship_hs_code', sanitize_text_field($hs_code));
        }

        // Update Country of Origin
        $coo = $request->get_param('country_of_origin');
        if ($coo !== null) {
            $product->update_meta_data('_teleship_country_of_origin', sanitize_text_field($coo));
        }

        // Update Classification data
        $classification = $request->get_param('classification');
        if ($classification !== null) {
            $product->update_meta_data('_teleship_classification', $classification);
        }

        // Update Restrictions
        $restrictions = $request->get_param('restrictions');
        if ($restrictions !== null) {
            $product->update_meta_data('_teleship_restrictions', $restrictions);
        }

        // Update Default Package ID
        $package_id = $request->get_param('default_package_id');
        if ($package_id !== null) {
            $product->update_meta_data('_teleship_default_package_id', sanitize_text_field($package_id));
        }

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

        // Return response immediately — sync to Teleship in background
        $parent_id = $product->get_parent_id();
        $response = rest_ensure_response($this->format_product_for_response($product, $parent_id ?: null));

        // Fire async sync to Teleship (non-blocking)
        $this->async_sync_product($product, $parent_id ?: null);

        return $response;
    }

    /**
     * Resolve the Teleship UUID for a WC product.
     *
     * Uses a 2-tier strategy to avoid the expensive full upsert:
     * Tier 1: Search via POST /api/products/search (fast, sub-50ms, no restrictions).
     * Tier 2: Fall back to POST /api/products full upsert (creates product if needed).
     *
     * @param WC_Product $product   The WC product.
     * @param int|null   $parent_id Parent product ID for variations.
     * @return string|WP_Error The Teleship UUID or WP_Error on failure.
     */
    private function resolve_teleship_uuid($product, $parent_id = null) {
        $api_url     = teleship_get_api_url();
        $account_id  = get_option('teleship_account_id');
        $auth_header = teleship_oauth()->get_authorization_header();

        if (empty($account_id) || empty($auth_header)) {
            return new WP_Error('no_auth', 'Teleship account not configured or no auth token.');
        }

        $headers = array(
            'Authorization' => $auth_header,
            'x-account-id'  => $account_id,
        );

        $variant_id = (string) $product->get_id();
        $shop       = get_site_url();

        // Tier 1: Search via POST /api/products/search (fast, no restrictions)
        $search_response = Teleship_HTTP_Client::post(
            $api_url . '/api/products/search',
            array(
                'variantIds' => array($variant_id),
                'shop'       => $shop,
                'source'     => 'woocommerce',
            ),
            array(
                'headers'     => $headers,
                'timeout'     => 10,
                'max_retries' => 1,
            )
        );

        if (!is_wp_error($search_response)) {
            $search_code = wp_remote_retrieve_response_code($search_response);
            if ($search_code >= 200 && $search_code < 300) {
                $search_body = json_decode(wp_remote_retrieve_body($search_response), true);
                if (!empty($search_body) && is_array($search_body) && !empty($search_body[0]['id'])) {
                    return $search_body[0]['id'];
                }
            }
        }

        // Tier 2: Full upsert via POST /api/products (creates product if it doesn't exist)
        $formatted = $this->format_product_for_response($product, $parent_id);
        $payload = array(
            'productId'       => (string) $formatted['productId'],
            'variantId'       => (string) $formatted['variantId'],
            'title'           => $formatted['title'],
            'sku'             => $formatted['sku'],
            'category'        => $formatted['category'],
            'hsCode'          => $formatted['hsCode'],
            'countryOfOrigin' => $formatted['countryOfOrigin'],
            'classification'  => $formatted['classification'],
            'restrictions'    => $formatted['restrictions'],
            'contentHash'     => $formatted['contentHash'],
            'imageUrl'        => $formatted['imageUrl'],
            'source'          => 'woocommerce',
            'shop'            => $shop,
            'metadata'        => array(
                'weight'           => $formatted['metadata']['weight'] ?? null,
                'weightUnit'       => $formatted['metadata']['weightUnit'] ?? null,
                'defaultPackageId' => $formatted['metadata']['defaultPackageId'] ?? null,
            ),
        );

        $sync_response = Teleship_HTTP_Client::post(
            $api_url . '/api/products',
            $payload,
            array(
                'headers' => $headers,
                'timeout' => 35,
            )
        );

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

        $sync_body = json_decode(wp_remote_retrieve_body($sync_response), true);
        $uuid = isset($sync_body['id']) ? $sync_body['id'] : null;

        if (empty($uuid)) {
            return new WP_Error('sync_no_uuid', 'Failed to get Teleship product ID.');
        }

        return $uuid;
    }

    /**
     * Sync a single product to Teleship
     *
     * @param WC_Product $product      The product to sync.
     * @param int|null   $parent_id    Parent product ID for variations.
     * @return array|WP_Error Synced product data or error.
     */
    private function sync_single_product($product, $parent_id = null) {
        $formatted = $this->format_product_for_response($product, $parent_id);

        // Get Teleship API URL and account credentials
        $api_url    = teleship_get_api_url();
        $account_id = get_option('teleship_account_id');

        if (empty($account_id)) {
            return new WP_Error('no_account', 'Teleship account not configured');
        }

        // Build the payload for Teleship API
        $payload = array(
            'productId'       => (string) $formatted['productId'],
            'variantId'       => (string) $formatted['variantId'],
            'title'           => $formatted['title'],
            'sku'             => $formatted['sku'],
            'category'        => $formatted['category'],
            'imageUrl'        => $formatted['imageUrl'],
            'hsCode'          => $formatted['hsCode'],
            'countryOfOrigin' => $formatted['countryOfOrigin'],
            'classification'  => $formatted['classification'],
            'restrictions'    => $formatted['restrictions'],
            'contentHash'     => $formatted['contentHash'],
            'source'          => 'woocommerce',
            'shop'            => get_site_url(),
            'metadata'        => array(
                'weight'           => $formatted['metadata']['weight'] ?? null,
                'weightUnit'       => $formatted['metadata']['weightUnit'] ?? null,
                'defaultPackageId' => $formatted['metadata']['defaultPackageId'] ?? null,
            ),
        );

        // Send to Teleship API
        $response = Teleship_HTTP_Client::post(
            $api_url . '/api/products',
            $payload,
            array(
                'headers' => array(
                    'x-account-id' => $account_id,
                ),
            )
        );

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

        return $formatted;
    }

    /**
     * Fire a non-blocking sync of a product to Teleship API.
     * Returns immediately — the HTTP request runs in the background.
     *
     * @param WC_Product $product   The product to sync.
     * @param int|null   $parent_id Parent product ID for variations.
     */
    private function async_sync_product($product, $parent_id = null) {
        $formatted  = $this->format_product_for_response($product, $parent_id);
        $api_url    = teleship_get_api_url();
        $account_id = get_option('teleship_account_id');

        if (empty($account_id)) {
            return;
        }

        $payload = array(
            'productId'       => (string) $formatted['productId'],
            'variantId'       => (string) $formatted['variantId'],
            'title'           => $formatted['title'],
            'sku'             => $formatted['sku'],
            'category'        => $formatted['category'],
            'hsCode'          => $formatted['hsCode'],
            'countryOfOrigin' => $formatted['countryOfOrigin'],
            'classification'  => $formatted['classification'],
            'restrictions'    => $formatted['restrictions'],
            'contentHash'     => $formatted['contentHash'],
            'imageUrl'        => $formatted['imageUrl'],
            'source'          => 'woocommerce',
            'shop'            => get_site_url(),
            'metadata'        => array(
                'weight'           => $formatted['metadata']['weight'] ?? null,
                'weightUnit'       => $formatted['metadata']['weightUnit'] ?? null,
                'defaultPackageId' => $formatted['metadata']['defaultPackageId'] ?? null,
            ),
        );

        $auth_header = teleship_oauth()->get_authorization_header();

        wp_remote_post(
            $api_url . '/api/products',
            array(
                'headers'  => array(
                    'Authorization' => $auth_header ?: '',
                    'x-account-id'  => $account_id,
                    'Content-Type'  => 'application/json',
                ),
                'body'     => wp_json_encode($payload),
                'blocking' => false,
                'timeout'  => 0.01,
            )
        );
    }

    /**
     * Format a WooCommerce product for API response
     *
     * @param WC_Product $product   The product to format.
     * @param int|null   $parent_id Parent product ID for variations.
     * @return array Formatted product data.
     */
    private function format_product_for_response($product, $parent_id = null) {
        $product_id = $parent_id ? $parent_id : $product->get_id();
        $variant_id = $product->is_type('variation') ? $product->get_id() : $product->get_id();

        // Get Teleship meta data
        $hs_code           = $product->get_meta('_teleship_hs_code');
        $country_of_origin = $product->get_meta('_teleship_country_of_origin');
        $classification    = $product->get_meta('_teleship_classification');
        $default_package   = $product->get_meta('_teleship_default_package_id');
        $content_hash      = $product->get_meta('_teleship_content_hash');

        // Get restrictions from Teleship-specific meta
        $restrictions_raw = $product->get_meta('_teleship_restrictions');
        $restrictions = null;
        if (!empty($restrictions_raw)) {
            $decoded = is_string($restrictions_raw) ? json_decode($restrictions_raw, true) : $restrictions_raw;
            if (is_array($decoded)) {
                $restrictions = $decoded;
            }
        }

        // Get category
        $categories = wp_get_post_terms($product_id, 'product_cat', array('fields' => 'names'));
        $category   = !empty($categories) && !is_wp_error($categories) ? implode(' > ', $categories) : '';

        // Build title (include variation attributes for variations)
        $title = $product->get_name();

        // Get product image URL
        $image_url = null;
        $image_id  = $product->get_image_id();
        if ($image_id) {
            $image_url = wp_get_attachment_image_url($image_id, 'medium');
        }
        // Fallback to parent image for variations
        if (empty($image_url) && $parent_id) {
            $parent_image_id = get_post_thumbnail_id($parent_id);
            if ($parent_image_id) {
                $image_url = wp_get_attachment_image_url($parent_image_id, 'medium');
            }
        }

        return array(
            'id'               => $product->get_id(),
            'productId'        => (string) $product_id,
            'variantId'        => (string) $variant_id,
            'title'            => $title,
            'sku'              => $product->get_sku(),
            'category'         => $category,
            'imageUrl'         => $image_url ?: null,
            'hsCode'           => $hs_code ?: null,
            'countryOfOrigin'  => $country_of_origin ?: null,
            'classification'   => $classification ?: null,
            'restrictions'     => $restrictions,
            'contentHash'      => $content_hash ?: null,
            'source'           => 'woocommerce',
            'shop'             => get_site_url(),
            'createdAt'        => $product->get_date_created() ? $product->get_date_created()->format('c') : null,
            'updatedAt'        => $product->get_date_modified() ? $product->get_date_modified()->format('c') : null,
            'metadata'         => array(
                'weight'           => $product->get_weight(),
                'weightUnit'       => get_option('woocommerce_weight_unit', 'kg'),
                'defaultPackageId' => $default_package ?: null,
                'type'             => $product->get_type(),
            ),
        );
    }

    /**
     * Get order sync mapping table data for debugging
     *
     * Returns all rows from the teleship_order_mapping table so we can see
     * exactly why orders failed during initial sync (error_message column).
     *
     * @param WP_REST_Request $request Full data about the request.
     * @return WP_REST_Response Response object.
     */
    public function get_order_mappings($request) {
        global $wpdb;

        $table_name = $wpdb->prefix . 'teleship_order_mapping';

        // Check if table exists
        $table_exists = $wpdb->get_var( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- One-time table existence check
            $wpdb->prepare("SHOW TABLES LIKE %s", $table_name)
        );

        if (!$table_exists) {
            return rest_ensure_response(array(
                'success' => false,
                'message' => 'Order mapping table does not exist. Initial sync may not have run yet.',
                'data'    => array(),
            ));
        }

        $mappings = $wpdb->get_results("SELECT * FROM {$table_name} ORDER BY created_at DESC LIMIT 100"); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Custom mapping table; table name is safe (wpdb prefix + hardcoded string)

        $summary = array(
            'total'  => count($mappings),
            'synced' => 0,
            'failed' => 0,
            'pending' => 0,
        );

        foreach ($mappings as $mapping) {
            switch ($mapping->sync_status) {
                case 'synced':
                    $summary['synced']++;
                    break;
                case 'failed':
                    $summary['failed']++;
                    break;
                default:
                    $summary['pending']++;
                    break;
            }
        }

        return rest_ensure_response(array(
            'success' => true,
            'summary' => $summary,
            'data'    => $mappings,
        ));
    }
}

// Initialize the REST API
new Teleship_REST_API();
