<?php

namespace Core\App\Services;

use Core\App\Models\OrderModel;
use Core\App\Models\ProductModel;
use Core\App\Models\ShippingIntegrationModel;
use Core\App\Models\OrderLifecycleEventModel;
use Core\App\Support\ShippingSettings;
use Core\App\Support\OrderPricing;
use Core\App\Support\DzLocation;
use Core\App\Integrations\Shipping\EcotrackClient;

/**
 * OrderShippingService
 *
 * Encapsulates all carrier-specific shipment creation logic:
 * Ecotrack, Yalidine, ZR Express, Maystro, DHD.
 *
 * Each sendTo* method returns:
 *   ['success' => true]
 *   ['success' => false, 'alert' => 'danger'|'warning', 'message' => string]
 *
 * refreshEcotrackStatus returns the same shape with an extra 'message' on success.
 */
class OrderShippingService
{
    public function __construct(private OrderModel $orders) {}

    /* =====================================================================
       PUBLIC API
    ===================================================================== */

    public function sendToEcotrack(int $id, array $post): array
    {
        $order = $this->orders->getById($id);
        if (!$order) {
            return $this->err('danger', 'Order not found');
        }

        $extra = $this->parseExtra($order);

        if (!empty($extra['shipping']['tracking'])) {
            return $this->err('warning', __('shipping.already_sent', 'Shipment already created'));
        }

        if (($post['shipping_provider'] ?? '') !== 'ecotrack') {
            return $this->err('danger', 'مزود شحن غير صحيح');
        }

        $shippingType = $extra['shipping']['type'] ?? null;
        if (!in_array($shippingType, ['home', 'desk'], true)) {
            return $this->err('warning', 'نوع الشحن غير صالح');
        }

        $adresse    = trim($post['adresse'] ?? '');
        $codeWilaya = (int)($post['code_wilaya'] ?? 0);
        $montant    = (float)($post['montant'] ?? 0);
        $type       = trim($post['type'] ?? '');
        $commune    = trim($post['commune'] ?? '');

        if ($adresse === '' || $codeWilaya <= 0 || $montant <= 0 || $type === '' || $commune === '') {
            return $this->err('warning', 'بيانات الشحن ناقصة أو غير صحيحة (العنوان / الولاية / البلدية)');
        }

        $integration = (new ShippingIntegrationModel())->getByProvider('ecotrack');
        if (!$integration || empty($integration['api_token']) || empty($integration['api_id']) || empty($integration['is_active'])) {
            return $this->err('danger', 'تكامل Ecotrack غير مفعّل أو غير مضبوط');
        }

        $phone = preg_replace('/\D+/', '', $order['phone']);
        if (strpos($phone, '213') === 0) $phone = substr($phone, 3);
        if (strpos($phone, '0')   === 0) $phone = substr($phone, 1);

        $stopDesk = ($shippingType === 'desk') ? 1 : 0;

        $label = trim((string)($extra['product']['name'] ?? ''));
        $variantBits = [];
        if (!empty($extra['variants']['size']))  $variantBits[] = 'Taille: '  . $extra['variants']['size'];
        if (!empty($extra['variants']['color'])) $variantBits[] = 'Couleur: ' . $extra['variants']['color'];
        if (!empty($variantBits)) $label .= ($label !== '' ? ' - ' : '') . implode(' | ', $variantBits);
        if (!empty($extra['quantity']) && (int)$extra['quantity'] > 1) {
            $label .= ($label !== '' ? ' - ' : '') . 'Qte: ' . (int)$extra['quantity'];
        }
        if ($label === '') $label = 'Commande #' . $id;

        $payload = [
            'nom_client'  => trim($order['full_name']),
            'telephone'   => $phone,
            'adresse'     => $adresse,
            'commune'     => $commune,
            'code_wilaya' => $codeWilaya,
            'montant'     => $montant,
            'type'        => $type,
            'stop_desk'   => $stopDesk,
            'reference'   => (string)$order['id'],
            'produit'     => $label,
        ];

        try {
            $client   = new EcotrackClient($integration['api_token'], $integration['api_id']);
            $response = $client->createOrderSingle($payload);

            $extra['shipping']['provider'] = 'ecotrack';
            $extra['shipping']['status']   = $response['success'] ? 'sent' : 'error';
            $extra['shipping']['response'] = $response;

            if ($response['success'] && !empty($response['data']['tracking'])) {
                $extra['shipping']['tracking'] = $response['data']['tracking'];

                try {
                    $validateResponse = $client->validateOrder($extra['shipping']['tracking'], true);
                    $extra['shipping']['validated']          = !empty($validateResponse['success']);
                    $extra['shipping']['ask_collection']     = 1;
                    $extra['shipping']['validated_at']       = date('Y-m-d H:i:s');
                    $extra['shipping']['validate_response']  = $validateResponse;
                    if (!empty($validateResponse['success'])) {
                        $extra['shipping']['status'] = 'validated';
                    }
                } catch (\Throwable $e) {
                    $extra['shipping']['validated']     = false;
                    $extra['shipping']['validate_error'] = $e->getMessage();
                }
            }

            $ctx = $this->currentAdminContext();
            $extra['shipping']['processed_by_admin_id']   = $ctx['admin_id'] ?: null;
            $extra['shipping']['processed_by_admin_name'] = $ctx['admin_name'];
            $extra['shipping']['processed_by_admin_role'] = $ctx['admin_role'];
            $extra['shipping']['processed_at']            = date('Y-m-d H:i:s');

            // Persist negotiated price if amount changed
            $negotiatedNote  = trim($post['negotiated_note'] ?? '');
            $originalAmount  = OrderPricing::effectiveTotal($order);
            $db = \db();
            if ($montant > 0 && abs($montant - $originalAmount) > 0.01) {
                $extra = OrderPricing::applyNegotiatedTotal($order, $extra, $montant, $negotiatedNote);
                $db->prepare("UPDATE orders SET total_amount = ? WHERE id = ?")->execute([$montant, $id]);
                $order['total_amount'] = $montant;
            }

            $db->prepare("UPDATE orders SET extra = :extra WHERE id = :id")
               ->execute([':extra' => json_encode($extra, JSON_UNESCAPED_UNICODE), ':id' => $id]);

            if (!empty($extra['shipping']['tracking'])) {
                $this->postShipmentActions($id, $extra, 'ecotrack');
            }

            return ['success' => true];

        } catch (\Throwable $e) {
            return $this->err('danger', 'حدث خطأ أثناء إرسال الطلب إلى Ecotrack');
        }
    }

