<?php
/**
 * DZQCOD Shipping — Yalidine + ZR Express (Procolis) + EcoTrack API Integration
 * Handles: create parcel, track, get rates, bulk ship, webhooks, bordereau
 */
namespace DZQCOD;
defined('ABSPATH') || exit;

class Shipping {
    // Provider configs
    public const PROVIDERS = [
        // 🇩🇿 الجزائر
        'yalidine' => ['name' => 'Yalidine', 'icon' => '🚚', 'base' => 'https://api.yalidine.app/v1', 'auth' => 'header', 'country' => 'DZ'],
        'zr'       => ['name' => 'ZR Express', 'icon' => '📦', 'base' => 'https://api.zrexpress.com/api', 'auth' => 'bearer', 'country' => 'DZ'],
        'ecotrack' => ['name' => 'EcoTrack', 'icon' => '🌿', 'base' => 'https://ecotrack.dz/api', 'auth' => 'bearer', 'country' => 'DZ'],
        'noest'    => ['name' => 'NOEST', 'icon' => '🔵', 'base' => 'https://app.noest-dz.com/api/public', 'auth' => 'bearer', 'country' => 'DZ'],
        'procolis' => ['name' => 'ProColis', 'icon' => '📮', 'base' => 'https://procolis.com/api_v1', 'auth' => 'bearer', 'country' => 'DZ'],
        'guepex'   => ['name' => 'Guepex', 'icon' => '🐆', 'base' => 'https://api.guepex.com/api', 'auth' => 'bearer', 'country' => 'DZ'],
        'maystro'  => ['name' => 'Maystro Delivery', 'icon' => '🎯', 'base' => 'https://api.maystro-delivery.com/api/v1', 'auth' => 'bearer', 'country' => 'DZ'],
        // 🇹🇳 تونس
        'aramex_tn'=> ['name' => 'Aramex Tunisie', 'icon' => '🟠', 'base' => 'https://ws.aramex.net/ShippingAPI.V2', 'auth' => 'soap', 'country' => 'TN'],
        'mylerz_tn'=> ['name' => 'Mylerz', 'icon' => '📦', 'base' => 'https://api.mylerz.com/api', 'auth' => 'bearer', 'country' => 'TN'],
        // 🇲🇦 المغرب
        'amana'    => ['name' => 'Amana Express', 'icon' => '📬', 'base' => 'https://api.amana.ma/api', 'auth' => 'bearer', 'country' => 'MA'],
        'jiblili'  => ['name' => 'Jiblili', 'icon' => '🚴', 'base' => 'https://api.jiblili.com/v1', 'auth' => 'bearer', 'country' => 'MA'],
        // 🇪🇬 مصر
        'bosta_eg' => ['name' => 'Bosta Egypt', 'icon' => '📮', 'base' => 'https://app.bosta.co/api/v2', 'auth' => 'bearer', 'country' => 'EG'],
        'mylerz_eg'=> ['name' => 'Mylerz Egypt', 'icon' => '📦', 'base' => 'https://api.mylerz.com/api', 'auth' => 'bearer', 'country' => 'EG'],
        // 🇸🇦 السعودية
        'smsa'     => ['name' => 'SMSA Express', 'icon' => '📦', 'base' => 'https://sam.smsaexpress.com/api', 'auth' => 'apikey', 'country' => 'SA'],
        'aramex_sa'=> ['name' => 'Aramex KSA', 'icon' => '🟠', 'base' => 'https://ws.aramex.net/ShippingAPI.V2', 'auth' => 'soap', 'country' => 'SA'],
        // 🌍 دولي
        'dhl'      => ['name' => 'DHL Express', 'icon' => '🟡', 'base' => 'https://express.api.dhl.com/mydhlapi', 'auth' => 'basic', 'country' => 'INT'],
        'fedex'    => ['name' => 'FedEx', 'icon' => '🟣', 'base' => 'https://apis.fedex.com', 'auth' => 'oauth', 'country' => 'INT'],
    ];

    public function __construct() {
        // Admin AJAX
        foreach (['dzqcod_ship_create', 'dzqcod_ship_bulk', 'dzqcod_ship_track', 'dzqcod_ship_rates',
                   'dzqcod_ship_save_config', 'dzqcod_ship_test_connection', 'dzqcod_ship_label', 'dzqcod_ship_sync_noest_fees'] as $a) {
            add_action('wp_ajax_' . $a, [$this, str_replace('dzqcod_', '', $a)]);
        }
        // Webhooks (public)
        add_action('wp_ajax_nopriv_dzqcod_yalidine_webhook', [$this, 'yalidine_webhook']);
        add_action('wp_ajax_dzqcod_yalidine_webhook', [$this, 'yalidine_webhook']);
        add_action('wp_ajax_nopriv_dzqcod_zr_webhook', [$this, 'zr_webhook']);
        add_action('wp_ajax_dzqcod_zr_webhook', [$this, 'zr_webhook']);
        add_action('wp_ajax_nopriv_dzqcod_ecotrack_webhook', [$this, 'ecotrack_webhook']);
        add_action('wp_ajax_dzqcod_ecotrack_webhook', [$this, 'ecotrack_webhook']);
        add_action('wp_ajax_nopriv_dzqcod_noest_webhook', [$this, 'noest_webhook']);
        add_action('wp_ajax_dzqcod_noest_webhook', [$this, 'noest_webhook']);
        // New providers webhooks
        foreach (['maystro','procolis','guepex'] as $_pw) {
            add_action('wp_ajax_nopriv_dzqcod_'.$_pw.'_webhook', [$this, $_pw.'_webhook']);
            add_action('wp_ajax_dzqcod_'.$_pw.'_webhook', [$this, $_pw.'_webhook']);
        }
        // Auto-track cron
        add_action('dzqcod_cron_track_shipments', [$this, 'cron_track_all']);
        if (!wp_next_scheduled('dzqcod_cron_track_shipments')) {
            wp_schedule_event(time(), 'twicedaily', 'dzqcod_cron_track_shipments');
        }
    }

