<?php
/**
 * Teleship Order Normalizer
 *
 * Transforms WooCommerce orders to Teleship API format.
 * Mirrors Magento's OrderNormalizer with full HS code and
 * country of origin extraction from product attributes.
 *
 * @package Teleship
 * @since 2.0.0
 */

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

/**
 * Class Teleship_Order_Normalizer
 */
class Teleship_Order_Normalizer {

    /**
     * Possible HS code attribute codes to search
     * Checks WooCommerce product meta and custom fields
     */
    const HS_CODE_ATTRIBUTES = array(
        '_teleship_hs_code',
        'hs_code',
        'hscode',
        'tariff_code',
        'hts_code',
        'harmonized_code',
        'harmonised_code',
        'commodity_code',
        'shipperhq_hs_code',
        'harmonisation_code',
        'customs_tariff_number',
        'customs_code',
        'tariff_number',
        'hs_tariff',
        'hts_number',
        'customs_classification',
        'product_tariff_code',
        'export_code',
        'schedule_b',
        'cn_code',
    );

    /**
     * Possible country of origin attribute codes to search
     */
    const COUNTRY_OF_ORIGIN_ATTRIBUTES = array(
        '_teleship_country_of_origin',
        'country_of_manufacture',
        'country_of_origin',
        'origin_country',
        'countryorigin',
        'coo',
        'made_in',
        'manufacture_country',
        'shipperhq_country_of_origin',
        'customs_country_of_origin',
        'manufactured_in',
        'produced_in',
        'place_of_origin',
        'export_country',
    );

    /**
     * Supported destination countries for order sync
     * Must match the backend supportedCountries list in utils.ts
     *
     * @var array
     */
    const SUPPORTED_DESTINATION_COUNTRIES = array(
        'AE', 'AT', 'AU', 'BE', 'BG', 'CA', 'CY', 'CZ', 'DE', 'DK',
        'EE', 'ES', 'FI', 'FR', 'GR', 'HK', 'HR', 'HU', 'IE', 'IT',
        'LT', 'LU', 'LV', 'MT', 'NL', 'NZ', 'PL', 'PT', 'RO', 'SA',
        'SE', 'SG', 'SI', 'SK', 'US',
    );

    /**
     * Normalize a WooCommerce order to Teleship API format
     *
     * @param WC_Order $order Order object.
     * @return array|null Teleship order data or null if should not sync.
     */
    public static function normalize($order) {
        try {
            // Guard: must have shipping address
            $shipping_country = $order->get_shipping_country();
            if (empty($shipping_country)) {
                $shipping_country = $order->get_billing_country();
            }
            if (empty($shipping_country)) {
                teleship_log('[Order Normalizer] Order ' . $order->get_id() . ' skipped - no shipping address');
                return null;
            }

            // Guard: must have line items
            $items = $order->get_items();
            if (empty($items)) {
                teleship_log('[Order Normalizer] Order ' . $order->get_id() . ' skipped - no line items');
                return null;
            }

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

            // Normalize line items
            $line_items = self::normalize_line_items($order);
            if (empty($line_items)) {
                teleship_log('[Order Normalizer] Order ' . $order->get_id() . ' skipped - no valid line items');
                return null;
            }

            // Build order date in ISO-8601
            $order_date = $order->get_date_created();
            $order_date_str = $order_date ? $order_date->format('c') : gmdate('c');

            $data = array(
                'orderId'     => (string) $order->get_id(),
                'orderNumber' => $order->get_order_number(),
                'orderDate'   => $order_date_str,
                'source'      => 'woocommerce',
                'shop'        => get_site_url(),
                'status'      => $status,
                'shipTo'      => self::normalize_shipping_address($order),
                'shipFrom'    => self::get_store_address(),
                'billTo'      => self::normalize_billing_address($order),
                'lineItems'   => $line_items,
                'totalValue'  => array(
                    'amount'   => (float) $order->get_total(),
                    'currency' => $order->get_currency(),
                ),
                'shippingCost' => array(
                    'amount'   => (float) $order->get_shipping_total(),
                    'currency' => $order->get_currency(),
                ),
                'metadata'    => self::build_metadata($order),
            );

            // Include integrationId if available (saved during OAuth connection)
            $integration_id = get_option('teleship_integration_id');
            if (!empty($integration_id)) {
                $data['integrationId'] = $integration_id;
            }

            return $data;

        } catch (Exception $e) {
            teleship_log('[Order Normalizer] Error normalizing order ' . $order->get_id() . ': ' . $e->getMessage());
            return null;
        }
    }