    public function sendToYalidine(int $id, array $post): array
    {
        $order = $this->orders->getById($id);
        if (!$order) {
            return $this->err('danger', __('orders.not_found', 'Order not found'));
        }

        $extra = $this->parseExtra($order);

        if (!empty($extra['shipping']['tracking'])) {
            return $this->err('warning', __('shipping.already_sent', 'Shipment already created'));
        }

        if (($post['shipping_provider'] ?? 'yalidine') !== 'yalidine') {
            return $this->err('danger', __('shipping.invalid_provider', 'Invalid shipping provider'));
        }

        $integration = (new ShippingIntegrationModel())->getByProvider('yalidine');
        if (!$integration || empty($integration['api_token']) || empty($integration['is_active'])) {
            return $this->err('danger', __('shipping.integration_disabled', 'Yalidine integration is not configured or disabled'));
        }

        $nameParts  = explode(' ', trim($order['full_name']), 2);
        $firstName  = $nameParts[0] ?? $order['full_name'];
        $familyName = $nameParts[1] ?? '';

        $isStopDesk  = ($extra['shipping']['type'] ?? '') === 'desk';
        $toWilayaId  = (int)($post['to_wilaya_id']  ?? 0);
        $toCommuneId = (int)($post['to_commune_id'] ?? 0);
        $stopdeskId  = (int)($post['stopdesk_id']   ?? 0);

        $shippingSettings = ShippingSettings::load();
        $originStateCode  = (string)($shippingSettings['origin']['state_code'] ?? '16');
        $originStateId    = (int)ltrim($originStateCode, '0');
        if ($originStateId <= 0) $originStateId = 16;

        if ($toWilayaId <= 0 || $toCommuneId <= 0) {
            return $this->err('warning', __('shipping.missing_fields', 'Missing required shipment data'));
        }

        $payload = [
            'firstname'      => $firstName,
            'familyname'     => $familyName,
            'contact_phone'  => trim($order['phone']),
            'address'        => trim($post['address'] ?? $order['address'] ?? ''),
            'from_wilaya_id' => (int)($post['from_wilaya_id'] ?? $originStateId),
            'to_wilaya_id'   => $toWilayaId,
            'to_commune_id'  => $toCommuneId,
            'product_list'   => trim($post['product_list'] ?? $extra['product']['name'] ?? 'colis'),
            'price'          => (float)($post['amount'] ?? $order['total_amount'] ?? 0),
            'do_insurance'   => 0,
            'declared_value' => (float)($post['amount'] ?? $order['total_amount'] ?? 0),
            'length'         => (float)($post['length'] ?? 1),
            'width'          => (float)($post['width']  ?? 1),
            'height'         => (float)($post['height'] ?? 1),
            'weight'         => (float)($post['weight'] ?? 0.5),
            'freeshipping'   => 0,
            'is_stopdesk'    => $isStopDesk ? 1 : 0,
            'has_exchange'   => 0,
            'note'           => trim($post['note'] ?? ''),
            'reference'      => (string)$order['id'],
        ];

        if ($isStopDesk && $stopdeskId > 0) {
            $payload['stopdesk_id'] = $stopdeskId;
        }

        $client   = new \Core\App\Integrations\Shipping\YalidineClient(
            $integration['api_token'],
            $integration['api_id'] ?? ''
        );
        $response = $client->createParcel($payload);

        $extra['shipping']['provider'] = 'yalidine';
        $extra['shipping']['status']   = $response['success'] ? 'sent' : 'error';
        $extra['shipping']['response'] = $response;

        if ($response['success'] && isset($response['data']['data'][0]['tracking'])) {
            $extra['shipping']['tracking'] = $response['data']['data'][0]['tracking'];
        } elseif ($response['success'] && isset($response['data']['tracking'])) {
            $extra['shipping']['tracking'] = $response['data']['tracking'];
        }

        $db = \db();

        $yalidineAmount   = (float)($post['amount'] ?? $order['total_amount'] ?? 0);
        $yalidineOriginal = OrderPricing::effectiveTotal($order);
        $yalidineNote     = trim($post['negotiated_note'] ?? '');
        if ($yalidineAmount > 0 && abs($yalidineAmount - $yalidineOriginal) > 0.01) {
            $extra = OrderPricing::applyNegotiatedTotal($order, $extra, $yalidineAmount, $yalidineNote);
            $db->prepare("UPDATE orders SET total_amount = ? WHERE id = ?")->execute([$yalidineAmount, $id]);
            $order['total_amount'] = $yalidineAmount;
        }

        $db->prepare("UPDATE orders SET extra = :extra WHERE id = :id")
           ->execute([':extra' => json_encode($extra, JSON_UNESCAPED_UNICODE), ':id' => $id]);

        if (!empty($extra['shipping']['tracking'])) {
            $this->postShipmentActions($id, $extra, 'yalidine');
        }

        return ['success' => true];
    }