    // ═══════════════════════════════════════════
    //  API HELPER — Unified request for all 3
    // ═══════════════════════════════════════════
    private static function api(string $provider, string $method, string $endpoint, array $body = []): array {
        $p = self::PROVIDERS[$provider] ?? null;
        if (!$p) return ['ok' => false, 'error' => 'مزود غير معروف'];

        $headers = ['Content-Type' => 'application/json', 'Accept' => 'application/json'];

        if ($provider === 'yalidine') {
            $api_id = Core::opt('yalidine_api_id', '');
            $api_token = Core::opt('yalidine_api_token', '');
            if (!$api_id || !$api_token) return ['ok' => false, 'error' => 'مفاتيح Yalidine غير مضبوطة'];
            $headers['X-API-ID'] = $api_id;
            $headers['X-API-TOKEN'] = $api_token;
        } elseif ($provider === 'zr') {
            $token = Core::opt('zr_api_token', '');
            if (!$token) return ['ok' => false, 'error' => 'Token ZR Express غير مضبوط'];
            $headers['Authorization'] = 'Bearer ' . $token;
        } elseif ($provider === 'ecotrack') {
            $token = Core::opt('ecotrack_api_token', '');
            if (!$token) return ['ok' => false, 'error' => 'Token EcoTrack غير مضبوط'];
            $headers['Authorization'] = 'Bearer ' . $token;
        } elseif ($provider === 'maystro') {
            $token = Core::opt('maystro_api_token', '');
            if (!$token) return ['ok' => false, 'error' => 'Token Maystro غير مضبوط'];
            $headers['Authorization'] = 'Bearer ' . $token;
        } elseif ($provider === 'procolis') {
            $id = Core::opt('procolis_api_id', '');
            $token = Core::opt('procolis_api_token', '');
            if (!$id || !$token) return ['ok' => false, 'error' => 'مفاتيح ProColis غير مضبوطة'];
            $headers['X-API-ID'] = $id;
            $headers['X-API-TOKEN'] = $token;
        } elseif ($provider === 'guepex') {
            $token = Core::opt('guepex_api_token', '');
            if (!$token) return ['ok' => false, 'error' => 'Token Guepex غير مضبوط'];
            $headers['Authorization'] = 'Bearer ' . $token;
        }

        $url = rtrim($p['base'], '/') . '/' . ltrim($endpoint, '/');
        $args = ['headers' => $headers, 'timeout' => 20];

        if ($method === 'GET') {
            if ($body) $url .= '?' . http_build_query($body);
            $r = wp_remote_get($url, $args);
        } else {
            $args['body'] = wp_json_encode($body);
            $r = wp_remote_post($url, $args);
        }

        if (is_wp_error($r)) return ['ok' => false, 'error' => $r->get_error_message()];
        $code = wp_remote_retrieve_response_code($r);
        $data = json_decode(wp_remote_retrieve_body($r), true);
        return ['ok' => $code >= 200 && $code < 300, 'code' => $code, 'data' => $data];
    }

    // ═══════════════════════════════════════════
    //  CREATE PARCEL — Single order
    // ═══════════════════════════════════════════
    public function ship_create(): void {
        check_ajax_referer('dzqcod_admin', 'nonce');
        if (!current_user_can('manage_woocommerce')) wp_send_json_error(['message' => 'صلاحيات غير كافية']);

        $oid = absint($_POST['order_id'] ?? 0);
        $provider = sanitize_key($_POST['provider'] ?? Core::opt('shipping_provider', 'yalidine'));
        $is_stopdesk = ($_POST['is_stopdesk'] ?? '0') === '1';
        $stopdesk_id = absint($_POST['stopdesk_id'] ?? 0);

        $o = wc_get_order($oid);
        if (!$o) wp_send_json_error(['message' => 'طلب غير موجود']);

        // Check not already shipped
        $existing = $o->get_meta('_dzqcod_tracking_number');
        if ($existing) wp_send_json_error(['message' => 'الطلب مشحون مسبقاً: ' . $existing]);

        $result = $this->create_parcel($o, $provider, $is_stopdesk, $stopdesk_id);

        if ($result['ok']) {
            $tracking = $result['tracking'] ?? '';
            $o->update_meta_data('_dzqcod_tracking_number', $tracking);
            $o->update_meta_data('_dzqcod_shipping_provider', $provider);
            $o->update_meta_data('_dzqcod_shipped_at', current_time('mysql'));
            $o->set_status('wc-shipped');
            $o->add_order_note(sprintf('🚚 %s: تم الشحن — رقم التتبع: %s', self::PROVIDERS[$provider]['name'] ?? $provider, $tracking));
            $o->save();
            // Fire WhatsApp if configured
            do_action('dzqcod_parcel_shipped', $oid, $tracking, $provider);
            wp_send_json_success(['message' => 'تم الشحن ✅', 'tracking' => $tracking, 'provider' => $provider]);
        } else {
            wp_send_json_error(['message' => $result['error'] ?? 'خطأ في API الشحن', 'details' => $result['data'] ?? null]);
        }
    }

    // ═══════════════════════════════════════════
    //  BULK SHIP — Multiple orders at once
    // ═══════════════════════════════════════════
    public function ship_bulk(): void {
        check_ajax_referer('dzqcod_admin', 'nonce');
        if (!current_user_can('manage_woocommerce')) wp_send_json_error();

        $ids = array_map('absint', (array)($_POST['order_ids'] ?? []));
        $provider = sanitize_key($_POST['provider'] ?? Core::opt('shipping_provider', 'yalidine'));
        if (empty($ids)) wp_send_json_error(['message' => 'لم يتم اختيار طلبات']);

        $results = ['success' => 0, 'failed' => 0, 'errors' => []];

        // Yalidine supports batch creation — up to 10 per request
        if ($provider === 'yalidine') {
            $parcels = [];
            $order_map = [];
            foreach ($ids as $oid) {
                $o = wc_get_order($oid);
                if (!$o || $o->get_meta('_dzqcod_tracking_number')) { $results['failed']++; continue; }
                $parcel_data = $this->build_yalidine_parcel($o);
                if ($parcel_data) { $parcels[] = $parcel_data; $order_map[$o->get_id()] = $o; }
            }

            if (!empty($parcels)) {
                // Yalidine batch: send array of parcels
                foreach (array_chunk($parcels, 10) as $batch) {
                    $r = self::api('yalidine', 'POST', 'parcels/', $batch);
                    if ($r['ok'] && is_array($r['data'])) {
                        foreach ($r['data'] as $res) {
                            $oid_key = $res['order_id'] ?? '';
                            if (isset($res['tracking']) && $oid_key && isset($order_map[$oid_key])) {
                                $ox = $order_map[$oid_key];
                                $ox->update_meta_data('_dzqcod_tracking_number', $res['tracking']);
                                $ox->update_meta_data('_dzqcod_shipping_provider', 'yalidine');
                                $ox->update_meta_data('_dzqcod_shipped_at', current_time('mysql'));
                                $ox->set_status('wc-shipped');
                                $ox->add_order_note('🚚 Yalidine Bulk: ' . $res['tracking']);
                                $ox->save();
                                $results['success']++;
                            } else {
                                $results['failed']++;
                                $results['errors'][] = $oid_key . ': ' . ($res['error'] ?? 'خطأ');
                            }
                        }
                    } else {
                        $results['failed'] += count($batch);
                        $results['errors'][] = $r['error'] ?? 'Batch API error';
                    }
                }
            }
        } else {
            // Other providers: one by one
            foreach ($ids as $oid) {
                $o = wc_get_order($oid);
                if (!$o || $o->get_meta('_dzqcod_tracking_number')) { $results['failed']++; continue; }
                $res = $this->create_parcel($o, $provider);
                if ($res['ok']) {
                    $o->update_meta_data('_dzqcod_tracking_number', $res['tracking'] ?? '');
                    $o->update_meta_data('_dzqcod_shipping_provider', $provider);
                    $o->update_meta_data('_dzqcod_shipped_at', current_time('mysql'));
                    $o->set_status('wc-shipped');
                    $o->add_order_note(sprintf('🚚 %s Bulk: %s', self::PROVIDERS[$provider]['name'], $res['tracking'] ?? ''));
                    $o->save();
                    $results['success']++;
                } else {
                    $results['failed']++;
                    $results['errors'][] = '#' . $oid . ': ' . ($res['error'] ?? 'خطأ');
                }
            }
        }

        wp_send_json_success($results);
    }

