<?php
/**
 * Teleship Product Sync Handler
 *
 * Hooks into WooCommerce product lifecycle events to sync products with Teleship.
 * This ensures the Teleship Products table stays in sync with WooCommerce.
 *
 * @package Teleship
 * @since 1.1.0
 */

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

/**
 * Class Teleship_Product_Sync
 *
 * Handles automatic product synchronization between WooCommerce and Teleship.
 */
class Teleship_Product_Sync {

    /**
     * Queue of products to sync (used for batching)
     *
     * @var array
     */
    private $sync_queue = array();

    /**
     * Whether sync is enabled
     *
     * @var bool
     */
    private $sync_enabled = true;

    /**
     * Whether sync queueing is temporarily suspended.
     * Static so it works across all instances (the class is instantiated
     * both via `new` at the bottom of this file and via `instance()`).
     *
     * @var bool
     */
    private static $sync_suspended = false;

    /**
     * Temporarily suspend product sync queueing.
     * Used by REST API handlers to prevent shutdown-hook bulk sync
     * when the handler already syncs the product directly.
     */
    public static function suspend_sync() {
        self::$sync_suspended = true;
    }

    /**
     * Resume product sync queueing after suspension.
     */
    public static function resume_sync() {
        self::$sync_suspended = false;
    }

    /**
     * Initialize product sync hooks
     */
    public function __construct() {
        // Check if sync is enabled via option
        $this->sync_enabled = get_option('teleship_product_sync_enabled', true);

        if (!$this->sync_enabled) {
            return;
        }

        // Product create/update hooks
        add_action('woocommerce_new_product', array($this, 'on_product_created'), 10, 2);
        add_action('woocommerce_update_product', array($this, 'on_product_updated'), 10, 2);
        add_action('woocommerce_product_set_stock', array($this, 'on_stock_updated'), 10, 1);

        // Variation hooks
        add_action('woocommerce_new_product_variation', array($this, 'on_variation_created'), 10, 2);
        add_action('woocommerce_update_product_variation', array($this, 'on_variation_updated'), 10, 2);
        add_action('woocommerce_save_product_variation', array($this, 'on_variation_saved'), 10, 2);

        // Product delete hooks
        add_action('woocommerce_delete_product', array($this, 'on_product_deleted'), 10, 1);
        add_action('woocommerce_trash_product', array($this, 'on_product_trashed'), 10, 1);
        add_action('woocommerce_delete_product_variation', array($this, 'on_variation_deleted'), 10, 1);

        // Batch sync on shutdown to avoid multiple API calls
        add_action('shutdown', array($this, 'process_sync_queue'));

        // Admin AJAX handlers for manual sync
        add_action('wp_ajax_teleship_sync_product', array($this, 'ajax_sync_product'));
        add_action('wp_ajax_teleship_sync_all_products', array($this, 'ajax_sync_all_products'));
    }

    /**
     * Handle new product creation
     *
     * @param int        $product_id Product ID.
     * @param WC_Product $product    Product object.
     */
    public function on_product_created($product_id, $product = null) {
        if (!$product) {
            $product = wc_get_product($product_id);
        }

        if (!$product) {
            return;
        }

        // For variable products, we'll sync variations separately
        if ($product->is_type('variable')) {
            return;
        }

        $this->queue_product_sync($product_id, 'create');
    }

    /**
     * Handle product update
     *
     * @param int        $product_id Product ID.
     * @param WC_Product $product    Product object.
     */
    public function on_product_updated($product_id, $product = null) {
        if (!$product) {
            $product = wc_get_product($product_id);
        }

        if (!$product) {
            return;
        }

        // For variable products, we'll sync variations separately
        if ($product->is_type('variable')) {
            return;
        }

        $this->queue_product_sync($product_id, 'update');
    }

    /**
     * Handle stock update
     *
     * @param WC_Product $product Product object.
     */
    public function on_stock_updated($product) {
        if (!$product) {
            return;
        }

        // Stock updates don't need to sync to Teleship (only classification data matters)
        // But we might want to track inventory for some use cases in the future
    }

    /**
     * Handle new variation creation
     *
     * @param int        $variation_id Variation ID.
     * @param WC_Product $variation    Variation object.
     */
    public function on_variation_created($variation_id, $variation = null) {
        $this->queue_product_sync($variation_id, 'create');
    }