    public function sendToZrexpress(int $id, array $post): array
    {
        $order = $this->orders->getById($id);
        if (!$order) {
            return $this->err('danger', __('orders.not_found', 'Order not found'));
        }

        $extra = $this->parseExtra($order);

        if (!empty($extra['shipping']['tracking'])) {
            return $this->err('warning', __('shipping.already_sent', 'Shipment already created'));
        }

        if (($post['shipping_provider'] ?? '') !== 'zr_express') {
            return $this->err('danger', __('shipping.invalid_provider', 'Invalid shipping provider'));
        }

        $integration = (new ShippingIntegrationModel())->getByProvider('zr_express');
        if (!$integration || empty($integration['api_id']) || empty($integration['api_token']) || empty($integration['is_active'])) {
            return $this->err('danger', __('shipping.integration_disabled', 'Shipping integration is disabled'));
        }

        $address = trim($post['address'] ?? '');
        $amount  = (float)($post['amount'] ?? 0);
        $weight  = (float)($post['weight'] ?? 0);

        if ($address === '' || $amount <= 0 || $weight <= 0) {
            return $this->err('warning', __('shipping.missing_fields', 'Missing required shipment data'));
        }

        $payload = [
            'nom_client' => $order['full_name'],
            'telephone'  => $order['phone'],
            'wilaya'     => $extra['shipping']['destination']['state_code'] ?? '',
            'commune'    => $extra['shipping']['destination']['city_name']  ?? '',
            'adresse'    => $address,
            'montant'    => $amount,
            'poids'      => $weight,
            'remarque'   => trim($post['note'] ?? ''),
            'produit'    => (string)$order['id'],
        ];

        try {
            $baseUrl = rtrim($integration['api_id'], '/');
            $headers = [
                'Authorization: Bearer ' . $integration['api_token'],
                'Accept: application/json',
                'Content-Type: application/json',
            ];

            $ch = curl_init($baseUrl . '/add_colis');
            curl_setopt_array($ch, [
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_POST           => true,
                CURLOPT_HTTPHEADER     => $headers,
                CURLOPT_POSTFIELDS     => json_encode($payload, JSON_UNESCAPED_UNICODE),
            ]);
            $responseRaw = curl_exec($ch);
            curl_close($ch);

            $response = json_decode((string)$responseRaw, true);

            if (empty($response['tracking'])) {
                throw new \RuntimeException('ZR EXPRESS create failed');
            }

            $tracking = $response['tracking'];

            $ch = curl_init($baseUrl . '/pret');
            curl_setopt_array($ch, [
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_POST           => true,
                CURLOPT_HTTPHEADER     => $headers,
                CURLOPT_POSTFIELDS     => json_encode(['tracking' => $tracking]),
            ]);
            curl_exec($ch);
            curl_close($ch);

            $extra['shipping']['provider'] = 'zr_express';
            $extra['shipping']['tracking'] = $tracking;
            $extra['shipping']['status']   = 'sent';
            $extra['shipping']['response'] = $response;

            $this->orders->updateExtra($id, $extra);
            $this->postShipmentActions($id, $extra, 'zr_express');

            return ['success' => true];

        } catch (\Throwable $e) {
            return $this->err('danger', __('shipping.send_failed', 'Failed to send shipment'));
        }
    }