    /**
     * Normalize line items with full attribute extraction
     *
     * @param WC_Order $order Order object.
     * @return array Normalized line items.
     */
    private static function normalize_line_items($order) {
        $line_items = array();

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

            $qty = $item->get_quantity();
            if ($qty <= 0) {
                continue;
            }

            $product_id = $product->get_id();
            $parent_id  = $product->get_parent_id();
            $is_variation = $product->is_type('variation');

            // Get parent product for fallback attribute extraction
            $parent_product = null;
            if ($is_variation && $parent_id) {
                $parent_product = wc_get_product($parent_id);
            }

            // Calculate fulfillable quantity
            $fulfillable_qty = Teleship_Fulfillment_Calculator::get_fulfillable_quantity($item);

            // Unit price
            $unit_price = $qty > 0 ? (float) $item->get_total() / $qty : 0.0;

            $line_item = array(
                'title'              => $item->get_name(),
                'sku'                => $product->get_sku() ?: 'NO-SKU',
                'quantity'           => $qty,
                'fulfillableQuantity' => $fulfillable_qty,
                'value'              => array(
                    'amount'   => $unit_price,
                    'currency' => $order->get_currency(),
                ),
                'productId'          => (string) ($parent_id ?: $product_id),
                'variantId'          => $is_variation ? (string) $product_id : (string) ($parent_id ?: $product_id),
            );

            // Weight - use product weight with fallback to 0.1kg
            $weight = $product->get_weight();
            $wc_unit = get_option('woocommerce_weight_unit', 'kg');
            // Map WooCommerce units to Teleship API units (kg or lb only)
            $unit_map = array('kg' => 'kg', 'g' => 'kg', 'lbs' => 'lb', 'oz' => 'lb');
            $api_unit = isset($unit_map[$wc_unit]) ? $unit_map[$wc_unit] : 'kg';
            if ($weight && (float) $weight > 0) {
                $weight_value = (float) $weight;
                // Convert g→kg and oz→lb for the API
                if ($wc_unit === 'g') {
                    $weight_value = $weight_value / 1000;
                } elseif ($wc_unit === 'oz') {
                    $weight_value = $weight_value / 16;
                }
                $line_item['unitWeight'] = array(
                    'value' => $weight_value,
                    'unit'  => $api_unit,
                );
            } else {
                $line_item['unitWeight'] = array(
                    'value' => 0.1,
                    'unit'  => 'kg',
                );
            }

            // Description - strip tags, 250 char limit
            $description = $product->get_short_description();
            if (empty($description) && $parent_product) {
                $description = $parent_product->get_short_description();
            }
            if (!empty($description)) {
                $line_item['description'] = mb_substr(wp_strip_all_tags($description), 0, 250);
            }

            // Category
            $category_names = self::get_product_category_names($product, $parent_product);
            if (!empty($category_names)) {
                $line_item['category'] = implode(' > ', $category_names);
            }

            // Image URL
            $image_url = self::get_product_image_url($product, $parent_product);
            if ($image_url) {
                $line_item['imageUrl'] = $image_url;
            }

            // Product URL
            $product_url = $product->get_permalink();
            if ($product_url) {
                $line_item['productUrl'] = $product_url;
            }

            // HS code - variation first, then parent fallback
            $hs_code = self::extract_hs_code($product);
            if (empty($hs_code) && $parent_product) {
                $hs_code = self::extract_hs_code($parent_product);
            }
            if (!empty($hs_code)) {
                $line_item['hsCode'] = $hs_code;
            }

            // Country of origin - variation first, then parent fallback
            $coo = self::extract_country_of_origin($product);
            if (empty($coo) && $parent_product) {
                $coo = self::extract_country_of_origin($parent_product);
            }
            if (!empty($coo)) {
                $line_item['countryOfOrigin'] = $coo;
            }

            // Line item metadata
            $line_item['metadata'] = array(
                'wc_item_id'     => $item_id,
                'wc_product_type' => $product->get_type(),
            );

            $line_items[] = $line_item;
        }