    // ═══════════════════════════════════════════
    //  BUILD PARCEL DATA PER PROVIDER
    // ═══════════════════════════════════════════
    private function create_parcel($order, string $provider, bool $stopdesk = false, int $stopdesk_id = 0): array {
        switch ($provider) {
            case 'yalidine': return $this->create_yalidine($order, $stopdesk, $stopdesk_id);
            case 'zr':       return $this->create_zr($order, $stopdesk, $stopdesk_id);
            case 'ecotrack': return $this->create_ecotrack($order, $stopdesk, $stopdesk_id);
            case 'noest':    return $this->create_noest($order, $stopdesk, $stopdesk_id);
            case 'maystro':  return $this->create_maystro($order, $stopdesk, $stopdesk_id);
            case 'procolis': return $this->create_procolis($order, $stopdesk, $stopdesk_id);
            case 'guepex':   return $this->create_guepex($order, $stopdesk, $stopdesk_id);
            default: return ['ok' => false, 'error' => 'مزود غير مدعوم: ' . $provider];
        }
    }

    // — YALIDINE —
    private function build_yalidine_parcel($order, bool $stopdesk = false, int $stopdesk_id = 0): ?array {
        $items = []; foreach ($order->get_items() as $i) $items[] = $i->get_name();
        $from = Core::opt('yalidine_from_wilaya', 'Alger');
        $name = $order->get_billing_first_name() . ' ' . $order->get_billing_last_name();
        $name_parts = explode(' ', trim($name), 2);

        return [
            'order_id'         => (string)$order->get_id(),
            'from_wilaya_name' => $from,
            'firstname'        => $name_parts[0] ?? $order->get_billing_first_name(),
            'familyname'       => $name_parts[1] ?? '',
            'contact_phone'    => $order->get_billing_phone(),
            'address'          => $order->get_shipping_address_1() ?: $order->get_billing_address_1(),
            'to_commune_name'  => $order->get_shipping_city(),
            'to_wilaya_name'   => $order->get_shipping_state(),
            'product_list'     => implode(', ', $items),
            'price'            => (int)round((float)$order->get_total()),
            'freeshipping'     => (float)$order->get_shipping_total() <= 0,
            'is_stopdesk'      => $stopdesk,
            'stopdesk_id'      => $stopdesk ? $stopdesk_id : null,
            'has_exchange'     => false,
            'product_to_collect' => null,
        ];
    }

    private function create_yalidine($order, bool $stopdesk, int $stopdesk_id): array {
        $parcel = $this->build_yalidine_parcel($order, $stopdesk, $stopdesk_id);
        if (!$parcel) return ['ok' => false, 'error' => 'بيانات الطلب ناقصة'];
        $r = self::api('yalidine', 'POST', 'parcels/', [$parcel]);
        if ($r['ok'] && is_array($r['data'])) {
            $first = $r['data'][0] ?? $r['data'];
            if (isset($first['tracking'])) return ['ok' => true, 'tracking' => $first['tracking'], 'data' => $first];
            if (isset($first['error'])) return ['ok' => false, 'error' => $first['error']];
        }
        return ['ok' => false, 'error' => $r['error'] ?? 'خطأ Yalidine API', 'data' => $r['data'] ?? null];
    }

    // — ZR EXPRESS (Procolis) —
    private function create_zr($order, bool $stopdesk, int $stopdesk_id): array {
        $items = []; foreach ($order->get_items() as $i) $items[] = $i->get_name();
        $body = [
            'Ession'          => Core::opt('zr_session_id', ''),
            'NumCommande'     => (string)$order->get_id(),
            'TypeLivraison'   => $stopdesk ? 'STOPDESK' : 'DOMICILE',
            'TypeColis'       => 'STANDARD',
            'Confrimee'       => true,
            'Client'          => $order->get_billing_first_name() . ' ' . $order->get_billing_last_name(),
            'MobileA'         => $order->get_billing_phone(),
            'MobileB'         => $order->get_meta('_dzqcod_phone2') ?: '',
            'Adresse'         => $order->get_shipping_address_1() ?: $order->get_billing_address_1(),
            'IDWilaya'        => $this->wilaya_name_to_id($order->get_shipping_state()),
            'Commune'         => $order->get_shipping_city(),
            'Total'           => (float)$order->get_total(),
            'Note'            => implode(', ', $items),
            'TProduit'        => implode(', ', $items),
            'StopDesck_id'    => $stopdesk ? $stopdesk_id : 0,
        ];
        $r = self::api('zr', 'POST', 'CreateOrder', $body);
        if ($r['ok'] && isset($r['data']['tracking'])) {
            return ['ok' => true, 'tracking' => $r['data']['tracking']];
        }
        // ZR may return tracking in different field
        if ($r['ok'] && isset($r['data']['Tracking'])) {
            return ['ok' => true, 'tracking' => $r['data']['Tracking']];
        }
        return ['ok' => false, 'error' => $r['data']['message'] ?? $r['error'] ?? 'خطأ ZR Express', 'data' => $r['data'] ?? null];
    }

    // — ECOTRACK —
    private function create_ecotrack($order, bool $stopdesk, int $stopdesk_id): array {
        $items = []; foreach ($order->get_items() as $i) $items[] = $i->get_name();
        $body = [
            'order_id'     => (string)$order->get_id(),
            'client_name'  => $order->get_billing_first_name() . ' ' . $order->get_billing_last_name(),
            'client_phone' => $order->get_billing_phone(),
            'client_phone2' => $order->get_meta('_dzqcod_phone2') ?: '',
            'address'      => $order->get_shipping_address_1() ?: $order->get_billing_address_1(),
            'wilaya'       => $order->get_shipping_state(),
            'commune'      => $order->get_shipping_city(),
            'amount'       => (float)$order->get_total(),
            'product'      => implode(', ', $items),
            'delivery_type' => $stopdesk ? 'stopdesk' : 'home',
            'note'         => $order->get_customer_note(),
        ];
        $r = self::api('ecotrack', 'POST', 'parcels', $body);
        if ($r['ok'] && isset($r['data']['tracking'])) {
            return ['ok' => true, 'tracking' => $r['data']['tracking']];
        }
        return ['ok' => false, 'error' => $r['data']['message'] ?? $r['error'] ?? 'خطأ EcoTrack', 'data' => $r['data'] ?? null];
    }

    // ═══════════════════════════════════════════
    //  NOEST — Create Order (app.noest-dz.com)
    // ═══════════════════════════════════════════
    private function create_noest($order, bool $stopdesk, int $stopdesk_id): array {
        $items = []; $qtys = [];
        foreach ($order->get_items() as $i) {$items[] = $i->get_name(); $qtys[] = $i->get_quantity();}
        $wilaya = $order->get_shipping_state() ?: $order->get_billing_state();
        $commune = $order->get_shipping_city() ?: $order->get_billing_city();
        $wil_id = is_numeric($wilaya) ? (int)$wilaya : self::noest_wilaya_id($wilaya);
        $body = [
            'user_guid'   => Core::opt('noest_user_guid', ''),
            'reference'   => 'DZQCOD-' . $order->get_id(),
            'client'      => trim($order->get_billing_first_name() . ' ' . $order->get_billing_last_name()),
            'phone'       => $order->get_billing_phone(),
            'phone_2'     => $order->get_meta('_dzqcod_phone2') ?: '',
            'adresse'     => $order->get_shipping_address_1() ?: $order->get_billing_address_1(),
            'wilaya_id'   => $wil_id,
            'commune'     => $commune,
            'montant'     => (float)$order->get_total(),
            'produit'     => implode(', ', $items),
            'type_id'     => 1, // Delivery
            'stop_desk'   => $stopdesk ? 1 : 0,
            'poids'       => 0.5,
            'can_open'    => (int)(Core::opt('noest_can_open', '1') === '1'),
            'remarque'    => mb_substr($order->get_customer_note(), 0, 255),
        ];
        if ($stopdesk && $stopdesk_id) $body['station_code'] = (string)$stopdesk_id;
        $r = self::noest_api('POST', 'create/order', $body);
        if (!empty($r['success']) && !empty($r['tracking'])) {
            // Auto-validate if setting is on
            if (Core::opt('noest_auto_validate', 'no') === 'yes') {
                self::noest_api('POST', 'valid/order', ['user_guid' => $body['user_guid'], 'tracking' => $r['tracking']]);
            }
            return ['ok' => true, 'tracking' => $r['tracking']];
        }
        $err = $r['message'] ?? '';
        if (empty($err) && is_array($r)) {
            foreach ($r as $k => $v) {if (is_array($v)) $err .= implode(', ', $v) . '; '; elseif (is_string($v) && $k !== 'success') $err .= $v . '; ';}
        }
        return ['ok' => false, 'error' => $err ?: 'خطأ NOEST', 'data' => $r];
    }