    public function sendToMaystro(int $id, array $post): array
    {
        $order = $this->orders->getById($id);
        if (!$order) {
            return $this->err('danger', __('orders.not_found', 'Order not found'));
        }

        $extra = $this->parseExtra($order);

        if (!empty($extra['shipping']['tracking'])) {
            return $this->err('warning', __('shipping.already_sent', 'Shipment already created'));
        }

        $postedProvider = $post['shipping_provider'] ?? 'maystro';
        if (!in_array($postedProvider, ['maystro', ''], true) && $postedProvider !== 'maystro') {
            return $this->err('danger', __('shipping.invalid_provider', 'Invalid shipping provider'));
        }

        $integration = (new ShippingIntegrationModel())->getByProvider('maystro');
        if (!$integration || empty($integration['api_token']) || empty($integration['is_active'])) {
            return $this->err('danger', __('shipping.integration_disabled', 'Maystro integration is not configured'));
        }

        $destination   = $extra['shipping']['destination'] ?? [];
        $shippingType  = $extra['shipping']['type'] ?? 'home';
        $postedCustomerName  = trim((string)($post['customer_name']  ?? ($order['full_name'] ?? '')));
        $postedCustomerPhone = trim((string)($post['customer_phone'] ?? ($order['phone'] ?? '')));

        $deliveryTypeRaw = $post['delivery_type'] ?? null;
        if ($deliveryTypeRaw !== null && in_array((int)$deliveryTypeRaw, [1, 2, 3], true)) {
            $deliveryTypeInt = (int)$deliveryTypeRaw;
        } else {
            $typeMap = ['home' => 1, 'desk' => 2, 'pickup' => 3];
            $deliveryTypeInt = $typeMap[$shippingType] ?? 1;
        }

        $maystroAmount   = (float)($post['amount'] ?? OrderPricing::effectiveTotal($order));
        $maystroOriginal = OrderPricing::effectiveTotal($order);
        $maystroNote     = trim($post['negotiated_note'] ?? '');

        $description = trim($post['description'] ?? $extra['product']['name'] ?? ('Commande #' . $id));
        $quantity    = max(1, (int)($post['quantity'] ?? $extra['quantity'] ?? 1));
        $productId   = trim($post['product_id'] ?? '');

        $postedStateCode = DzLocation::normalizeCode($post['state_code'] ?? ($destination['state_code'] ?? ''));
        $postedWilayaId  = (int)($post['wilaya_id'] ?? 0);
        if ($postedStateCode === '' && $postedWilayaId > 0) {
            $postedStateCode = DzLocation::normalizeCode((string)$postedWilayaId);
        }

        $stateName = trim((string)($post['state_name'] ?? ($destination['state_name'] ?? '')));
        if ($stateName === '' && $postedStateCode !== '') {
            $stateData = DzLocation::stateByCode($postedStateCode);
            $stateName = $stateData['name'] ?? '';
        }

        $communeName  = trim((string)($post['commune_name'] ?? ($destination['city_name'] ?? '')));
        $communeValue = (int)($post['commune_id'] ?? ($destination['city_id'] ?? 0));
        if ($communeName === '' && $communeValue > 0) {
            $cityData    = DzLocation::findCityById($postedStateCode, $communeValue);
            $communeName = $cityData['name'] ?? '';
        }
        if ($communeValue <= 0) $communeValue = $communeName;

        $address = trim($post['address'] ?? ($stateName . ($communeName ? ' - ' . $communeName : '')));

        $payload = [
            'external_id'      => (string)$id,
            'customer_name'    => $postedCustomerName,
            'customer_phone'   => $postedCustomerPhone,
            'destination_text' => $address !== '' ? $address : (string)($order['address'] ?? ''),
            'total_price'      => (int)round($maystroAmount),
            'delivery_type'    => $deliveryTypeInt,
            'commune'          => $communeValue,
            'express'          => !empty($post['express']),
            'note_to_driver'   => trim($post['note'] ?? '') ?: null,
            'details'          => [[
                'product'     => $productId !== '' ? $productId : '',
                'description' => $description !== '' ? $description : ('Commande #' . $id),
                'quantity'    => max(1, $quantity),
            ]],
        ];

        if (is_string($communeValue) && $stateName !== '') {
            $payload['wilaya'] = $stateName;
        }
        if (!empty($post['customer_phone2'])) {
            $payload['customer_phone2'] = trim((string)$post['customer_phone2']);
        }
        if ($deliveryTypeInt === 3 && !empty($post['pickup_point'])) {
            $payload['pickup_point'] = (int)$post['pickup_point'];
        }

        $extra['shipping']['destination'] = array_merge((array)$destination, [
            'state_code' => $postedStateCode,
            'state_name' => $stateName,
            'city_id'    => is_int($communeValue) ? $communeValue : ((is_string($communeValue) && ctype_digit($communeValue)) ? (int)$communeValue : (int)($destination['city_id'] ?? 0)),
            'city_name'  => $communeName,
        ]);
        $extra['shipping']['type'] = [1 => 'home', 2 => 'desk', 3 => 'pickup'][$deliveryTypeInt] ?? 'home';

        $client   = \Core\App\Integrations\Shipping\MaystroClient::fromIntegration($integration);
        $response = $client->createOrder($payload);

        $extra['shipping']['debug_payload'] = $payload;

        $resultList = $response['data'] ?? [];
        $result     = is_array($resultList) && isset($resultList[0]) ? $resultList[0] : null;
        $apiSuccess = !empty($response['success']) && !empty($result['success']);

        $extra['shipping']['provider']    = 'maystro';
        $extra['shipping']['status']      = $apiSuccess ? 'CREATED' : 'error';
        $extra['shipping']['status_code'] = $apiSuccess ? 4 : ($extra['shipping']['status_code'] ?? null);
        $extra['shipping']['response']    = $result ?? $response;

        if ($apiSuccess) {
            $extra['shipping']['tracking']           = $result['tracking']       ?? null;
            $extra['shipping']['maystro_id']         = $result['id']             ?? null;
            $extra['shipping']['maystro_display_id'] = $this->findMaystroDisplayId(is_array($result) ? $result : []);
            $extra['shipping']['delivery_price']     = $result['delivery_price'] ?? null;
            $extra['shipping']['created_at']         = date('Y-m-d H:i:s');
            $extra['shipping']['last_update_at']     = $extra['shipping']['created_at'];
            $extra['shipping']['tracking_lifecycle'] = [[
                'status'      => 'CREATED',
                'status_code' => 4,
                'date'        => date('Y-m-d'),
                'time'        => date('H:i:s'),
            ]];

            if (empty($extra['shipping']['maystro_display_id']) && !empty($extra['shipping']['tracking'])) {
                try {
                    $trackRes = $client->getOrder((string)$extra['shipping']['tracking']);
                    if (!empty($trackRes['success']) && is_array($trackRes['data'] ?? null)) {
                        $extra['shipping']['maystro_display_id'] = $this->findMaystroDisplayId($trackRes['data']);
                        $extra['shipping']['response']           = $trackRes['data'];
                    }
                } catch (\Throwable $e) {}
            }

            unset($extra['shipping']['debug_payload'], $extra['shipping']['error_codes'], $extra['shipping']['error_msgs'], $extra['shipping']['raw_body']);
        } else {
            $errorCodes = (array)($result['errors'] ?? []);
            $extra['shipping']['error_codes'] = $errorCodes;
            $extra['shipping']['error_msgs']  = \Core\App\Integrations\Shipping\MaystroClient::decodeErrors($errorCodes);
            if (!empty($response['raw_body'])) {
                $extra['shipping']['raw_body'] = $response['raw_body'];
            }
        }

        $db = \db();
        if ($maystroAmount > 0 && abs($maystroAmount - $maystroOriginal) > 0.01) {
            $extra = OrderPricing::applyNegotiatedTotal($order, $extra, $maystroAmount, $maystroNote);
            $db->prepare("UPDATE orders SET total_amount = ? WHERE id = ?")->execute([$maystroAmount, $id]);
        }

        $db->prepare("UPDATE orders SET extra = :extra WHERE id = :id")
           ->execute([':extra' => json_encode($extra, JSON_UNESCAPED_UNICODE), ':id' => $id]);

        if ($apiSuccess) {
            $this->postShipmentActions($id, $extra, 'maystro');
        }

        return ['success' => true];
    }