        return $line_items;
    }

    /**
     * Extract HS code from product using multiple attribute name variations
     *
     * @param WC_Product $product Product object.
     * @return string|null HS code or null.
     */
    private static function extract_hs_code($product) {
        $product_id = $product->get_id();

        foreach (self::HS_CODE_ATTRIBUTES as $attr) {
            // Check product meta (covers both WC meta and custom fields)
            $value = get_post_meta($product_id, $attr, true);

            if (!empty($value) && is_string($value)) {
                return trim($value);
            }

            // Also check with underscore prefix for WC convention
            if (strpos($attr, '_') !== 0) {
                $value = get_post_meta($product_id, '_' . $attr, true);
                if (!empty($value) && is_string($value)) {
                    return trim($value);
                }
            }
        }

        // Check product attributes (WooCommerce product attributes tab)
        $attributes = $product->get_attributes();
        foreach (self::HS_CODE_ATTRIBUTES as $attr) {
            $clean_attr = ltrim($attr, '_');
            if (isset($attributes[$clean_attr])) {
                $attribute = $attributes[$clean_attr];
                $value = is_object($attribute) ? $attribute->get_options() : $attribute;
                if (is_array($value)) {
                    $value = reset($value);
                }
                if (!empty($value) && is_string($value)) {
                    return trim($value);
                }
            }
        }

        return null;
    }

    /**
     * Extract country of origin from product using multiple attribute name variations
     * Handles both ISO codes and full country names via Teleship_Country_Codes
     *
     * @param WC_Product $product Product object.
     * @return string|null ISO country code or null.
     */
    private static function extract_country_of_origin($product) {
        $product_id = $product->get_id();

        foreach (self::COUNTRY_OF_ORIGIN_ATTRIBUTES as $attr) {
            $value = get_post_meta($product_id, $attr, true);

            if (!empty($value) && is_string($value)) {
                $code = Teleship_Country_Codes::normalize(trim($value));
                if ($code) {
                    return $code;
                }
            }

            // Check with underscore prefix
            if (strpos($attr, '_') !== 0) {
                $value = get_post_meta($product_id, '_' . $attr, true);
                if (!empty($value) && is_string($value)) {
                    $code = Teleship_Country_Codes::normalize(trim($value));
                    if ($code) {
                        return $code;
                    }
                }
            }
        }

        // Check product attributes
        $attributes = $product->get_attributes();
        foreach (self::COUNTRY_OF_ORIGIN_ATTRIBUTES as $attr) {
            $clean_attr = ltrim($attr, '_');
            if (isset($attributes[$clean_attr])) {
                $attribute = $attributes[$clean_attr];
                $value = is_object($attribute) ? $attribute->get_options() : $attribute;
                if (is_array($value)) {
                    $value = reset($value);
                }
                if (!empty($value) && is_string($value)) {
                    $code = Teleship_Country_Codes::normalize(trim($value));
                    if ($code) {
                        return $code;
                    }
                }
            }
        }

        return null;
    }

    /**
     * Get product category names
     *
     * @param WC_Product      $product        Product object.
     * @param WC_Product|null $parent_product Parent product for fallback.
     * @return array Category names.
     */
    private static function get_product_category_names($product, $parent_product = null) {
        $target = $parent_product ?: $product;
        $target_id = $target->get_id();

        $terms = get_the_terms($target_id, 'product_cat');
        if (!$terms || is_wp_error($terms)) {
            return array();
        }

        $names = array();
        foreach ($terms as $term) {
            $names[] = $term->name;
            break; // Just first category to avoid long chains
        }

        return $names;
    }

    /**
     * Get product image URL with fallback to parent
     *
     * @param WC_Product      $product        Product object.
     * @param WC_Product|null $parent_product Parent product for fallback.
     * @return string|null Image URL or null.
     */
    private static function get_product_image_url($product, $parent_product = null) {
        $image_id = $product->get_image_id();
        if ($image_id) {
            $url = wp_get_attachment_url($image_id);
            if ($url) {
                return $url;
            }
        }

        // Fallback to parent product image
        if ($parent_product) {
            $image_id = $parent_product->get_image_id();
            if ($image_id) {
                return wp_get_attachment_url($image_id);
            }
        }

        return null;
    }

    /**
     * Build metadata for the order
     *
     * @param WC_Order $order Order object.
     * @return array Metadata array.
     */
    public static function build_metadata($order) {
        $metadata = array(
            'woocommerce_order_id' => $order->get_id(),
            'payment_method'       => $order->get_payment_method(),
            'payment_method_title' => $order->get_payment_method_title(),
            'customer_note'        => $order->get_customer_note(),
            'customer_id'          => $order->get_customer_id(),
            'order_currency'       => $order->get_currency(),
            'store_name'           => get_bloginfo('name'),
        );

        // Customer name
        $customer_name = trim($order->get_billing_first_name() . ' ' . $order->get_billing_last_name());
        if (!empty($customer_name)) {
            $metadata['customer_name'] = $customer_name;
        }

        // Shipping method
        $shipping_methods = $order->get_shipping_methods();
        if (!empty($shipping_methods)) {
            $first_method = reset($shipping_methods);
            $metadata['shipping_method'] = $first_method->get_method_title();
        }

        // Extract Teleship shipping data if applicable
        $platform_data = self::extract_teleship_shipping_data($order);
        if ($platform_data !== null) {
            $metadata['platform_data'] = $platform_data;
        }

        return $metadata;
    }

    /**
     * Extract Teleship shipping line data when order used Teleship shipping
     *
     * @param WC_Order $order Order object.
     * @return array|null Platform data or null if not Teleship shipping.
     */
    public static function extract_teleship_shipping_data($order) {
        $shipping_methods = $order->get_shipping_methods();
        if (empty($shipping_methods)) {
            return null;
        }

        foreach ($shipping_methods as $method) {
            $method_id = $method->get_method_id();

            // Check if this is a Teleship shipping method
            if (strpos($method_id, 'teleship') === false) {
                continue;
            }

            // Get the instance ID which contains the service code
            $instance_id = $method->get_instance_id();
            $meta_data = $method->get_meta_data();

            // Extract service code from meta or method ID
            $service_code = '';
            $service_name = $method->get_method_title();

            if (isset($meta_data['teleship_service_code'])) {
                $service_code = $meta_data['teleship_service_code'];
            } elseif (isset($meta_data['teleship_service_name'])) {
                $service_name = $meta_data['teleship_service_name'];
            }

            // Skip fallback rates
            if ($service_code === 'fallback') {
                continue;
            }

            return array(
                'platform'      => 'woocommerce',
                'shipping_line' => array(
                    'service_code' => $service_code,
                    'service_name' => $service_name,
                    'charge'       => array(
                        'amount'   => (float) $method->get_total(),
                        'currency' => $order->get_currency(),
                    ),
                ),
            );
        }

        return null;
    }

    /**
     * Check if order is international (shipping country differs from store country)
     *
     * @param WC_Order $order Order object.
     * @return bool True if international.
     */
    public static function is_international_order($order) {
        $shipping_country = $order->get_shipping_country();
        if (empty($shipping_country)) {
            $shipping_country = $order->get_billing_country();
        }

        if (empty($shipping_country)) {
            return false;
        }

        $store_country = WC()->countries->get_base_country();
        return $shipping_country !== $store_country;
    }

    /**
     * Check if order destination is a supported country
     *
     * @param WC_Order $order Order object.
     * @return bool True if destination country is supported.
     */
    public static function is_supported_destination($order) {
        $shipping_country = $order->get_shipping_country();
        if (empty($shipping_country)) {
            $shipping_country = $order->get_billing_country();
        }

        if (empty($shipping_country)) {
            return false;
        }

        return in_array(strtoupper($shipping_country), self::SUPPORTED_DESTINATION_COUNTRIES, true);
    }

    /**
     * Normalize shipping address
     *
     * @param WC_Order $order Order object.
     * @return array Normalized address.
     */
    private static function normalize_shipping_address($order) {
        $first_name = $order->get_shipping_first_name();
        $last_name  = $order->get_shipping_last_name();

        // Fallback to billing if shipping name empty
        if (empty($first_name) && empty($last_name)) {
            $first_name = $order->get_billing_first_name();
            $last_name  = $order->get_billing_last_name();
        }

        $address = array(
            'name'    => trim($first_name . ' ' . $last_name),
            'email'   => $order->get_billing_email(),
            'phone'   => $order->get_billing_phone() ?: $order->get_shipping_phone(),
            'address' => array(
                'line1'    => $order->get_shipping_address_1() ?: $order->get_billing_address_1(),
                'line2'    => $order->get_shipping_address_2() ?: $order->get_billing_address_2(),
                'city'     => $order->get_shipping_city() ?: $order->get_billing_city(),
                'state'    => $order->get_shipping_state() ?: $order->get_billing_state(),
                'postcode' => $order->get_shipping_postcode() ?: $order->get_billing_postcode(),
                'country'  => $order->get_shipping_country() ?: $order->get_billing_country(),
            ),
        );

        $company = $order->get_shipping_company() ?: $order->get_billing_company();
        if (!empty($company)) {
            $address['company'] = $company;
        }

        return $address;
    }

    /**
     * Normalize billing address
     *
     * @param WC_Order $order Order object.
     * @return array Normalized address.
     */
    private static function normalize_billing_address($order) {
        $address = array(
            'name'    => trim($order->get_billing_first_name() . ' ' . $order->get_billing_last_name()),
            'email'   => $order->get_billing_email(),
            'phone'   => $order->get_billing_phone(),
            'address' => array(
                'line1'    => $order->get_billing_address_1(),
                'line2'    => $order->get_billing_address_2(),
                'city'     => $order->get_billing_city(),
                'state'    => $order->get_billing_state(),
                'postcode' => $order->get_billing_postcode(),
                'country'  => $order->get_billing_country(),
            ),
        );

        $company = $order->get_billing_company();
        if (!empty($company)) {
            $address['company'] = $company;
        }

        return $address;
    }

    /**
     * Get store/warehouse address
     *
     * @return array Store address.
     */
    private static function get_store_address() {
        $address = array(
            'name'    => get_bloginfo('name'),
            'company' => get_bloginfo('name'),
            'email'   => get_option('woocommerce_email_from_address', get_option('admin_email')),
            'phone'   => '',
            'address' => array(
                'line1'    => get_option('woocommerce_store_address', ''),
                'line2'    => get_option('woocommerce_store_address_2', ''),
                'city'     => get_option('woocommerce_store_city', ''),
                'state'    => WC()->countries->get_base_state(),
                'postcode' => get_option('woocommerce_store_postcode', ''),
                'country'  => WC()->countries->get_base_country(),
            ),
        );

        return $address;
    }
}