    // NOEST API helper
    private static function noest_api(string $method, string $endpoint, array $body = []): array {
        $token = Core::opt('noest_api_token', '');
        if (!$token) return ['success' => false, 'message' => 'NOEST Token غير مضبوط'];
        $url = 'https://app.noest-dz.com/api/public/' . $endpoint;
        $args = ['method' => $method, 'timeout' => 30, 'headers' => ['Authorization' => 'Bearer ' . $token, 'Content-Type' => 'application/json', 'Accept' => 'application/json']];
        if ($method === 'POST') $args['body'] = wp_json_encode($body);
        $resp = wp_remote_request($url, $args);
        if (is_wp_error($resp)) return ['success' => false, 'message' => $resp->get_error_message()];
        return json_decode(wp_remote_retrieve_body($resp), true) ?: ['success' => false, 'message' => 'استجابة فارغة'];
    }

    // NOEST wilaya name → ID helper
    private static function noest_wilaya_id(string $name): int {
        $map = ['Adrar'=>1,'Chlef'=>2,'Laghouat'=>3,'Oum El Bouaghi'=>4,'Batna'=>5,'Béjaïa'=>6,'Biskra'=>7,'Béchar'=>8,'Blida'=>9,'Bouira'=>10,'Tamanrasset'=>11,'Tébessa'=>12,'Tlemcen'=>13,'Tiaret'=>14,'Tizi Ouzou'=>15,'Alger'=>16,'Djelfa'=>17,'Jijel'=>18,'Sétif'=>19,'Saïda'=>20,'Skikda'=>21,'Sidi Bel Abbès'=>22,'Annaba'=>23,'Guelma'=>24,'Constantine'=>25,'Médéa'=>26,'Mostaganem'=>27,'M\'Sila'=>28,'Mascara'=>29,'Ouargla'=>30,'Oran'=>31,'El Bayadh'=>32,'Illizi'=>33,'Bordj Bou Arréridj'=>34,'Boumerdès'=>35,'El Tarf'=>36,'Tindouf'=>37,'Tissemsilt'=>38,'El Oued'=>39,'Khenchela'=>40,'Souk Ahras'=>41,'Tipaza'=>42,'Mila'=>43,'Aïn Defla'=>44,'Naâma'=>45,'Aïn Témouchent'=>46,'Ghardaïa'=>47,'Relizane'=>48,'El M\'Ghair'=>49,'El Meniaa'=>50,'Ouled Djellal'=>51,'Bordj Badji Mokhtar'=>52,'Béni Abbès'=>53,'Timimoun'=>54,'Touggourt'=>55,'Djanet'=>56,'In Salah'=>57,'In Guezzam'=>58];
        foreach ($map as $n => $id) {if (mb_stripos($n, $name) !== false || mb_stripos($name, $n) !== false) return $id;}
        return (int)preg_replace('/\D/', '', $name) ?: 16;
    }

    // NOEST: validate order
    public static function noest_validate(string $tracking): array {
        return self::noest_api('POST', 'valid/order', ['user_guid' => Core::opt('noest_user_guid', ''), 'tracking' => $tracking]);
    }

    // NOEST: track order
    private function track_noest(string $tracking): array {
        $r = self::noest_api('POST', 'get/trackings/info', ['trackings' => [$tracking]]);
        if (isset($r[$tracking])) {
            $info = $r[$tracking];
            $events = [];
            foreach ($info['activity'] ?? [] as $a) {
                $events[] = ['date' => $a['date'] ?? '', 'status' => $a['event'] ?? '', 'by' => $a['by'] ?? ''];
            }
            $oi = $info['OrderInfo'] ?? [];
            return ['tracking' => $tracking, 'status' => end($events)['status'] ?? 'unknown', 'events' => $events, 'driver' => $oi['driver_name'] ?? '', 'driver_phone' => $oi['driver_tel'] ?? ''];
        }
        return ['tracking' => $tracking, 'status' => 'not_found', 'events' => []];
    }

    // NOEST: download label
    public static function noest_label(string $tracking): string {
        $token = Core::opt('noest_api_token', '');
        return 'https://app.noest-dz.com/api/public/get/order/label?tracking=' . urlencode($tracking) . '&token=' . urlencode($token);
    }

    // NOEST: get fees
    public static function noest_fees(): array {
        $r = self::noest_api('GET', 'fees');
        return $r['tarifs'] ?? [];
    }

    // NOEST: get communes
    public static function noest_communes(int $wilaya_id = 0): array {
        $endpoint = 'get/communes' . ($wilaya_id ? '/' . $wilaya_id : '');
        return self::noest_api('GET', $endpoint) ?: [];
    }

    // NOEST: get desks (stop desk points)
    public static function noest_desks(): array {
        return self::noest_api('GET', 'desks') ?: [];
    }

    // — MAYSTRO DELIVERY —
    private function create_maystro($order, bool $stopdesk, int $stopdesk_id): array {
        $items = []; foreach ($order->get_items() as $i) $items[] = $i->get_name();
        $wilaya = $order->get_shipping_state() ?: $order->get_billing_state();
        $body = [
            'reference'    => 'DZQCOD-' . $order->get_id(),
            'first_name'   => $order->get_billing_first_name(),
            'last_name'    => $order->get_billing_last_name(),
            'phone'        => $order->get_billing_phone(),
            'address'      => $order->get_shipping_address_1() ?: $order->get_billing_address_1(),
            'wilaya'       => $wilaya,
            'commune'      => $order->get_shipping_city() ?: $order->get_billing_city(),
            'amount'       => (float)$order->get_total(),
            'product'      => implode(', ', $items),
            'delivery_type'=> $stopdesk ? 'DESK' : 'HOME',
            'note'         => mb_substr($order->get_customer_note(), 0, 255),
        ];
        $r = self::api('maystro', 'POST', 'orders', $body);
        if ($r['ok'] && !empty($r['data']['tracking'])) return ['ok' => true, 'tracking' => $r['data']['tracking'], 'data' => $r['data']];
        if ($r['ok'] && !empty($r['data']['code'])) return ['ok' => true, 'tracking' => $r['data']['code'], 'data' => $r['data']];
        return ['ok' => false, 'error' => $r['data']['message'] ?? $r['error'] ?? 'خطأ Maystro API', 'data' => $r['data'] ?? null];
    }