    public function sendToDhd(int $id, array $post): array
    {
        $order = $this->orders->getById($id);
        if (!$order) {
            return $this->err('danger', __('orders.not_found'));
        }

        $extra = $this->parseExtra($order);

        if (!empty($extra['shipping']['tracking'])) {
            return $this->err('warning', __('shipping.already_sent'));
        }

        $integration = (new ShippingIntegrationModel())->getByProvider('dhd');
        if (!$integration || empty($integration['api_token']) || empty($integration['is_active'])) {
            return $this->err('danger', __('shipping.integration_disabled'));
        }

        $shippingType = $extra['shipping']['type'] ?? null;
        $adresse      = trim($post['adresse']      ?? '');
        $codeWilaya   = (int)($post['code_wilaya'] ?? 0);
        $montant      = (float)($post['montant']   ?? 0);
        $type         = trim($post['type']         ?? '');
        $commune      = trim($post['commune']      ?? '');

        if ($adresse === '' || $codeWilaya <= 0 || $montant <= 0 || $type === '' || $commune === '') {
            return $this->err('warning', __('shipping.missing_fields'));
        }

        $phone = preg_replace('/\D+/', '', $order['phone']);
        if (str_starts_with($phone, '213')) $phone = substr($phone, 3);
        if (str_starts_with($phone, '0'))   $phone = substr($phone, 1);

        $payload = [
            'nom_client'  => trim($order['full_name']),
            'telephone'   => $phone,
            'adresse'     => $adresse,
            'commune'     => $commune,
            'code_wilaya' => $codeWilaya,
            'montant'     => $montant,
            'type'        => $type,
            'stop_desk'   => ($shippingType === 'desk') ? 1 : 0,
            'reference'   => (string)$order['id'],
        ];

        try {
            $client   = new \Core\App\Integrations\Shipping\DhdClient(
                $integration['api_token'],
                $integration['api_id'] ?? ''
            );
            $response = $client->createOrderSingle($payload);

            $extra['shipping']['provider'] = 'dhd';
            $extra['shipping']['status']   = $response['success'] ? 'sent' : 'error';
            $extra['shipping']['response'] = $response;

            if ($response['success'] && !empty($response['data']['tracking'])) {
                $extra['shipping']['tracking'] = $response['data']['tracking'];

                try {
                    $validateResponse = $client->validateOrder($extra['shipping']['tracking'], true);
                    if (!empty($validateResponse['success'])) {
                        $extra['shipping']['status']    = 'validated';
                        $extra['shipping']['validated'] = true;
                    }
                } catch (\Throwable $e) {}
            }

            \db()->prepare("UPDATE orders SET extra = :extra WHERE id = :id")
                 ->execute([':extra' => json_encode($extra, JSON_UNESCAPED_UNICODE), ':id' => $id]);

            if (!empty($extra['shipping']['tracking'])) {
                $this->postShipmentActions($id, $extra, 'dhd');
            }

            return ['success' => true];

        } catch (\Throwable $e) {
            return $this->err('danger', __('shipping.send_failed'));
        }
    }