    /**
     * Handle variation update
     *
     * @param int        $variation_id Variation ID.
     * @param WC_Product $variation    Variation object.
     */
    public function on_variation_updated($variation_id, $variation = null) {
        $this->queue_product_sync($variation_id, 'update');
    }

    /**
     * Handle variation save (from product edit screen)
     *
     * @param int $variation_id Variation ID.
     * @param int $loop         Variation loop index.
     */
    public function on_variation_saved($variation_id, $loop) {
        $this->queue_product_sync($variation_id, 'update');
    }

    /**
     * Handle product deletion
     *
     * @param int $product_id Product ID.
     */
    public function on_product_deleted($product_id) {
        $this->queue_product_sync($product_id, 'delete');
    }

    /**
     * Handle product trash
     *
     * @param int $product_id Product ID.
     */
    public function on_product_trashed($product_id) {
        $this->queue_product_sync($product_id, 'delete');
    }

    /**
     * Handle variation deletion
     *
     * @param int $variation_id Variation ID.
     */
    public function on_variation_deleted($variation_id) {
        $this->queue_product_sync($variation_id, 'delete');
    }

    /**
     * Queue a product for sync
     *
     * @param int    $product_id Product ID.
     * @param string $action     Action type (create, update, delete).
     */
    private function queue_product_sync($product_id, $action) {
        // Skip queueing when suspended (REST API handlers sync directly)
        if (self::$sync_suspended) {
            return;
        }

        // Prevent duplicate entries
        $key = $product_id . '_' . $action;
        if (isset($this->sync_queue[$key])) {
            return;
        }

        $this->sync_queue[$key] = array(
            'product_id' => $product_id,
            'action'     => $action,
            'timestamp'  => time(),
        );
    }

    /**
     * Get singleton instance
     *
     * @return Teleship_Product_Sync
     */
    public static function instance() {
        static $instance = null;
        if (null === $instance) {
            $instance = new self();
        }
        return $instance;
    }

    /**
     * Process the sync queue (called on shutdown)
     */
    public function process_sync_queue() {
        if (empty($this->sync_queue)) {
            return;
        }

        // Check if connected to Teleship
        if (!teleship_oauth()->is_connected()) {
            teleship_log('Product sync skipped: Not connected to Teleship');
            return;
        }

        $api_url = teleship_get_api_url();

        // Group by action type for batch processing
        $creates_updates = array();
        $deletes         = array();

        foreach ($this->sync_queue as $item) {
            if ($item['action'] === 'delete') {
                $deletes[] = $item['product_id'];
            } else {
                $product = wc_get_product($item['product_id']);
                if ($product) {
                    $creates_updates[] = $this->format_product_for_sync($product);
                }
            }
        }

        // Batch create/update products
        if (!empty($creates_updates)) {
            $this->batch_sync_products($creates_updates, $api_url);
        }

        // Handle deletes
        if (!empty($deletes)) {
            $this->batch_delete_products($deletes, $api_url);
        }

        // Clear the queue
        $this->sync_queue = array();
    }

    /**
     * Batch sync products to Teleship
     *
     * @param array  $products Array of formatted product data.
     * @param string $api_url  Teleship API URL.
     */
    private function batch_sync_products($products, $api_url) {
        $auth_header = teleship_oauth()->get_authorization_header();
        if (empty($auth_header)) {
            teleship_log('Product sync skipped: No auth token available');
            return;
        }

        // Chunk into batches of 50
        $chunks = array_chunk($products, 50);

        foreach ($chunks as $chunk) {
            $response = Teleship_HTTP_Client::post(
                $api_url . '/api/products/bulk',
                array('products' => $chunk),
                array(
                    'headers' => array(
                        'Authorization' => $auth_header,
                    ),
                    'timeout' => 60,
                )
            );

            if (is_wp_error($response)) {
                teleship_log('Product sync error: ' . $response->get_error_message());
            } else {
                $body = json_decode(wp_remote_retrieve_body($response), true);
                teleship_log('Product sync completed: ' . count($chunk) . ' products');
            }
        }
    }