    // — PROCOLIS —
    private function create_procolis($order, bool $stopdesk, int $stopdesk_id): array {
        $items = []; foreach ($order->get_items() as $i) $items[] = $i->get_name();
        $body = [
            'NumCommande'  => (string)$order->get_id(),
            'TypeLivraison'=> $stopdesk ? 'STOPDESK' : 'DOMICILE',
            'TypeColis'    => 'STANDARD',
            'Confrimee'    => true,
            'Client'       => $order->get_billing_first_name() . ' ' . $order->get_billing_last_name(),
            'MobileA'      => $order->get_billing_phone(),
            'MobileB'      => $order->get_meta('_dzqcod_phone2') ?: '',
            'Adresse'      => $order->get_shipping_address_1() ?: $order->get_billing_address_1(),
            'IDWilaya'     => $this->wilaya_name_to_id($order->get_shipping_state()),
            'Commune'      => $order->get_shipping_city(),
            'Total'        => (float)$order->get_total(),
            'Note'         => implode(', ', $items),
            'TProduit'     => implode(', ', $items),
            'StopDesck_id' => $stopdesk ? $stopdesk_id : 0,
        ];
        $r = self::api('procolis', 'POST', 'CreateOrder', $body);
        if ($r['ok'] && isset($r['data']['tracking'])) return ['ok' => true, 'tracking' => $r['data']['tracking']];
        if ($r['ok'] && isset($r['data']['Tracking'])) return ['ok' => true, 'tracking' => $r['data']['Tracking']];
        return ['ok' => false, 'error' => $r['data']['message'] ?? $r['error'] ?? 'خطأ ProColis API', 'data' => $r['data'] ?? null];
    }

    // — GUEPEX —
    private function create_guepex($order, bool $stopdesk, int $stopdesk_id): array {
        $items = []; foreach ($order->get_items() as $i) $items[] = $i->get_name();
        $body = [
            'order_id'     => (string)$order->get_id(),
            'nom'          => $order->get_billing_first_name() . ' ' . $order->get_billing_last_name(),
            'telephone'    => $order->get_billing_phone(),
            'telephone_2'  => $order->get_meta('_dzqcod_phone2') ?: '',
            'adresse'      => $order->get_shipping_address_1() ?: $order->get_billing_address_1(),
            'wilaya_id'    => $this->wilaya_name_to_id($order->get_shipping_state()),
            'commune'      => $order->get_shipping_city() ?: $order->get_billing_city(),
            'montant'      => (float)$order->get_total(),
            'produit'      => implode(', ', $items),
            'type_livraison' => $stopdesk ? 'point_relais' : 'domicile',
            'remarque'     => mb_substr($order->get_customer_note(), 0, 255),
            'poids'        => 0.5,
        ];
        $r = self::api('guepex', 'POST', 'colis/create', $body);
        if ($r['ok'] && !empty($r['data']['tracking'])) return ['ok' => true, 'tracking' => $r['data']['tracking'], 'data' => $r['data']];
        if ($r['ok'] && !empty($r['data']['code_suivi'])) return ['ok' => true, 'tracking' => $r['data']['code_suivi'], 'data' => $r['data']];
        return ['ok' => false, 'error' => $r['data']['message'] ?? $r['error'] ?? 'خطأ Guepex API', 'data' => $r['data'] ?? null];
    }

    // NOEST webhook
    public function noest_webhook(): void {
        $payload = json_decode(WP_REST_Server::get_raw_data(), true);
        if (empty($payload)) wp_send_json(['status' => 'no_data']);
        $tracking = $payload['tracking'] ?? '';
        $event = $payload['event'] ?? $payload['event_key'] ?? '';
        if (!$tracking) wp_send_json(['status' => 'no_tracking']);
        // Find order by tracking
        global $wpdb;
        $oid = $wpdb->get_var($wpdb->prepare("SELECT post_id FROM {$wpdb->postmeta} WHERE meta_key='_dzqcod_tracking_number' AND meta_value=%s", $tracking));
        if (!$oid) wp_send_json(['status' => 'not_found']);
        $o = wc_get_order($oid);
        if (!$o) wp_send_json(['status' => 'invalid_order']);
        // Map NOEST events to DZQCOD statuses
        $map = ['livre' => 'delivered', 'livred' => 'delivered', 'livraison_echoue_recu' => 'rto-received', 'fdr_activated' => 'out-for-delivery', 'validation_reception' => 'in-transit', 'customer_validation' => 'shipped', 'retour_dispatched_to_partenaires' => 'rto', 'return_dispatched_to_partenaire' => 'rto', 'colis_retour_transmit_to_partner' => 'rto-received'];
        $new_st = $map[$event] ?? null;
        if ($new_st && isset(Core::ST[$new_st])) {
            $o->update_status('wc-' . $new_st, 'NOEST: ' . ($payload['event'] ?? $event));
        }
        $o->add_order_note('🔵 NOEST: ' . ($payload['event'] ?? $event) . ' — ' . current_time('mysql'));
        wp_send_json(['status' => 'ok']);
    }

    // ═══════════════════════════════════════════
    //  TRACKING — Get status for a parcel
    // ═══════════════════════════════════════════
    public function ship_track(): void {
        check_ajax_referer('dzqcod_admin', 'nonce');
        $oid = absint($_POST['order_id'] ?? 0);
        $o = wc_get_order($oid);
        if (!$o) wp_send_json_error(['message' => 'طلب غير موجود']);

        $tracking = $o->get_meta('_dzqcod_tracking_number');
        $provider = $o->get_meta('_dzqcod_shipping_provider') ?: Core::opt('shipping_provider', 'yalidine');
        if (!$tracking) wp_send_json_error(['message' => 'لا يوجد رقم تتبع']);

        $result = $this->track_parcel($tracking, $provider);
        wp_send_json_success($result);
    }

    public function track_parcel(string $tracking, string $provider): array {
        switch ($provider) {
            case 'yalidine':
                $r = self::api('yalidine', 'GET', 'parcels/', ['tracking' => $tracking]);
                if ($r['ok'] && !empty($r['data'])) {
                    $p = is_array($r['data'][0] ?? null) ? $r['data'][0] : $r['data'];
                    return ['tracking' => $tracking, 'status' => $p['last_status'] ?? $p['status'] ?? '', 'history' => $p['histories'] ?? [],
                            'provider' => 'yalidine', 'raw' => $p];
                }
                break;
            case 'zr':
                $r = self::api('zr', 'GET', 'Tracking', ['tracking' => $tracking]);
                if ($r['ok']) return ['tracking' => $tracking, 'status' => $r['data']['status'] ?? '', 'history' => $r['data']['history'] ?? [],
                                     'provider' => 'zr', 'raw' => $r['data']];
                break;
            case 'ecotrack':
                $r = self::api('ecotrack', 'GET', 'tracking', ['tracking' => $tracking]);
                if ($r['ok']) return ['tracking' => $tracking, 'status' => $r['data']['status'] ?? '', 'history' => $r['data']['history'] ?? [],
                                     'provider' => 'ecotrack', 'raw' => $r['data']];
                break;
            case 'noest':
                return $this->track_noest($tracking);
            case 'maystro':
                $r = self::api('maystro', 'GET', 'orders/' . $tracking, []);
                if ($r['ok']) return ['tracking' => $tracking, 'status' => $r['data']['status'] ?? $r['data']['state'] ?? '', 'history' => $r['data']['history'] ?? $r['data']['tracking_history'] ?? [], 'provider' => 'maystro', 'raw' => $r['data']];
                break;
            case 'procolis':
                $r = self::api('procolis', 'GET', 'Tracking', ['tracking' => $tracking]);
                if ($r['ok']) return ['tracking' => $tracking, 'status' => $r['data']['status'] ?? $r['data']['Status'] ?? '', 'history' => $r['data']['history'] ?? [], 'provider' => 'procolis', 'raw' => $r['data']];
                break;
            case 'guepex':
                $r = self::api('guepex', 'GET', 'colis/tracking/' . $tracking, []);
                if ($r['ok']) return ['tracking' => $tracking, 'status' => $r['data']['statut'] ?? $r['data']['status'] ?? '', 'history' => $r['data']['historique'] ?? $r['data']['history'] ?? [], 'provider' => 'guepex', 'raw' => $r['data']];
                break;
        }
        return ['tracking' => $tracking, 'status' => 'unknown', 'error' => 'لم يتم العثور على معلومات التتبع'];
    }