    /**
     * Refresh Ecotrack status for an order.
     * Returns ['success' => bool, 'alert' => string, 'message' => string]
     */
    public function refreshEcotrackStatus(int $id): array
    {
        $order = $this->orders->getById($id);
        if (!$order) {
            return $this->err('danger', __('orders.not_found'));
        }

        $extra = $this->parseExtra($order);

        if (
            empty($extra['shipping']['provider']) ||
            $extra['shipping']['provider'] !== 'ecotrack' ||
            empty($extra['shipping']['tracking'])
        ) {
            return $this->err('warning', __('shipping.invalid_shipment'));
        }

        $integration = (new ShippingIntegrationModel())->getByProvider('ecotrack');
        if (!$integration || empty($integration['api_token']) || empty($integration['api_id']) || empty($integration['is_active'])) {
            return $this->err('danger', __('shipping.integration_disabled'));
        }

        try {
            $client   = new \Core\App\Integrations\Shipping\EcotrackClient($integration['api_token'], $integration['api_id']);
            $response = $client->getOrderStatusSingle($extra['shipping']['tracking']);

            if (empty($response['success']) || empty($response['data']) || empty($response['data']['status'])) {
                return $this->err('warning', __('shipping.not_ready_yet'));
            }

            $extra['shipping']['status']            = $response['data']['status'];
            $extra['shipping']['response']          = $response['data'];
            $extra['shipping']['last_update_at']    = date('Y-m-d H:i:s');
            $extra['shipping']['manual_refresh_at'] = date('Y-m-d H:i:s');

            $this->orders->updateExtra($id, $extra);

            // WhatsApp — Status Updated
            try {
                $waService = \Core\App\Services\WhatsAppService::fromSettings();
                if ($waService && $waService->isEnabled('status_updated')) {
                    $freshOrder = $this->orders->getById($id);
                    $waCfg = (function() {
                        $stmt = \db()->prepare("SELECT config FROM tracking_integrations WHERE provider='whatsapp' LIMIT 1");
                        $stmt->execute();
                        return json_decode($stmt->fetchColumn() ?: '{}', true) ?: [];
                    })();
                    $tpl = $waCfg['templates']['status_updated'] ?? '';
                    if ($tpl && $freshOrder) {
                        $msg = \Core\App\Services\WhatsAppService::buildMessage($tpl, $freshOrder);
                        $waService->send($freshOrder['phone'], $msg, [
                            'event'    => 'status_updated',
                            'order_id' => (int)$freshOrder['id'],
                            'type'     => 'auto',
                        ]);
                    }
                }
            } catch (\Throwable $e) {}

            return ['success' => true, 'alert' => 'success', 'message' => __('shipping.status_updated')];

        } catch (\Throwable $e) {
            return $this->err('danger', __('shipping.tracking_failed'));
        }
    }

    /**
     * Restore stock for an order. Called by the controller for returns/rejections.
     */
    public function restoreStockForOrder(array $order, string $reason = 'restore'): void
    {
        $extra = $this->parseExtra($order);
        if (empty($extra['inventory']['stock_deducted']) || !empty($extra['inventory']['stock_restored'])) {
            return;
        }

        $qty    = max(1, (int)($extra['quantity'] ?? 1));
        $pId    = (int)($order['product_id'] ?? 0);
        $vSize  = $extra['variants']['size']  ?? null;
        $vColor = $extra['variants']['color'] ?? null;

        $pm = new ProductModel();
        if (($vSize !== null && $vSize !== '') || ($vColor !== null && $vColor !== '')) {
            $pm->increaseVariantStock($pId, $vSize ?: null, $vColor ?: null, $qty);
        } else {
            $pm->increaseStock($pId, $qty);
        }

        $extra['inventory']['stock_restored']    = true;
        $extra['inventory']['stock_restored_at'] = date('Y-m-d H:i:s');
        $this->appendInventoryLog($extra, 'restored', [
            'reason'     => $reason,
            'order_id'   => (int)($order['id'] ?? 0),
            'product_id' => $pId,
            'quantity'   => $qty,
            'size'       => $vSize  ?: '',
            'color'      => $vColor ?: '',
            'tracking'   => (string)($extra['shipping']['tracking'] ?? ''),
            'provider'   => (string)($extra['shipping']['provider'] ?? ''),
        ]);
        $this->orders->updateExtra((int)$order['id'], $extra);
    }

    /* =====================================================================
       PRIVATE HELPERS
    ===================================================================== */