    /**
     * Batch delete products from Teleship
     *
     * @param array  $product_ids Array of product IDs to delete.
     * @param string $api_url     Teleship API URL.
     */
    private function batch_delete_products($product_ids, $api_url) {
        $auth_header = teleship_oauth()->get_authorization_header();
        if (empty($auth_header)) {
            teleship_log('Product delete skipped: No auth token available');
            return;
        }

        $shop = get_site_url();

        foreach ($product_ids as $product_id) {
            // Use the product search endpoint to find and delete
            $response = Teleship_HTTP_Client::delete(
                $api_url . '/api/products/by-external-id',
                array(
                    'headers' => array(
                        'Authorization' => $auth_header,
                    ),
                    'body' => array(
                        'productId' => (string) $product_id,
                        'shop'      => $shop,
                        'source'    => 'woocommerce',
                    ),
                )
            );

            if (is_wp_error($response)) {
                teleship_log('Product delete error for ID ' . $product_id . ': ' . $response->get_error_message());
            }
        }
    }

    /**
     * Format a WooCommerce product for Teleship sync
     *
     * @param WC_Product $product The product to format.
     * @return array Formatted product data.
     */
    private function format_product_for_sync($product) {
        $parent_id  = $product->is_type('variation') ? $product->get_parent_id() : $product->get_id();
        $parent     = $product->is_type('variation') ? wc_get_product($parent_id) : null;

        // Extract HS code from broad attribute search with variation→parent fallback
        $hs_code = self::extract_attribute($product, Teleship_Order_Normalizer::HS_CODE_ATTRIBUTES, $parent);

        // Extract COO from broad attribute search with country name→code normalization
        $raw_coo = self::extract_attribute($product, Teleship_Order_Normalizer::COUNTRY_OF_ORIGIN_ATTRIBUTES, $parent);
        $country_of_origin = $raw_coo ? Teleship_Country_Codes::normalize($raw_coo) : null;

        // Classification from Teleship-specific meta
        $classification  = $product->get_meta('_teleship_classification') ?: null;
        $default_package = $product->get_meta('_teleship_default_package_id');

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

        // Content hash must match backend: SHA256 of {category, classification, countryOfOrigin, hsCode} (sorted keys)
        $hash_data = wp_json_encode(array(
            'category'        => $category ?: null,
            'classification'  => $classification ?: null,
            'countryOfOrigin' => $country_of_origin ?: null,
            'hsCode'          => $hs_code ?: null,
        ));
        $content_hash = hash('sha256', $hash_data);

        // 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;
            }
        }

        return array(
            'productId'       => (string) $parent_id,
            'variantId'       => (string) $product->get_id(),
            'title'           => $product->get_name(),
            'sku'             => $product->get_sku(),
            'category'        => $category,
            'hsCode'          => $hs_code ?: null,
            'countryOfOrigin' => $country_of_origin ?: null,
            'classification'  => $classification,
            'restrictions'    => $restrictions,
            'contentHash'     => $content_hash,
            'source'          => 'woocommerce',
            'shop'            => get_site_url(),
            'metadata'        => array(
                'weight'           => $product->get_weight(),
                'weightUnit'       => get_option('woocommerce_weight_unit', 'kg'),
                'defaultPackageId' => $default_package ?: null,
            ),
        );
    }

    /**
     * Extract an attribute value from a product, searching multiple attribute names.
     * Falls back to parent product for variations.
     *
     * @param WC_Product      $product    The product.
     * @param array           $attributes List of attribute names to search.
     * @param WC_Product|null $parent     Parent product for variation fallback.
     * @return string|null The found value or null.
     */
    private static function extract_attribute($product, $attributes, $parent = null) {
        // Search product meta first
        foreach ($attributes as $attr) {
            $value = $product->get_meta($attr);
            if (!empty($value)) {
                return (string) $value;
            }

            // Also check WC product attributes
            $wc_attr = $product->get_attribute($attr);
            if (!empty($wc_attr)) {
                return (string) $wc_attr;
            }
        }

        // Fall back to parent product for variations
        if ($parent) {
            foreach ($attributes as $attr) {
                $value = $parent->get_meta($attr);
                if (!empty($value)) {
                    return (string) $value;
                }

                $wc_attr = $parent->get_attribute($attr);
                if (!empty($wc_attr)) {
                    return (string) $wc_attr;
                }
            }
        }

        return null;
    }

    /**
     * AJAX handler for syncing a single product
     */
    public function ajax_sync_product() {
        check_ajax_referer('teleship_sync_nonce', 'nonce');

        if (!current_user_can('manage_woocommerce')) {
            wp_send_json_error(array('message' => 'Permission denied'));
        }

        $product_id = isset($_POST['product_id']) ? absint($_POST['product_id']) : 0;

        if (!$product_id) {
            wp_send_json_error(array('message' => 'Invalid product ID'));
        }

        $product = wc_get_product($product_id);
        if (!$product) {
            wp_send_json_error(array('message' => 'Product not found'));
        }

        $account_id = get_option('teleship_account_id');
        if (empty($account_id)) {
            wp_send_json_error(array('message' => 'Teleship account not configured'));
        }

        $api_url = teleship_get_api_url();

        // Sync the product
        $products_to_sync = array();

        if ($product->is_type('variable')) {
            foreach ($product->get_children() as $variation_id) {
                $variation = wc_get_product($variation_id);
                if ($variation) {
                    $products_to_sync[] = $this->format_product_for_sync($variation);
                }
            }
        } else {
            $products_to_sync[] = $this->format_product_for_sync($product);
        }

        $response = Teleship_HTTP_Client::post(
            $api_url . '/api/products/bulk',
            array('products' => $products_to_sync),
            array(
                'headers' => array(
                    'x-account-id' => $account_id,
                ),
                'timeout' => 60,
            )
        );

        if (is_wp_error($response)) {
            wp_send_json_error(array('message' => $response->get_error_message()));
        }

        wp_send_json_success(array(
            'message' => sprintf('Synced %d product(s)', count($products_to_sync)),
        ));
    }

    /**
     * AJAX handler for syncing all products
     */
    public function ajax_sync_all_products() {
        check_ajax_referer('teleship_sync_nonce', 'nonce');

        if (!current_user_can('manage_woocommerce')) {
            wp_send_json_error(array('message' => 'Permission denied'));
        }

        $result = $this->bulk_sync_all_products();

        if ($result['success']) {
            wp_send_json_success($result);
        } else {
            wp_send_json_error($result);
        }
    }

    /**
     * Bulk sync all products (for AJAX and programmatic use)
     *
     * Uses pagination to avoid loading all products into memory at once.
     *
     * @return array Result with success, message, and count
     */
    public function bulk_sync_all_products() {
        if (!teleship_oauth()->is_connected()) {
            return array(
                'success' => false,
                'message' => 'Not connected to Teleship',
                'count'   => 0,
            );
        }

        $api_url    = teleship_get_api_url();
        $page       = 1;
        $page_size  = 100;
        $total_synced = 0;

        do {
            $products = wc_get_products(array(
                'status' => 'publish',
                'limit'  => $page_size,
                'page'   => $page,
            ));

            if (empty($products)) {
                break;
            }

            $products_to_sync = array();

            foreach ($products as $product) {
                if ($product->is_type('variable')) {
                    foreach ($product->get_children() as $variation_id) {
                        $variation = wc_get_product($variation_id);
                        if ($variation) {
                            $products_to_sync[] = $this->format_product_for_sync($variation);
                        }
                    }
                } else {
                    $products_to_sync[] = $this->format_product_for_sync($product);
                }
            }

            if (!empty($products_to_sync)) {
                $this->batch_sync_products($products_to_sync, $api_url);
                $total_synced += count($products_to_sync);
            }

            // Free memory before loading next page
            unset($products, $products_to_sync);

            $page++;
        } while (true);

        return array(
            'success' => true,
            'message' => sprintf('Synced %d product(s)', $total_synced),
            'count'   => $total_synced,
        );
    }

    /**
     * Check if product sync is enabled
     *
     * @return bool Whether sync is enabled.
     */
    public function is_enabled() {
        return $this->sync_enabled;
    }

    /**
     * Enable product sync
     */
    public function enable() {
        $this->sync_enabled = true;
        update_option('teleship_product_sync_enabled', true);
    }

    /**
     * Disable product sync
     */
    public function disable() {
        $this->sync_enabled = false;
        update_option('teleship_product_sync_enabled', false);
    }
}

// Initialize product sync
new Teleship_Product_Sync();