    // ═══════════════════════════════════════════
    //  GET SHIPPING RATES
    // ═══════════════════════════════════════════
    public function ship_rates(): void {
        check_ajax_referer('dzqcod_admin', 'nonce');
        $provider = sanitize_key($_POST['provider'] ?? Core::opt('shipping_provider', 'yalidine'));
        $result = $this->get_rates($provider);
        wp_send_json_success($result);
    }

    public function get_rates(string $provider): array {
        $cache_key = 'dzqcod_rates_' . $provider;
        $cached = get_transient($cache_key);
        if ($cached) return $cached;

        $rates = [];
        switch ($provider) {
            case 'yalidine':
                $from = $this->wilaya_name_to_id(Core::opt('yalidine_from_wilaya', 'Alger'));
                $r = self::api('yalidine', 'GET', 'deliveryfees/', ['from_wilaya_id' => $from]);
                if ($r['ok'] && is_array($r['data'])) $rates = $r['data'];
                break;
            case 'zr':
                $r = self::api('zr', 'GET', 'Tarifs', []);
                if ($r['ok'] && is_array($r['data'])) $rates = $r['data'];
                break;
            case 'ecotrack':
                $r = self::api('ecotrack', 'GET', 'fees', []);
                if ($r['ok'] && is_array($r['data'])) $rates = $r['data'];
                break;
            case 'noest':
                $nr = self::noest_fees();
                if (!empty($nr['delivery'])) {
                    foreach ($nr['delivery'] as $wid => $fee) {
                        $rates[] = [
                            'wilaya_id' => (int)$wid,
                            'tarif_home' => (float)($fee['tarif'] ?? 0),
                            'tarif_desk' => (float)($fee['tarif_stopdesk'] ?? 0),
                            'return_home' => (float)($nr['return'][$wid]['tarif'] ?? 0),
                            'return_desk' => (float)($nr['return'][$wid]['tarif_stopdesk'] ?? 0),
                        ];
                    }
                }
                break;
            case 'maystro':
                $r = self::api('maystro', 'GET', 'fees', []);
                if ($r['ok'] && is_array($r['data'])) {
                    foreach ($r['data'] as $f) {
                        $rates[] = [
                            'wilaya_id' => (int)($f['wilaya_id'] ?? $f['id'] ?? 0),
                            'wilaya_name' => $f['wilaya'] ?? $f['name'] ?? '',
                            'tarif_home' => (float)($f['home_fee'] ?? $f['tarif'] ?? $f['delivery_fee'] ?? 0),
                            'tarif_desk' => (float)($f['desk_fee'] ?? $f['stopdesk_fee'] ?? $f['tarif_desk'] ?? 0),
                            'return_home' => (float)($f['return_fee'] ?? 0),
                        ];
                    }
                }
                break;
            case 'procolis':
                $r = self::api('procolis', 'GET', 'Tarifs', []);
                if ($r['ok'] && is_array($r['data'])) {
                    foreach ($r['data'] as $f) {
                        $rates[] = [
                            'wilaya_id' => (int)($f['IDWilaya'] ?? $f['wilaya_id'] ?? 0),
                            'wilaya_name' => $f['Wilaya'] ?? $f['wilaya'] ?? '',
                            'tarif_home' => (float)($f['TarifDomicile'] ?? $f['tarif_home'] ?? 0),
                            'tarif_desk' => (float)($f['TarifStopDesk'] ?? $f['tarif_desk'] ?? 0),
                            'return_home' => (float)($f['TarifRetour'] ?? $f['return_fee'] ?? 0),
                        ];
                    }
                }
                break;
            case 'guepex':
                $r = self::api('guepex', 'GET', 'tarification', []);
                if ($r['ok'] && is_array($r['data'])) {
                    foreach ($r['data'] as $f) {
                        $rates[] = [
                            'wilaya_id' => (int)($f['wilaya_id'] ?? $f['id'] ?? 0),
                            'wilaya_name' => $f['wilaya'] ?? $f['nom_wilaya'] ?? '',
                            'tarif_home' => (float)($f['tarif_domicile'] ?? $f['tarif'] ?? 0),
                            'tarif_desk' => (float)($f['tarif_point_relais'] ?? $f['tarif_stopdesk'] ?? 0),
                            'return_home' => (float)($f['tarif_retour'] ?? 0),
                            'delai' => $f['delai'] ?? $f['delivery_time'] ?? '',
                        ];
                    }
                }
                break;
        }

        if (!empty($rates)) set_transient($cache_key, $rates, 6 * HOUR_IN_SECONDS);
        return $rates;
    }

    // ═══════════════════════════════════════════
    //  PRINT SHIPPING LABEL / BORDEREAU
    // ═══════════════════════════════════════════
    public function ship_label(): void {
        check_ajax_referer('dzqcod_admin', 'nonce');
        $oid = absint($_POST['order_id'] ?? 0);
        $o = wc_get_order($oid); if (!$o) wp_send_json_error();
        $tracking = $o->get_meta('_dzqcod_tracking_number');
        $provider = $o->get_meta('_dzqcod_shipping_provider') ?: 'yalidine';
        if (!$tracking) wp_send_json_error(['message' => 'لا يوجد رقم تتبع']);

        // Generate label HTML
        $label = $this->generate_label_html($o, $tracking, $provider);
        wp_send_json_success(['html' => $label]);
    }