    /**
     * Public wrapper: allows BulkShippingService to trigger post-shipment
     * side effects (stock deduction, status update, WhatsApp, lifecycle log)
     * without duplicating logic.
     */
    public function handlePostShipmentActions(int $id, array $extra, string $provider): void
    {
        $this->postShipmentActions($id, $extra, $provider);
    }

    /**
     * Run post-shipment side effects shared by all providers:
     * stock deduction, status confirmation, WhatsApp, action log, lifecycle, customer upsert.
     */
    private function postShipmentActions(int $id, array $extra, string $provider): void
    {
        try {
            $freshOrder = $this->orders->getById($id);
            if ($freshOrder && !empty($extra['shipping']['tracking'])) {
                $this->deductStockForOrder($freshOrder, 'shipment');
            }
        } catch (\Throwable $e) {}

        try { $this->orders->updateStatus($id, 'confirmed'); } catch (\Throwable $e) {}

        $this->sendWhatsAppOrderShippedNotification($id);

        try {
            $shipOrder = $this->orders->getById($id);
            if ($shipOrder) {
                $extraShip = $this->appendActionLog($shipOrder, 'shipment_sent', [
                    'provider' => $provider,
                    'tracking' => (string)($shipOrder['extra']['shipping']['tracking'] ?? ''),
                ]);
                $this->orders->updateExtra($id, $extraShip);
                $this->logLifecycleEvent($id, 'shipment_sent', $provider, [
                    'tracking' => (string)($shipOrder['extra']['shipping']['tracking'] ?? ''),
                ]);
                (new \Core\App\Models\CustomerModel())->upsertFromOrder($shipOrder);
            }
        } catch (\Throwable $e) {}
    }

    private function deductStockForOrder(array $order, string $reason = 'shipment'): void
    {
        $extra = $this->parseExtra($order);
        if (!empty($extra['inventory']['stock_deducted']) && empty($extra['inventory']['stock_restored'])) {
            return;
        }

        $qty    = max(1, (int)($extra['quantity'] ?? 1));
        $pId    = (int)($order['product_id'] ?? 0);
        $vSize  = $extra['variants']['size']  ?? null;
        $vColor = $extra['variants']['color'] ?? null;

        $pm = new ProductModel();
        if (($vSize !== null && $vSize !== '') || ($vColor !== null && $vColor !== '')) {
            $pm->decreaseVariantStock($pId, $vSize ?: null, $vColor ?: null, $qty);
        } else {
            $pm->decreaseStock($pId, $qty);
        }

        $extra['inventory']['stock_deducted']    = true;
        $extra['inventory']['stock_deducted_at'] = date('Y-m-d H:i:s');
        $extra['inventory']['stock_restored']    = false;
        $extra['inventory']['stock_restored_at'] = null;
        $this->appendInventoryLog($extra, 'deducted', [
            'reason'     => $reason,
            'order_id'   => (int)($order['id'] ?? 0),
            'product_id' => $pId,
            'quantity'   => $qty,
            'size'       => $vSize  ?: '',
            'color'      => $vColor ?: '',
            'tracking'   => (string)($extra['shipping']['tracking'] ?? ''),
            'provider'   => (string)($extra['shipping']['provider'] ?? ''),
        ]);
        $this->orders->updateExtra((int)$order['id'], $extra);
    }

    private function appendInventoryLog(array &$extra, string $action, array $context = []): void
    {
        if (!isset($extra['inventory_log']) || !is_array($extra['inventory_log'])) {
            $extra['inventory_log'] = [];
        }
        $extra['inventory_log'][] = array_merge(['action' => $action, 'at' => date('Y-m-d H:i:s')], $context);
    }

    private function sendWhatsAppOrderShippedNotification(int $orderId): void
    {
        try {
            $waService = \Core\App\Services\WhatsAppService::fromSettings();
            if (!$waService || !$waService->isEnabled('order_shipped')) return;

            $stmt = \db()->prepare("SELECT config FROM tracking_integrations WHERE provider='whatsapp' LIMIT 1");
            $stmt->execute();
            $waCfg = json_decode($stmt->fetchColumn() ?: '{}', true) ?: [];
            $tpl   = trim((string)($waCfg['templates']['order_shipped'] ?? ''));
            if ($tpl === '') return;

            $freshOrder = $this->orders->getById($orderId);
            if (!$freshOrder || empty($freshOrder['phone'])) return;

            $msg = \Core\App\Services\WhatsAppService::buildMessage($tpl, $freshOrder, $waCfg);
            $waService->send((string)$freshOrder['phone'], $msg, [
                'event'    => 'order_shipped',
                'order_id' => (int)$freshOrder['id'],
                'type'     => 'auto',
            ]);
        } catch (\Throwable $e) {}
    }

    public function findMaystroDisplayId($data): ?string
    {
        if (!is_array($data)) return null;

        foreach (['display_id_order', 'display_order_id', 'display_id'] as $key) {
            if (isset($data[$key]) && (string)$data[$key] !== '') return (string)$data[$key];
        }
        foreach ($data as $value) {
            if (is_array($value)) {
                $found = $this->findMaystroDisplayId($value);
                if ($found !== null && $found !== '') return $found;
            }
        }
        return null;
    }