    private function generate_label_html($order, string $tracking, string $provider): string {
        $p = self::PROVIDERS[$provider] ?? ['name' => $provider, 'icon' => '📦'];
        $items = []; foreach ($order->get_items() as $i) $items[] = $i->get_name() . ' ×' . $i->get_quantity();
        ob_start(); ?>
<div style="width:400px;border:2px solid #333;padding:16px;font-family:Arial;direction:rtl;font-size:13px">
<div style="display:flex;justify-content:space-between;border-bottom:2px solid #333;padding-bottom:8px;margin-bottom:10px">
<div><strong style="font-size:18px"><?php echo wp_kses_post($p['icon'] . ' ' . esc_html($p['name'])); ?></strong></div>
<div style="text-align:left"><strong>طلب #<?php echo (int)$order->get_id(); ?></strong><br><small><?php echo esc_html($order->get_date_created() ? $order->get_date_created()->format('Y/m/d') : ''); ?></small></div>
</div>
<div style="background:#f0f0f0;padding:10px;border-radius:6px;text-align:center;margin-bottom:10px">
<div style="font-size:10px;color:#666">رقم التتبع</div>
<div style="font-size:20px;font-weight:900;letter-spacing:2px;font-family:monospace;direction:ltr"><?php echo esc_html($tracking); ?></div>
</div>
<table style="width:100%;font-size:12px;border-collapse:collapse">
<tr><td style="padding:4px;font-weight:700;width:30%">المرسل إليه:</td><td><?php echo esc_html($order->get_billing_first_name() . ' ' . $order->get_billing_last_name()); ?></td></tr>
<tr><td style="padding:4px;font-weight:700">الهاتف:</td><td style="direction:ltr"><?php echo esc_html($order->get_billing_phone()); ?></td></tr>
<?php $ph2 = $order->get_meta('_dzqcod_phone2'); if ($ph2): ?>
<tr><td style="padding:4px;font-weight:700">هاتف 2:</td><td style="direction:ltr"><?php echo esc_html($ph2); ?></td></tr>
<?php endif; ?>
<tr><td style="padding:4px;font-weight:700">الولاية:</td><td><?php echo esc_html($order->get_shipping_state()); ?></td></tr>
<tr><td style="padding:4px;font-weight:700">البلدية:</td><td><?php echo esc_html($order->get_shipping_city()); ?></td></tr>
<tr><td style="padding:4px;font-weight:700">العنوان:</td><td><?php echo esc_html($order->get_shipping_address_1()); ?></td></tr>
<tr style="border-top:1px solid #ccc"><td style="padding:6px 4px;font-weight:700">المنتجات:</td><td><?php echo esc_html(implode(' | ', $items)); ?></td></tr>
<tr style="border-top:2px solid #333"><td style="padding:8px 4px;font-weight:900;font-size:16px">المبلغ:</td>
<td style="font-weight:900;font-size:16px"><?php echo number_format((float)$order->get_total(), 0); ?> دج</td></tr>
</table>
</div>
        <?php return ob_get_clean();
    }

    // ═══════════════════════════════════════════
    //  WEBHOOKS — Auto-update order status
    // ═══════════════════════════════════════════
    private static function status_map(string $provider, string $api_status): string {
        $maps = [
            'yalidine' => [
                'Livré' => 'wc-delivered', 'Livré(e)' => 'wc-delivered',
                'En préparation' => 'wc-ready-to-ship', 'En attente du chauffeur' => 'wc-ready-to-ship',
                'Expédié(e)' => 'wc-shipped', 'Expédié' => 'wc-shipped',
                'En transit' => 'wc-in-transit', 'Centre' => 'wc-in-transit',
                'Sorti en livraison' => 'wc-out-for-delivery',
                'Tentative échouée' => 'wc-no-answer',
                'Retour en agence' => 'wc-rto', 'Retourné' => 'wc-rto', 'Retourné au vendeur' => 'wc-rto',
                'Retour vers vendeur' => 'wc-rto',
            ],
            'zr' => [
                'LIVREE' => 'wc-delivered', 'LIVRÉ' => 'wc-delivered',
                'EN_PREPARATION' => 'wc-ready-to-ship',
                'EXPEDIEE' => 'wc-shipped', 'EXPEDIÉ' => 'wc-shipped',
                'EN_TRANSIT' => 'wc-in-transit',
                'EN_LIVRAISON' => 'wc-out-for-delivery',
                'ECHEC_LIVRAISON' => 'wc-no-answer',
                'RETOURNEE' => 'wc-rto', 'RETOUR' => 'wc-rto',
            ],
            'ecotrack' => [
                'delivered' => 'wc-delivered', 'shipped' => 'wc-shipped',
                'in_transit' => 'wc-in-transit', 'out_for_delivery' => 'wc-out-for-delivery',
                'failed' => 'wc-no-answer', 'returned' => 'wc-rto',
            ],
        ];
        return $maps[$provider][$api_status] ?? '';
    }

    /**
     * Validate webhook request — checks IP whitelist + secret token + rate limit
     */
    private function validate_webhook(string $provider): bool {
        // 1. Check webhook secret if configured
        $secret = \DZQCOD\Core::opt($provider . '_webhook_secret', '');
        if ($secret) {
            $sig = sanitize_text_field($_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? $_SERVER['HTTP_X_HUB_SIGNATURE_256'] ?? $_SERVER['HTTP_X_SIGNATURE'] ?? '');
            if (!$sig) {
                // Also check query param ?secret=
                $sig = sanitize_text_field($_GET['secret'] ?? '');
            }
            if ($sig && strpos($sig, 'sha256=') === 0) {
                $body = WP_REST_Server::get_raw_data();
                $expected = 'sha256=' . hash_hmac('sha256', $body, $secret);
                if (!hash_equals($expected, $sig)) return false;
            } elseif ($sig !== $secret) {
                return false;
            }
        }
        // 2. Rate limit: max 100 webhooks per provider per hour
        $rk = 'dzqcod_wh_' . $provider . '_' . gmdate('YmdH');
        $cnt = (int)get_transient($rk);
        if ($cnt >= 100) return false;
        set_transient($rk, $cnt + 1, HOUR_IN_SECONDS);
        // 3. Log webhook
        if (defined('WP_DEBUG') && WP_DEBUG) {
            error_log("DZQCOD webhook [{$provider}] from " . sanitize_text_field($_SERVER['REMOTE_ADDR'] ?? ''));
        }
        return true;
    }

    public function yalidine_webhook(): void {
        if (!$this->validate_webhook('yalidine')) wp_die('Unauthorized', '', ['response' => 403]);
        $data = json_decode(WP_REST_Server::get_raw_data(), true);
        if (!$data) wp_die('', '', ['response' => 400]);
        $order_id = $data['order_id'] ?? ($data['data']['order_id'] ?? '');
        $tracking = $data['tracking'] ?? ($data['data']['tracking'] ?? '');
        $status = $data['new_status'] ?? ($data['data']['last_status'] ?? '');
        $this->process_webhook('yalidine', $order_id, $tracking, $status);
        wp_die('OK', '', ['response' => 200]);
    }

    public function zr_webhook(): void {
        if (!$this->validate_webhook('zr')) wp_die('Unauthorized', '', ['response' => 403]);
        $data = json_decode(WP_REST_Server::get_raw_data(), true);
        if (!$data) wp_die('', '', ['response' => 400]);
        $this->process_webhook('zr', $data['NumCommande'] ?? '', $data['Tracking'] ?? '', $data['Status'] ?? '');
        wp_die('OK', '', ['response' => 200]);
    }

    public function ecotrack_webhook(): void {
        if (!$this->validate_webhook('ecotrack')) wp_die('Unauthorized', '', ['response' => 403]);
        $data = json_decode(WP_REST_Server::get_raw_data(), true);
        if (!$data) wp_die('', '', ['response' => 400]);
        $this->process_webhook('ecotrack', $data['order_id'] ?? '', $data['tracking'] ?? '', $data['status'] ?? '');
        wp_die('OK', '', ['response' => 200]);
    }
    public function maystro_webhook(): void {
        if (!$this->validate_webhook('maystro')) wp_die('Unauthorized', '', ['response' => 403]);
        $data = json_decode(WP_REST_Server::get_raw_data(), true);
        if (!$data) wp_die('', '', ['response' => 400]);
        $this->process_webhook('maystro', $data['reference'] ?? $data['order_id'] ?? '', $data['tracking'] ?? $data['code'] ?? '', $data['status'] ?? $data['state'] ?? '');
        wp_die('OK', '', ['response' => 200]);
    }
    public function procolis_webhook(): void {
        if (!$this->validate_webhook('procolis')) wp_die('Unauthorized', '', ['response' => 403]);
        $data = json_decode(WP_REST_Server::get_raw_data(), true);
        if (!$data) wp_die('', '', ['response' => 400]);
        $this->process_webhook('procolis', $data['NumCommande'] ?? $data['order_id'] ?? '', $data['Tracking'] ?? $data['tracking'] ?? '', $data['Status'] ?? $data['status'] ?? '');
        wp_die('OK', '', ['response' => 200]);
    }
    public function guepex_webhook(): void {
        if (!$this->validate_webhook('guepex')) wp_die('Unauthorized', '', ['response' => 403]);
        $data = json_decode(WP_REST_Server::get_raw_data(), true);
        if (!$data) wp_die('', '', ['response' => 400]);
        $this->process_webhook('guepex', $data['order_id'] ?? '', $data['code_suivi'] ?? $data['tracking'] ?? '', $data['statut'] ?? $data['status'] ?? '');
        wp_die('OK', '', ['response' => 200]);
    }

    private function process_webhook(string $provider, string $order_id, string $tracking, string $api_status): void {
        // Find order by order_id or tracking
        $o = null;
        if ($order_id && is_numeric($order_id)) $o = wc_get_order(absint($order_id));
        if (!$o && $tracking) {
            $orders = wc_get_orders(['meta_key' => '_dzqcod_tracking_number', 'meta_value' => $tracking, 'limit' => 1]);
            $o = $orders[0] ?? null;
        }
        if (!$o) return;

        $new_status = self::status_map($provider, $api_status);
        if ($new_status && 'wc-' . $o->get_status() !== $new_status) {
            $o->set_status($new_status);
            $o->add_order_note(sprintf('🔄 %s Webhook: %s → %s', self::PROVIDERS[$provider]['name'], $api_status, $new_status));
            $o->save();
        }
    }

    // ═══════════════════════════════════════════
    //  CRON: Auto-track shipped orders
    // ═══════════════════════════════════════════
    public function cron_track_all(): void {
        $shipped = wc_get_orders(['status' => ['wc-shipped', 'wc-in-transit', 'wc-out-for-delivery'], 'limit' => 50,
            'meta_key' => '_dzqcod_tracking_number', 'orderby' => 'date', 'order' => 'ASC']);

        foreach ($shipped as $o) {
            $tracking = $o->get_meta('_dzqcod_tracking_number');
            $provider = $o->get_meta('_dzqcod_shipping_provider') ?: 'yalidine';
            if (!$tracking) continue;

            $result = $this->track_parcel($tracking, $provider);
            $new_status = self::status_map($provider, $result['status'] ?? '');
            if ($new_status && 'wc-' . $o->get_status() !== $new_status) {
                $o->set_status($new_status);
                $o->add_order_note(sprintf('🔄 Auto-Track %s: %s', self::PROVIDERS[$provider]['name'], $result['status']));
                $o->save();
            }
        }
    }

    // ═══════════════════════════════════════════
    //  CONFIG: Save shipping settings
    // ═══════════════════════════════════════════
    // ═══ NOEST: Sync real fees to wilayas table ═══
    public function ship_sync_noest_fees(): void {
        check_ajax_referer('dzqcod_admin', 'nonce');
        if (!current_user_can('manage_options')) wp_send_json_error();
        $tarifs = self::noest_fees();
        if (empty($tarifs['delivery'])) wp_send_json_error(['message' => 'فشل جلب الأسعار من NOEST']);
        global $wpdb;
        $table = Core::db() . 'wilayas';
        $updated = 0;
        foreach ($tarifs['delivery'] as $wid => $fee) {
            $home = (float)($fee['tarif'] ?? 0);
            $desk = (float)($fee['tarif_stopdesk'] ?? 0);
            $done = $wpdb->update($table, ['shipping_home' => $home, 'shipping_desk' => $desk], ['code' => str_pad($wid, 2, '0', STR_PAD_LEFT)]);
            if ($done === false) $wpdb->update($table, ['shipping_home' => $home, 'shipping_desk' => $desk], ['code' => (string)(int)$wid]);
            $updated++;
        }
        wp_send_json_success(['message' => '✅ تم تحديث أسعار ' . $updated . ' ولاية من NOEST']);
    }

    public function ship_save_config(): void {
        check_ajax_referer('dzqcod_admin', 'nonce');
        if (!current_user_can('manage_options')) wp_send_json_error();

        $keys = ['shipping_provider', 'yalidine_api_id', 'yalidine_api_token', 'yalidine_from_wilaya',
                 'zr_api_token', 'zr_session_id', 'ecotrack_api_token',
                 'noest_user_guid', 'noest_api_token', 'noest_can_open', 'noest_auto_validate'];
        foreach ($keys as $k) {
            if (isset($_POST[$k])) update_option('dzqcod_' . $k, sanitize_text_field($_POST[$k]), false);
        }
        wp_send_json_success(['message' => 'تم حفظ إعدادات الشحن ✅']);
    }

    // ═══════════════════════════════════════════
    //  TEST CONNECTION
    // ═══════════════════════════════════════════
    public function ship_test_connection(): void {
        check_ajax_referer('dzqcod_admin', 'nonce');
        $provider = sanitize_key($_POST['provider'] ?? 'yalidine');

        switch ($provider) {
            case 'yalidine':
                $r = self::api('yalidine', 'GET', 'wilayas/');
                break;
            case 'zr':
                $r = self::api('zr', 'GET', 'Tarifs', []);
                break;
            case 'ecotrack':
                $r = self::api('ecotrack', 'GET', 'fees', []);
                break;
            case 'noest':
                $nr = self::noest_api('GET', 'get/wilayas');
                $r = ['ok' => is_array($nr) && !empty($nr) && !isset($nr['message'])];
                if (!$r['ok']) $r['error'] = $nr['message'] ?? 'فشل الاتصال';
                break;
            case 'maystro':
                $r = self::api('maystro', 'GET', 'fees', []);
                break;
            case 'procolis':
                $r = self::api('procolis', 'GET', 'Tarifs', []);
                break;
            case 'guepex':
                $r = self::api('guepex', 'GET', 'tarification', []);
                break;
            default:
                wp_send_json_error(['message' => 'مزود غير معروف']);
                return;
        }

        if ($r['ok']) {
            wp_send_json_success(['message' => '✅ الاتصال ناجح مع ' . (self::PROVIDERS[$provider]['name'] ?? $provider)]);
        } else {
            wp_send_json_error(['message' => '❌ فشل الاتصال: ' . ($r['error'] ?? 'خطأ غير معروف'), 'code' => $r['code'] ?? 0]);
        }
    }

    // ═══════════════════════════════════════════
    //  HELPERS
    // ═══════════════════════════════════════════
    private function wilaya_name_to_id(string $name): int {
        global $wpdb;
        $t = $wpdb->prefix . \DZQCOD_DB . 'wilayas';
        $row = $wpdb->get_row($wpdb->prepare(
            "SELECT code FROM {$t} WHERE name_ar=%s OR name_fr=%s OR code=%s LIMIT 1",
            $name, $name, $name
        ));
        return $row ? (int)$row->code : 16; // default Alger
    }

    public static function get_provider_name(string $slug): string {
        return self::PROVIDERS[$slug]['name'] ?? $slug;
    }

    public static function get_providers(): array {
        return self::PROVIDERS;
    }
}