    public function buildMaystroBasePayload(array $order, array $extra, array $overrides = []): array
    {
        $destination     = $extra['shipping']['destination'] ?? [];
        $shippingType    = $extra['shipping']['type'] ?? 'home';
        $deliveryTypeInt = (int)($overrides['delivery_type'] ?? 0);
        if (!in_array($deliveryTypeInt, [1, 2, 3], true)) {
            $typeMap         = ['home' => 1, 'desk' => 2, 'pickup' => 3];
            $deliveryTypeInt = $typeMap[$shippingType] ?? 1;
        }

        $postedStateCode = DzLocation::normalizeCode($overrides['state_code'] ?? ($destination['state_code'] ?? ''));
        $postedWilayaId  = (int)($overrides['wilaya_id'] ?? 0);
        if ($postedStateCode === '' && $postedWilayaId > 0) {
            $postedStateCode = DzLocation::normalizeCode((string)$postedWilayaId);
        }

        $stateName = trim((string)($overrides['state_name'] ?? ($destination['state_name'] ?? '')));
        if ($stateName === '' && $postedStateCode !== '') {
            $stateData = DzLocation::stateByCode($postedStateCode);
            $stateName = $stateData['name'] ?? '';
        }

        $communeName  = trim((string)($overrides['commune_name'] ?? ($destination['city_name'] ?? '')));
        $communeValue = $overrides['commune_id'] ?? ($destination['city_id'] ?? 0);
        if (is_string($communeValue) && ctype_digit($communeValue)) $communeValue = (int)$communeValue;
        if ($communeName === '' && is_int($communeValue) && $communeValue > 0) {
            $cityData    = DzLocation::findCityById($postedStateCode, $communeValue);
            $communeName = $cityData['name'] ?? '';
        }
        if ((!is_int($communeValue) || $communeValue <= 0) && $communeName !== '') {
            $communeValue = $communeName;
        }

        $address     = trim((string)($overrides['address']     ?? ($stateName . ($communeName ? ' - ' . $communeName : ''))));
        $description = trim((string)($overrides['description'] ?? ($extra['product']['name'] ?? ('Commande #' . (int)($order['id'] ?? 0)))));
        $quantity    = max(1, (int)($overrides['quantity'] ?? ($extra['quantity'] ?? 1)));
        $productId   = trim((string)($overrides['product_id'] ?? ''));

        $payload = [
            'destination_text' => $address !== '' ? $address : (string)($order['address'] ?? ''),
            'customer_name'    => trim((string)($overrides['customer_name']  ?? ($order['full_name'] ?? ''))),
            'customer_phone'   => trim((string)($overrides['customer_phone'] ?? ($order['phone']     ?? ''))),
            'express'          => !empty($overrides['express']),
            'commune'          => $communeValue,
            'note_to_driver'   => trim((string)($overrides['note'] ?? '')) ?: null,
            'details'          => [[
                'product'     => $productId !== '' ? $productId : '',
                'description' => $description !== '' ? $description : ('Commande #' . (int)($order['id'] ?? 0)),
                'quantity'    => $quantity,
            ]],
            'delivery_type'    => $deliveryTypeInt,
        ];

        if (is_string($communeValue) && $stateName !== '') {
            $payload['wilaya'] = $stateName;
        }
        if (!empty($overrides['customer_phone2'])) {
            $payload['customer_phone2'] = trim((string)$overrides['customer_phone2']);
        }
        if ($deliveryTypeInt === 3 && !empty($overrides['pickup_point'])) {
            $payload['pickup_point'] = (int)$overrides['pickup_point'];
        }

        return $payload;
    }

    private function appendActionLog(array $order, string $action, array $extraMeta = []): array
    {
        $extra = $this->parseExtra($order);
        $ctx   = $this->currentAdminContext();
        $extra['action_log'][] = array_merge([
            'action'     => $action,
            'admin_id'   => $ctx['admin_id'],
            'admin_name' => $ctx['admin_name'],
            'admin_role' => $ctx['admin_role'],
            'at'         => date('Y-m-d H:i:s'),
        ], $extraMeta);
        return $extra;
    }

    private function logLifecycleEvent(int $orderId, string $eventType, ?string $provider = null, array $meta = []): void
    {
        try {
            $ctx = $this->currentAdminContext();
            (new OrderLifecycleEventModel())->log($orderId, $eventType, $ctx['admin_id'] ?: null, $provider, array_merge([
                'admin_name' => $ctx['admin_name'],
                'admin_role' => $ctx['admin_role'],
            ], $meta));
        } catch (\Throwable $e) {}
    }

    private function currentAdminContext(): array
    {
        return [
            'admin_id'   => (int)($_SESSION['admin_id']       ?? 0),
            'admin_name' => (string)($_SESSION['admin_username'] ?? ''),
            'admin_role' => (string)($_SESSION['admin_role']    ?? ''),
        ];
    }

    private function parseExtra(array $order): array
    {
        return is_array($order['extra'] ?? null)
            ? $order['extra']
            : (json_decode((string)($order['extra'] ?? '{}'), true) ?: []);
    }

    private function err(string $alert, string $message): array
    {
        return ['success' => false, 'alert' => $alert, 'message' => $message];
    }
}
