<?php

namespace Core\App\Controllers;

use Core\App\Models\OrderModel;
use Core\App\Models\OrderWorkflowModel;
use Core\App\Models\ProductModel;
use Core\App\Models\ShippingIntegrationModel;
use Core\App\Services\MaystroLocationService;
use Core\App\Models\OrderLifecycleEventModel;
use Core\App\Support\ShippingSettings;
use Core\App\Integrations\Shipping\EcotrackClient;
use Core\App\Services\Shipping\ShippingService;
use Core\App\Services\OrderShippingService;
use Core\App\Services\OrderExportService;
use Core\App\Support\OrderPricing;
use Core\System\ThemeManager;
use Core\App\Support\DzLocation;

class OrderController
{
    protected OrderModel $orders;
    protected OrderWorkflowModel $workflow;
    protected OrderShippingService $shippingService;
    protected OrderExportService $exportService;

    public function __construct()
    {
        $this->orders          = new OrderModel();
        $this->workflow        = new OrderWorkflowModel();
        $this->shippingService = new OrderShippingService($this->orders);
        $this->exportService   = new OrderExportService($this->orders);
    }

    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 appendActionLog(array $order, string $action, array $extraMeta = []): array
    {
        $extra = is_array($order['extra'] ?? null) ? $order['extra'] : (json_decode((string)($order['extra'] ?? ''), true) ?: []);
        $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 logLifecycleEventSystem(int $orderId, string $eventType, ?string $provider = null, array $meta = []): void
    {
        try {
            (new OrderLifecycleEventModel())->log($orderId, $eventType, null, $provider, $meta);
        } catch (\Throwable $e) {}
    }


    private function wantsJsonResponse(): bool
    {
        $accept = strtolower((string)($_SERVER['HTTP_ACCEPT'] ?? ''));
        $requestedWith = strtolower((string)($_SERVER['HTTP_X_REQUESTED_WITH'] ?? ''));
        return strpos($accept, 'application/json') !== false || $requestedWith === 'xmlhttprequest';
    }

    private function failFrontendOrder(string $reason, array $payload = []): void
    {
        if ($this->wantsJsonResponse()) {
            header('Content-Type: application/json; charset=utf-8');
            echo json_encode(array_merge(['status' => 'error', 'reason' => $reason], $payload));
            exit;
        }

        if (session_status() !== PHP_SESSION_ACTIVE) @session_start();
        $_SESSION['order_error'] = $reason;
        $_SESSION['order_error_product_id'] = (int)($_POST['product_id'] ?? 0);
        foreach ($payload as $k => $v) {
            $_SESSION['order_error_' . $k] = $v;
        }
        \Core\System\Response::back('/');
    }


    private function normalizeVariantSelection(array $product, ?string $size, ?string $color, ?string $colorCode = null): array
    {
        $variants = [];
        if (isset($product['variants'])) {
            if (is_string($product['variants'])) {
                $variants = json_decode($product['variants'], true) ?: [];
            } elseif (is_array($product['variants'])) {
                $variants = $product['variants'];
            }
        }

        $sizes  = is_array($variants['sizes'] ?? null) ? $variants['sizes'] : [];
        $colors = is_array($variants['colors'] ?? null) ? $variants['colors'] : [];

        $size = trim((string)$size);
        $color = trim((string)$color);
        $colorCode = trim((string)$colorCode);

        $hasSizes = !empty($sizes);
        $hasColors = !empty($colors);

        $sizeValues = [];
        foreach ($sizes as $s) {
            $val = trim((string)($s['value'] ?? ''));
            if ($val !== '') $sizeValues[] = $val;
        }

        $colorMapByName = [];
        $colorMapByCode = [];
        foreach ($colors as $c) {
            $name = trim((string)($c['name'] ?? ''));
            $code = trim((string)($c['code'] ?? ''));
            if ($name !== '') {
                $colorMapByName[mb_strtolower($name, 'UTF-8')] = ['name'=>$name,'code'=>$code];
            }
            if ($code !== '') {
                $colorMapByCode[strtolower($code)] = ['name'=>$name,'code'=>$code];
            }
        }

        if ($hasSizes) {
            if ($size === '') {
                return ['valid'=>false,'reason'=>'missing_size'];
            }
            if (!in_array($size, $sizeValues, true)) {
                return ['valid'=>false,'reason'=>'invalid_size'];
            }
        } else {
            $size = '';
        }

        if ($hasColors) {
            if ($color === '' && $colorCode === '') {
                return ['valid'=>false,'reason'=>'missing_color'];
            }
            $picked = null;
            if ($color !== '' && isset($colorMapByName[mb_strtolower($color, 'UTF-8')])) {
                $picked = $colorMapByName[mb_strtolower($color, 'UTF-8')];
            } elseif ($color !== '' && isset($colorMapByCode[strtolower($color)])) {
                $picked = $colorMapByCode[strtolower($color)];
            } elseif ($colorCode !== '' && isset($colorMapByCode[strtolower($colorCode)])) {
                $picked = $colorMapByCode[strtolower($colorCode)];
            }

            if (!$picked) {
                return ['valid'=>false,'reason'=>'invalid_color'];
            }
            $color = (string)($picked['name'] ?? $color);
            $colorCode = (string)($picked['code'] ?? $colorCode);
        } else {
            $color = '';
            $colorCode = '';
        }

        return [
            'valid' => true,
            'size' => $size,
            'color' => $color,
            'color_code' => $colorCode,
            'has_sizes' => $hasSizes,
            'has_colors' => $hasColors,
        ];
    }


    /**
     * Delegate to OrderShippingService — kept here so internal callers
     * (updateStatus, logCallAttempt, processReturn) don't need to change.
     */
    private function restoreStockForOrder(array $order, string $reason = 'restore'): void
    {
        $this->shippingService->restoreStockForOrder($order, $reason);
    }


    /* =============================
       FRONTEND – Submit Order
    ============================= */
public function submitOrder()
{
    $data = $_POST;
    $shippingSettings = ShippingSettings::load();
    $selectionMode = (string)($shippingSettings['provider_selection_mode'] ?? 'manual_admin');
    $forcedProvider = (string)($shippingSettings['forced_provider'] ?? '');
    $frontendMaystroEnabled = !empty($shippingSettings['frontend_maystro_enabled']);

    $selectedProvider = trim((string)($data['shipping_provider'] ?? ''));
    if ($selectionMode === 'forced_provider' && $forcedProvider !== '') {
        $selectedProvider = $forcedProvider;
    }
    if ($selectionMode === 'manual_admin') {
        $selectedProvider = '';
    }

    $rawStateCode = $data['state_code'] ?? $data['state'] ?? '';
    $stateCode = DzLocation::normalizeCode($rawStateCode);
    $stateMeta = DzLocation::findState($stateCode);

    $cityId = (int)($data['city_id'] ?? $data['city'] ?? $data['commune_id'] ?? 0);
    $cityName = trim((string)($data['city_name'] ?? ''));
    $stateName = trim((string)($data['state_name'] ?? ($stateMeta['name'] ?? '')));

    $cityMeta = null;
    if ($cityId > 0) {
        $cityMeta = DzLocation::findCityById($stateCode, $cityId);
    }
    if (!$cityMeta && $cityName !== '') {
        $cityMeta = DzLocation::findCityByName($stateCode, $cityName);
    }
    if ($cityMeta) {
        $cityId = (int)($cityMeta['id'] ?? $cityId);
        $cityName = (string)($cityMeta['name'] ?? $cityName);
    }

    $data['state_code'] = $stateCode;
    $data['state_name'] = $stateName;
    $data['city_id'] = $cityId;
    $data['city_name'] = $cityName;
    $data['shipping_type'] = isset($data['shipping_type']) && in_array($data['shipping_type'], ['home', 'desk'], true)
        ? $data['shipping_type']
        : 'home';

    $maystroDeliveryType = (int)($data['shipping_delivery_type'] ?? 0);
    if (!in_array($maystroDeliveryType, [1,2,3], true)) {
        $maystroDeliveryType = $data['shipping_type'] === 'desk' ? 2 : 1;
    }
    $maystroWilayaId = (int)($data['shipping_provider_state_id'] ?? 0);
    $maystroWilayaName = trim((string)($data['shipping_provider_state_name'] ?? ''));
    $maystroCommuneId = (int)($data['shipping_provider_commune_id'] ?? 0);
    $maystroCommuneName = trim((string)($data['shipping_provider_commune_name'] ?? ''));
    $maystroPickupPoint = (int)($data['shipping_pickup_point'] ?? 0);
    $maystroExpress = !empty($data['shipping_express']) ? 1 : 0;

    if ($selectedProvider === 'maystro' && $frontendMaystroEnabled) {
        if ($maystroWilayaId > 0) {
            $stateCode = DzLocation::normalizeCode((string)$maystroWilayaId);
            $data['state_code'] = $stateCode;
        }
        if ($maystroWilayaName !== '') {
            $stateName = $maystroWilayaName;
            $data['state_name'] = $stateName;
        }
        if ($maystroCommuneId > 0) {
            $cityId = $maystroCommuneId;
            $data['city_id'] = $cityId;
        }
        if ($maystroCommuneName !== '') {
            $cityName = $maystroCommuneName;
            $data['city_name'] = $cityName;
        }
        $data['shipping_type'] = [1 => 'home', 2 => 'desk', 3 => 'pickup'][$maystroDeliveryType] ?? 'home';
    }

    /* =============================
       Basic Validation
    ============================= */
    if (
        empty($data['product_id']) ||
        empty($data['full_name']) ||
        empty($data['phone']) ||
        $stateCode === '' ||
        $cityName === ''
    ) {
        $this->failFrontendOrder('missing_required_fields');
    }

    if ($selectedProvider === 'maystro' && $frontendMaystroEnabled) {
        if ($maystroWilayaId <= 0 || $maystroCommuneId <= 0) {
            $this->failFrontendOrder('missing_required_fields');
        }
        if ($maystroDeliveryType === 3 && $maystroPickupPoint <= 0) {
            $this->failFrontendOrder('missing_required_fields');
        }
    }

    /* =============================
       Load Product
    ============================= */
    $productModel = new ProductModel();
    $product = $productModel->getById((int)$data['product_id']);

    if (!$product) {
        $this->failFrontendOrder('product_not_found');
    }

    /* =============================
       ANTI-FRAUD CHECK
    ============================= */
    $phone     = trim($data['phone']);
    $ip        = $_SERVER['REMOTE_ADDR'] ?? '';
    $productId = (int)$data['product_id'];

    $fraudScore  = 0;
    $fraudReason = [];

    try {
        $db = \db();

        // 1. هاتف محظور — رفض فوري بـ redirect لصفحة خطأ
        $s = $db->prepare("SELECT 1 FROM fraud_banned_phones WHERE phone = ? LIMIT 1");
        $s->execute([$phone]);
        if ($s->fetchColumn()) {
            $this->logFraudAttempt(
                $phone, $ip, 'phone_banned',
                trim($data['full_name'] ?? ''),
                $productId, 0
            );
            if (session_status() !== PHP_SESSION_ACTIVE) @session_start();
            $_SESSION['order_error'] = 'phone_banned';
            \Core\System\Response::back('/');
        }

        // 2. flood الهاتف — أكثر من 3 طلبات في 60 دقيقة
        $s = $db->prepare("
            SELECT COUNT(*) FROM orders
            WHERE phone = ?
            AND created_at >= DATE_SUB(NOW(), INTERVAL 60 MINUTE)
        ");
        $s->execute([$phone]);
        $phoneCount = (int)$s->fetchColumn();
        if ($phoneCount >= 3) {
            $fraudScore  += 60;
            $fraudReason[] = 'phone_flood:' . $phoneCount;
        }

        // 3. flood IP — أكثر من 5 طلبات في 60 دقيقة
        $s = $db->prepare("
            SELECT COUNT(*) FROM orders
            WHERE JSON_UNQUOTE(JSON_EXTRACT(extra, '$.ip')) = ?
            AND created_at >= DATE_SUB(NOW(), INTERVAL 60 MINUTE)
        ");
        $s->execute([$ip]);
        $ipCount = (int)$s->fetchColumn();
        if ($ipCount >= 5) {
            $fraudScore  += 50;
            $fraudReason[] = 'ip_flood:' . $ipCount;
        }

        // 4. نفس المنتج + نفس الهاتف في 30 دقيقة
        $s = $db->prepare("
            SELECT COUNT(*) FROM orders
            WHERE phone = ?
            AND product_id = ?
            AND created_at >= DATE_SUB(NOW(), INTERVAL 30 MINUTE)
        ");
        $s->execute([$phone, $productId]);
        if ((int)$s->fetchColumn() > 0) {
            $fraudScore  += 40;
            $fraudReason[] = 'duplicate_order';
        }

        // 5. نسبة الروتور
        $s = $db->prepare("
            SELECT
                COUNT(*) as total,
                SUM(CASE WHEN outcome IN ('returned','cancelled') THEN 1 ELSE 0 END) as bad
            FROM fraud_order_outcomes WHERE phone = ?
        ");
        $s->execute([$phone]);
        $row = $s->fetch(\PDO::FETCH_ASSOC);
        if ($row && (int)$row['total'] >= 3) {
            $rate = round(((int)$row['bad'] / (int)$row['total']) * 100, 1);
            if ($rate >= 60) {
                $fraudScore  += 50;
                $fraudReason[] = 'high_return_rate:' . $rate . '%';
            }
        }

    } catch (\Throwable $e) {
        $fraudScore  = 0;
        $fraudReason = [];
    }

    // رفض فوري إذا تجاوز 60 نقطة — redirect للصفحة السابقة مع رسالة
    if ($fraudScore >= 60) {
        $this->logFraudAttempt(
            $phone, $ip, 'suspicious',
            trim($data['full_name'] ?? ''),
            $productId, $fraudScore
        );
        if (session_status() !== PHP_SESSION_ACTIVE) @session_start();
        $_SESSION['order_error'] = 'suspicious';
        \Core\System\Response::back('/');
    }

    /* =============================
       VARIANT VALIDATION + STOCK CHECK
    ============================= */
    $productModel2    = new ProductModel();
    $variantSelection = $this->normalizeVariantSelection(
        $product,
        $data['variant_size'] ?? '',
        $data['variant_color'] ?? '',
        $data['variant_color_code'] ?? ''
    );

    if (empty($variantSelection['valid'])) {
        $this->failFrontendOrder((string)($variantSelection['reason'] ?? 'invalid_variant'));
    }

    $variantSize   = (string)($variantSelection['size'] ?? '');
    $variantColor  = (string)($variantSelection['color'] ?? '');
    $variantColorCode = (string)($variantSelection['color_code'] ?? '');
    $orderQty      = max(1, (int)($data['quantity'] ?? 1));

    $usesVariants = !empty($variantSelection['has_sizes']) || !empty($variantSelection['has_colors']);
    $hasVariant   = ($variantSize !== '' || $variantColor !== '');

    if ($usesVariants && !$hasVariant) {
        $this->failFrontendOrder('invalid_variant');
    }

    if ($usesVariants) {
        $availableStock = $productModel2->getVariantStock(
            $productId,
            $variantSize ?: null,
            $variantColor ?: null
        );
    } else {
        $availableStock = $productModel2->getStock($productId);
    }

    if ($availableStock <= 0 || $orderQty > $availableStock) {
        $this->failFrontendOrder('out_of_stock', ['available_stock' => max(0, $availableStock)]);
    }

    $data['variant_size'] = $variantSize;
    $data['variant_color'] = $variantColor;
    $data['variant_color_code'] = $variantColorCode;

    /* =============================
       Quantity & Pricing
    ============================= */
    $quantity = max(1, (int)($data['quantity'] ?? 1));

    $productPrice = (float)(
        !empty($product['sale_price']) && $product['sale_price'] > 0
            ? $product['sale_price']
            : $product['price']
    );

    $shippingPrice = (float)($data['shipping_price'] ?? 0);
    $currency      = ThemeManager::getSetting('currency', 'DZD');

    $offerRaw = $product['offers'] ?? null;
    $offerData = is_string($offerRaw) ? (json_decode($offerRaw, true) ?: []) : (is_array($offerRaw) ? $offerRaw : []);
    $selectedOfferType = trim((string)($data['selected_offer'] ?? ''));
    $selectedOfferLabel = trim((string)($data['selected_offer_label'] ?? ''));
    $selectedOffer = null;
    $productsTotal = $productPrice * $quantity;

    if (!empty($offerData['enabled']) && !empty($offerData['type']) && $selectedOfferType === (string)$offerData['type']) {
        if ($selectedOfferType === 'quantity_pricing' && !empty($offerData['tiers']) && is_array($offerData['tiers'])) {
            foreach ($offerData['tiers'] as $tier) {
                $tierQty = max(1, (int)($tier['qty'] ?? 0));
                if ($tierQty !== $quantity) {
                    continue;
                }
                $tierPrice = (float)($tier['price'] ?? 0);
                $productsTotal = $tierPrice;
                $selectedOffer = [
                    'type' => 'quantity_pricing',
                    'label' => $selectedOfferLabel !== '' ? $selectedOfferLabel : ('Qty ' . $tierQty),
                    'paid_qty' => $tierQty,
                    'received_qty' => $tierQty,
                    'free_qty' => 0,
                    'unit_base_price' => $productPrice,
                    'offer_total' => $tierPrice,
                ];
                break;
            }
        } elseif ($selectedOfferType === 'bundle_free') {
            $buy = max(1, (int)($offerData['buy'] ?? 0));
            $free = max(1, (int)($offerData['free'] ?? 0));
            $bundleQty = $buy + $free;
            if ($quantity === $bundleQty) {
                $productsTotal = $buy * $productPrice;
                $selectedOffer = [
                    'type' => 'bundle_free',
                    'label' => $selectedOfferLabel !== '' ? $selectedOfferLabel : ('Buy ' . $buy . ' Get ' . $free . ' Free'),
                    'paid_qty' => $buy,
                    'received_qty' => $bundleQty,
                    'free_qty' => $free,
                    'unit_base_price' => $productPrice,
                    'offer_total' => $productsTotal,
                ];
            }
        }
    }

    $total = $productsTotal + $shippingPrice;

    /* =============================
       Build Extra
    ============================= */
    $extra = [
        'product' => [
            'id'    => (int)$product['id'],
            'name'  => $product['title'],
            'image' => $product['featured_image'],
            'price' => $productPrice,
        ],
        'variants' => [
            'size'  => $data['variant_size']  ?? null,
            'color' => $data['variant_color'] ?? null,
        ],
        'quantity' => $quantity,
        'selected_offer' => $selectedOffer,
        'pricing' => [
            'products_total' => $productsTotal,
            'shipping_price' => $shippingPrice,
            'currency'       => $currency,
            'total'          => $total,
        ],
        'inventory' => [
            'stock_deducted' => false,
            'stock_restored' => false,
        ],
        'shipping' => [
            'type' => isset($data['shipping_type']) && in_array($data['shipping_type'], ['home', 'desk', 'pickup'], true)
                ? $data['shipping_type']
                : null,
            'provider'    => $selectedProvider !== '' ? $selectedProvider : null,
            'provider_selected_by_customer' => ($selectionMode === 'customer_choice' && $selectedProvider !== ''),
            'provider_selection_mode' => $selectionMode,
            'service'     => null,
            'tracking'    => null,
            'status'      => 'not_sent',
            'delivery_type' => $selectedProvider === 'maystro' ? $maystroDeliveryType : null,
            'pickup_point' => $selectedProvider === 'maystro' ? $maystroPickupPoint : null,
            'express' => $selectedProvider === 'maystro' ? $maystroExpress : 0,
            'destination' => [
                'state_code'   => $data['state_code'],
                'state_name'   => $data['state_name'] ?? '',
                'city_id'      => (int)($data['city_id'] ?? 0),
                'city_name'    => $data['city_name'],
                'city_ar_name' => $cityMeta['ar_name'] ?? '',
                'provider_state_id' => $selectedProvider === 'maystro' ? $maystroWilayaId : null,
                'provider_state_name' => $selectedProvider === 'maystro' ? $maystroWilayaName : null,
                'provider_commune_id' => $selectedProvider === 'maystro' ? $maystroCommuneId : null,
                'provider_commune_name' => $selectedProvider === 'maystro' ? $maystroCommuneName : null,
            ],
            'response'    => null,
        ],
        'fraud_score'  => $fraudScore,
        'fraud_reason' => implode(', ', $fraudReason),
        'ip'           => $ip,
    ];

    $orderId = $this->orders->addOrder([
        'product_id'     => (int)$product['id'],
        'full_name'      => trim($data['full_name']),
        'phone'          => trim($data['phone']),
        'address'        => $data['city_name'],
        'payment_method' => 'cod',
        'payment_status' => 'pending',
        'order_status'   => 'pending',
        'total_amount'   => $total,
        'extra'          => $extra,
    ]);

    /* ── Workflow assignment: rules first, fallback to least-loaded confirmer ── */
    try {
        if ($orderId > 0) {
            $assigned = $this->workflow->autoAssignOrder((int)$orderId, 'new_order_submission');
            if (!$assigned) {
                $fallbackAdminId = (new \Core\App\Models\TeamMemberModel())->assignOrder((int)$orderId);
                if ($fallbackAdminId) {
                    (new \Core\App\Models\AdminNotificationModel())->create(
                        'order_assigned',
                        (int)$orderId,
                        __('team.workflow_order_assigned_notification', 'Order assigned to you') . ' #' . (int)$orderId,
                        (int)$fallbackAdminId,
                        ['reason' => 'legacy_fallback_assignment']
                    );
                }
            }
        }
    } catch (\Throwable $e) {}


require_once ROOT_PATH . '/core/app/Services/Ga4TrackingService.php';

$ga4 = new \Ga4TrackingService();
$ga4->event('order_created', [
    'order_id' => (int) $orderId
]);

/* =============================
   ADMIN NOTIFICATION — NEW ORDER
============================= */
try {
    $db = \db();

    $stmt = $db->prepare("
        INSERT INTO admin_notifications
            (type, reference_id, title, is_read, archived)
        VALUES
            (:type, :ref, :title, 0, 0)
    ");

    $stmt->execute([
        ':type'  => 'order_created',
        ':ref'   => (int) $orderId,
        ':title' => __('notifications.new_order', 'New order received'),
    ]);

} catch (\Throwable $e) {
    // لا نكسر عملية الطلب أبداً
}

/* =============================
   SEND EMAIL — NEW ORDER
============================= */
try {
    $mailer = new \Core\System\Mailer();

    $db = \db();
    $stmt = $db->prepare("
        SELECT setting_value
        FROM settings
        WHERE setting_key = 'admin_email'
        LIMIT 1
    ");
    $stmt->execute();
    $adminEmail = (string) ($stmt->fetchColumn() ?? '');

    if ($adminEmail !== '') {

        $subject = __('emails.new_order.subject', 'New order received');

       $dir = __('direction', 'rtl') === 'rtl' ? 'rtl' : 'ltr';
$textAlign = $dir === 'rtl' ? 'right' : 'left';

$body = '
<!DOCTYPE html>
<html lang="' . htmlspecialchars(__('lang.code','ar'), ENT_QUOTES, 'UTF-8') . '" dir="' . $dir . '">
<head>
<meta charset="UTF-8">
<title>' . __('emails.new_order.subject', 'New order received') . '</title>
</head>
<body style="
    margin:0;
    padding:0;
    background:#f6f4f4;
    font-family: Cairo, Arial, sans-serif;
    direction:' . $dir . ';
    text-align:' . $textAlign . ';
">

<table width="100%" cellpadding="0" cellspacing="0">
<tr>
<td align="center" style="padding:40px 16px;">

    <table width="100%" cellpadding="0" cellspacing="0" style="
        max-width:620px;
        background:#ffffff;
        border-radius:14px;
        overflow:hidden;
        box-shadow:0 10px 30px rgba(0,0,0,.08);
    ">

        <!-- HEADER -->
        <tr>
            <td style="
                background:#598aee;
                color:#ffffff;
                padding:24px;
                font-family:Tajawal, Arial, sans-serif;
                font-size:20px;
                font-weight:700;
            ">
                🛒 ' . __('emails.new_order.title', 'New Order Details') . '
            </td>
        </tr>

        <!-- BODY -->
        <tr>
            <td style="padding:24px 24px 10px; color:#16161d; font-size:15px;">

                <p style="margin:0 0 14px;">
                    <strong>' . __('emails.order_id', 'Order ID') . ':</strong>
                    #' . (int)$orderId . '
                </p>

                <p style="margin:0 0 14px;">
                    <strong>' . __('emails.customer_name', 'Customer Name') . ':</strong>
                    ' . htmlspecialchars($data['full_name'], ENT_QUOTES, 'UTF-8') . '
                </p>

                <p style="margin:0 0 14px;">
                    <strong>' . __('emails.customer_phone', 'Phone') . ':</strong>
                    ' . htmlspecialchars($data['phone'], ENT_QUOTES, 'UTF-8') . '
                </p>

                <p style="margin:0 0 14px;">
                    <strong>' . __('emails.product', 'Product') . ':</strong>
                    ' . htmlspecialchars($product['title'], ENT_QUOTES, 'UTF-8') . '
                </p>

                <p style="margin:0 0 14px;">
                    <strong>' . __('emails.quantity', 'Quantity') . ':</strong>
                    ' . (int)$quantity . '
                </p>

                <hr style="border:none;border-top:1px solid #eee;margin:20px 0;">

                <p style="margin:0 0 14px;">
                    <strong>' . __('emails.total', 'Total') . ':</strong>
                    <span style="color:#1ae055;font-weight:800;">
                        ' . number_format($total) . ' ' . htmlspecialchars($currency, ENT_QUOTES, 'UTF-8') . '
                    </span>
                </p>

                <p style="margin:0 0 14px;">
                    <strong>' . __('emails.destination', 'Delivery Address') . ':</strong>
                    ' . htmlspecialchars($data['state_code'] . ' - ' . $data['city_name'], ENT_QUOTES, 'UTF-8') . '
                </p>

            </td>
        </tr>

        <!-- FOOTER -->
        <tr>
            <td style="
                background:#f6f4f4;
                padding:16px 24px;
                font-size:13px;
                color:#6c757d;
            ">
                ' . __('emails.footer_note', 'This email was sent automatically after a new order was created.') . '
            </td>
        </tr>

    </table>

</td>
</tr>
</table>

</body>
</html>
';

        $mailer->send($adminEmail, $subject, $body);
    }

} catch (\Throwable $e) {
    // لا نكسر الطلب أبداً لو فشل البريد
}

if (session_status() !== PHP_SESSION_ACTIVE) {
    @session_start();
}

$_SESSION['last_order_id'] = (int)$orderId;

// Google Sheets: if mode is all_orders, export immediately on receipt
try {
    $gsExportMode = \Core\App\Models\Settings::getStatic('gsheets_export_mode', 'confirmed_only');
    if ($gsExportMode === 'all_orders') {
        $gsExporter = new \Core\App\Services\GoogleSheetsExporter();
        $gsExporter->queueConfirmedOrder((int)$orderId);
    }
} catch (\Throwable $e) {
    // non-blocking
}

header('Location: /order/success');
exit;

}

    /* =============================
       Orders success
    ============================= */
public function success()
{
    if (session_status() !== PHP_SESSION_ACTIVE) {
        @session_start();
    }

    $order = null;

    if (!empty($_SESSION['last_order_id'])) {
        $orderId = (int) $_SESSION['last_order_id'];
        unset($_SESSION['last_order_id']);
        $order = $this->orders->getById($orderId);
    }

    // Prevent search engines from indexing transactional confirmation pages
    \Core\System\SeoManager::set([
        'robots'    => 'noindex, nofollow',
        'canonical' => '',
    ]);

    ThemeManager::render('order_success', [
        'order' => $order
    ]);
}

    /* =============================
       ADMIN – List Orders
    ============================= */
    public function index()
    {
        $page    = max(1, (int)($_GET['page'] ?? 1));
        $perPage = 50;
        $filters = [];

        if (!empty($_GET['status'])) {
            $filters['status'] = $_GET['status'];
        }
        if (!empty($_GET['search'])) {
            $filters['search'] = $_GET['search'];
        }

        $paginated = $this->orders->getPaginated($page, $perPage, $filters);
        $orders    = $paginated['items'];
        $totalPages = $paginated['pages'];
        $totalOrders = $paginated['total'];

        require ROOT_PATH . '/admin/templates/header.php';
        require ROOT_PATH . '/admin/templates/orders/index.php';
        require ROOT_PATH . '/admin/templates/footer.php';
    }

    /* =============================
       ADMIN – View Order
    ============================= */
    public function view($id)
    {
        $order = $this->orders->getById((int)$id);

        if (!$order) {
            http_response_code(404);
            require ROOT_PATH . '/admin/templates/header.php';
            echo '<div class="container py-4">' . __('orders.not_found') . '</div>';
            require ROOT_PATH . '/admin/templates/footer.php';
            return;
        }

        $productModel = new ProductModel();
        $product = $productModel->getById((int)$order['product_id']);

        // جلب محاولات الاتصال
        $callAttempts      = (new \Core\App\Models\CallAttemptModel())->getByOrder((int)$id);
        $callAttemptsCount = count($callAttempts);
        $currentAssignment = $this->workflow->getCurrentAssignment((int)$id);
        $workflowTimeline = $this->workflow->getOrderTimeline((int)$id);
        $assignableMembers = $this->workflow->getAssignableMembers();
        $currentAdminId = (int)($_SESSION['admin_id'] ?? 0);
        $currentAdminRole = (string)($_SESSION['admin_role'] ?? '');
        if ($currentAssignment && (int)($currentAssignment['admin_id'] ?? 0) === $currentAdminId) {
            $this->workflow->markViewed((int)$id, $currentAdminId);
            $order = $this->orders->getById((int)$id) ?: $order;
            $workflowTimeline = $this->workflow->getOrderTimeline((int)$id);
        }

        // جلب المرتجعات (بدلاً من الاستعلام المباشر في القالب)
        $existingReturns = [];
        try {
            $retStmt = \db()->prepare("SELECT * FROM order_returns WHERE order_id = ? ORDER BY created_at DESC LIMIT 50");
            $retStmt->execute([(int)$id]);
            $existingReturns = $retStmt->fetchAll(\PDO::FETCH_ASSOC);
        } catch (\Throwable $e) {}

        require ROOT_PATH . '/admin/templates/header.php';
        require ROOT_PATH . '/admin/templates/orders/view.php';
        require ROOT_PATH . '/admin/templates/footer.php';
    }

    /* =============================
       AJAX — تسجيل محاولة اتصال
    ============================= */
    public function logCallAttempt($id)
    {
        header('Content-Type: application/json');

        $result = trim($_POST['result'] ?? '');
        $note   = trim($_POST['note']   ?? '');

        $validResults = ['no_answer','busy','wrong_number','callback_later',
                         'interested','not_interested','confirmed','rejected'];

        if (!in_array($result, $validResults, true)) {
            echo json_encode(['success'=>false,'error'=>'Invalid result']); exit;
        }

        $adminId   = (int)($_SESSION['admin_id']      ?? 0);
        $adminName = $_SESSION['admin_username']       ?? '';
        $adminRole = $_SESSION['admin_role']           ?? 'confirmer';

        try {
            $model = new \Core\App\Models\CallAttemptModel();
            $model->log([
                'order_id'   => (int)$id,
                'admin_id'   => $adminId,
                'admin_name' => $adminName,
                'admin_role' => $adminRole,
                'result'     => $result,
                'note'       => $note ?: null,
            ]);

            $workflowAction = $result === 'callback_later' ? 'callback_requested' : 'call_made';
            $this->workflow->recordHandlingAction((int)$id, $adminId, $workflowAction, $note);

            // إذا كانت النتيجة تأكيد أو رفض → نُحدّث حالة الطلب
            if ($result === 'confirmed' || $result === 'rejected') {
                $prevOrder = $this->orders->getById((int)$id);
                $prevStatus = $prevOrder['order_status'] ?? 'pending';
                $this->orders->updateStatus((int)$id, $result);
                if ($result === 'rejected' && $prevStatus === 'confirmed' && $prevOrder) {
                    try { $this->restoreStockForOrder($prevOrder, 'rejected'); } catch (\Throwable $e) {}
                }
                if ($adminId > 0) {
                    $this->workflow->markOutcome((int)$id, $adminId, $result, $note);
                }
            }

            $count = $model->countByOrder((int)$id);
            echo json_encode(['success'=>true,'count'=>$count]);
        } catch (\Throwable $e) {
            echo json_encode(['success'=>false,'error'=>$e->getMessage()]);
        }
        exit;
    }

    /* =============================
       إحصائيات محاولات الاتصال
    ============================= */
    public function callAttemptsStats()
    {
        $period   = (int)($_GET['period'] ?? 30);
        $dateFrom = date('Y-m-d 00:00:00', strtotime("-{$period} days"));
        $dateTo   = date('Y-m-d 23:59:59');

        $model     = new \Core\App\Models\CallAttemptModel();
        $stats     = $model->getDashboardStats($dateFrom, $dateTo);
        $agents    = $model->getAgentsComparison($dateFrom, $dateTo);
        $callbacks = $model->getPendingCallbacks();

        require ROOT_PATH . '/admin/templates/header.php';
        require ROOT_PATH . '/admin/templates/orders/call_attempts_stats.php';
        require ROOT_PATH . '/admin/templates/footer.php';
    }

    public function workflowAction($id)
    {
        \Core\System\Csrf::check();

        $orderId = (int)$id;
        $action = trim((string)($_POST['workflow_action'] ?? ''));
        $note = trim((string)($_POST['workflow_note'] ?? ''));
        $adminId = (int)($_SESSION['admin_id'] ?? 0);
        $role = (string)($_SESSION['admin_role'] ?? '');
        $assignment = $this->workflow->getCurrentAssignment($orderId);

        $isManager = in_array($role, ['owner', 'manager'], true);
        $isAssignedUser = $assignment && (int)($assignment['admin_id'] ?? 0) === $adminId;

        if (!$isManager && !$isAssignedUser) {
            $_SESSION['error'] = __('team.workflow_not_assigned', 'لا يمكنك معالجة هذا الطلب لأنه غير مسند إليك.');
            \Core\System\Response::redirect('/admin/orders/view/' . $orderId);
            return;
        }

        try {
            if ($action === 'decline_processing') {
                if ($note === '') {
                    throw new \RuntimeException(__('team.workflow_decline_note_required', 'يجب كتابة سبب رفض المعالجة.'));
                }
                $next = $this->workflow->declineAndReassign($orderId, $adminId, $note);
                $_SESSION['success'] = $next
                    ? __('team.workflow_declined_reassigned', 'تم تسجيل الرفض وإعادة إسناد الطلب تلقائيًا.')
                    : __('team.workflow_declined_waiting_manager', 'تم تسجيل الرفض، والطلب ينتظر تدخل المدير لعدم توفر بديل.');
            } elseif (in_array($action, ['call_made', 'no_answer', 'closed', 'deferred', 'callback_requested', 'cancelled', 'address_issue', 'wrong_number', 'needs_manager', 'note_added'], true)) {
                if (in_array($action, ['deferred', 'cancelled', 'address_issue', 'wrong_number', 'needs_manager'], true) && $note === '') {
                    throw new \RuntimeException(__('team.workflow_note_required_for_action', 'هذه الحالة تتطلب ملاحظة توضيحية.'));
                }
                $this->workflow->recordHandlingAction($orderId, $adminId, $action, $note);
                $_SESSION['success'] = __('team.workflow_action_saved', 'تم حفظ إجراء المعالجة بنجاح.');
            } else {
                throw new \RuntimeException(__('team.workflow_invalid_action', 'إجراء غير صالح.'));
            }
        } catch (\Throwable $e) {
            $_SESSION['error'] = $e->getMessage() ?: __('team.workflow_action_error', 'تعذر تنفيذ إجراء المعالجة.');
        }

        \Core\System\Response::redirect('/admin/orders/view/' . $orderId);
    }

    /* =============================
        ADMIN  Update Status
     ============================= */
public function updateStatus($id)
{
    $status = $_POST['status'] ?? null;
    $currentAdminId = (int)($_SESSION['admin_id'] ?? 0);
    $currentAdminRole = (string)($_SESSION['admin_role'] ?? '');
    $assignment = $this->workflow->getCurrentAssignment((int)$id);

    if (!in_array($status, ['pending', 'confirmed', 'rejected'], true)) {
        header('Location: /admin/orders');
        exit;
    }

    if (
        in_array($status, ['confirmed', 'rejected'], true)
        && $currentAdminRole === 'confirmer'
        && $assignment
        && (int)($assignment['admin_id'] ?? 0) > 0
        && (int)($assignment['admin_id'] ?? 0) !== $currentAdminId
    ) {
        $_SESSION['error'] = __('team.workflow_not_assigned', 'لا يمكنك معالجة هذا الطلب لأنه غير مسند إليك.');
        header('Location: /admin/orders/view/' . (int)$id);
        exit;
    }

    // نقرأ حالة الطلب قبل التحديث (guard ضد double-confirm)
    $prevOrder  = $this->orders->getById((int)$id);
    $prevStatus = $prevOrder['order_status'] ?? '';

    $this->orders->updateStatus((int)$id, $status);

    /* =============================
       TEAM PERFORMANCE — تسجيل أداء الموظف
       + حفظ معرف من قام بالعملية في extra الطلب
    ============================= */
    if (in_array($status, ['confirmed', 'rejected'], true)) {
        try {
            $currentAdminName = $_SESSION['admin_username'] ?? '';

            if ($currentAdminId > 0) {
                // تسجيل الأداء فقط لموظفي التأكيد
                if ($currentAdminRole === 'confirmer') {
                    (new \Core\App\Models\TeamMemberModel())
                        ->recordAction($currentAdminId, $status);
                }

                // حفظ معرف المنفّذ في extra الطلب + سجل دورة الحياة
                $orderForLog = $this->orders->getById((int)$id);
                if ($orderForLog) {
                    $extraLog = $this->appendActionLog($orderForLog, $status);
                    $this->orders->updateExtra((int)$id, $extraLog);
                    $this->logLifecycleEvent((int)$id, $status, $orderForLog['extra']['shipping']['provider'] ?? null, [
                        'source' => 'order_status',
                    ]);
                }

                $this->workflow->markOutcome((int)$id, $currentAdminId, $status, trim((string)($_POST['call_note'] ?? '')));
            }
        } catch (\Throwable $e) {}
    }

    /* =============================
       WHATSAPP — Order Confirmed
    ============================= */
    if ($status === 'confirmed' && $prevStatus !== 'confirmed') {
        try {
            $waOrder = $this->orders->getById((int)$id);
            if ($waOrder) {
                $waService = \Core\App\Services\WhatsAppService::fromSettings();
                if ($waService && $waService->isEnabled('order_confirmed')) {
                    $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']['order_confirmed'] ?? '';
                    if ($tpl) {
                        $msg = \Core\App\Services\WhatsAppService::buildMessage($tpl, $waOrder);
                        $waService->send($waOrder['phone'], $msg, [
                            'event'    => 'order_confirmed',
                            'order_id' => (int)$waOrder['id'],
                            'type'     => 'auto',
                        ]);
                    }
                }
            }
        } catch (\Throwable $e) {}
    }


    /* =============================
       STOCK MOVEMENT
       لا يتم الخصم عند التأكيد اليدوي؛ يتم فقط عند نجاح الشحن الفعلي.
       إعادة المخزون عند الرفض فقط إذا كان قد خُصم سابقاً.
    ============================= */
    if ($status === 'rejected' && $prevStatus === 'confirmed') {
        try {
            $orderRej = $this->orders->getById((int)$id);
            if ($orderRej) {
                $this->restoreStockForOrder($orderRej, 'rejected');
            }
        } catch (\Throwable $e) {}
    }


    if ($status === 'rejected') {
        try {
            $orderForCustomer = $this->orders->getById((int)$id);
            if ($orderForCustomer) {
                (new \Core\App\Models\CustomerModel())->decrementFromOrder($orderForCustomer, 'تم رفض طلب');
            }
        } catch (\Throwable $e) {}
    }

    /* =============================
       FRAUD ORDER OUTCOMES
       تسجيل نتيجة الطلب لحساب نسبة الروتور
    ============================= */
    if (in_array($status, ['confirmed', 'rejected'], true)) {
        try {
            $order = $this->orders->getById((int)$id);
            if ($order) {
                $outcome = ($status === 'confirmed') ? 'delivered' : 'cancelled';
                $db = \db();
                $db->prepare("
                    INSERT INTO fraud_order_outcomes (order_id, phone, outcome, created_at)
                    VALUES (?, ?, ?, NOW())
                    ON DUPLICATE KEY UPDATE outcome = VALUES(outcome)
                ")->execute([(int)$id, $order['phone'], $outcome]);
            }
        } catch (\Throwable $e) {
            // لا نكسر أي شيء
        }
    }

    if ($status === 'confirmed') {
        try {
            $order = $this->orders->getById((int)$id);

            if ($order) {
                $extra = $order['extra'] ?? [];

                if (empty($extra['gsheets']['exported_at'])) {
                    $exporter = new \Core\App\Services\GoogleSheetsExporter();
                    $exporter->queueConfirmedOrder((int)$id);

                    $extra['gsheets']['exported_at'] = date('Y-m-d H:i:s');
                    $this->orders->updateExtra((int)$id, $extra);
                }
            }
        } catch (\Throwable $e) {
        }
    }

    if ($status === 'confirmed') {
        require_once ROOT_PATH . '/core/app/Services/Ga4TrackingService.php';

        $ga4 = new \Ga4TrackingService();
        $ga4->event('order_delivered', [
            'order_id' => (int) $id
        ]);
    }

    if ($status === 'confirmed') {
        header('Location: /admin/orders/prepare-shipping/' . (int)$id);
        exit;
    }

    header('Location: /admin/orders');
    exit;
}


    /* =============================
       ADMIN – Prepare Shipping
    ============================= */
    public function prepareShipping($id)
    {
        $order = $this->orders->getById((int)$id);

        if (!$order) {
            http_response_code(404);
            require ROOT_PATH . '/admin/templates/header.php';
            echo '<div class="container py-4">' . __('orders.not_found') . '</div>';
            require ROOT_PATH . '/admin/templates/footer.php';
            return;
        }

        $extra = is_array($order['extra'] ?? null) ? $order['extra'] : (json_decode((string)($order['extra'] ?? '{}'), true) ?: []);
        $presetProvider = trim((string)($extra['shipping']['provider'] ?? ''));
        $providerRoutes = [
            'maystro' => '/admin/orders/prepare-maystro/' . (int)$id,
            'yalidine' => '/admin/orders/prepare-yalidine/' . (int)$id,
            'dhd' => '/admin/orders/prepare-dhd/' . (int)$id,
            'zr_express' => '/admin/orders/prepare-zr_express/' . (int)$id,
        ];
        if ($presetProvider !== '' && isset($providerRoutes[$presetProvider])) {
            header('Location: ' . $providerRoutes[$presetProvider]);
            exit;
        }
        $lockedShippingProvider = $presetProvider !== '' ? $presetProvider : null;

        $productModel = new ProductModel();
        $product = $productModel->getById((int)$order['product_id']);

        require ROOT_PATH . '/admin/templates/header.php';
        require ROOT_PATH . '/admin/templates/orders/prepare_shipping.php';
        require ROOT_PATH . '/admin/templates/footer.php';
    }

public function prepareYalidine($id)
{
    $order = $this->orders->getById((int)$id);

    if (!$order) {
        http_response_code(404);
        require ROOT_PATH . '/admin/templates/header.php';
        echo '<div class="container py-4">'
            . __('orders.not_found', 'Order not found')
            . '</div>';
        require ROOT_PATH . '/admin/templates/footer.php';
        return;
    }

    $productModel = new \Core\App\Models\ProductModel();
    $product = $productModel->getById((int)$order['product_id']);

    require ROOT_PATH . '/admin/templates/header.php';
    require ROOT_PATH . '/admin/templates/orders/prepare_yalidine.php';
    require ROOT_PATH . '/admin/templates/footer.php';
}

    /* =============================
       Shared helper — renders a shipping error/success alert from a service result array.
    ============================= */
    private function renderShippingResult(array $result): void
    {
        require ROOT_PATH . '/admin/templates/header.php';
        echo '<div class="container py-4 alert alert-' . htmlspecialchars($result['alert'] ?? 'danger') . '">'
            . ($result['message'] ?? 'Error')
            . '</div>';
        require ROOT_PATH . '/admin/templates/footer.php';
    }

    /* =============================
       ADMIN – Send To Shipping
    ============================= */
    public function sendToShipping($id)
    {
        $service = new ShippingService();
        $result  = $service->createShipment((int)$id);

        if ($result['success']) {
            header('Location: /admin/orders/view/' . (int)$id);
            exit;
        }

        require ROOT_PATH . '/admin/templates/header.php';
        echo '<div class="container py-4 alert alert-danger">'
            . htmlspecialchars($result['message'])
            . '</div>';
        require ROOT_PATH . '/admin/templates/footer.php';
    }

    /* =============================
       ADMIN – Delete Order
    ============================= */
    public function delete($id)
    {
        $order = $this->orders->getById((int)$id);

        if ($order) {
            $st    = $order['order_status'] ?? '';
            $extra = is_array($order['extra'])
                ? $order['extra']
                : json_decode($order['extra'] ?? '{}', true);

            $hasTracking = !empty($extra['shipping']['tracking']);
            $stockDeducted = !empty($extra['inventory']['stock_deducted']) && empty($extra['inventory']['stock_restored']);
            if (($hasTracking || $stockDeducted) && !in_array($st, ['returned', 'rejected'], true)) {
                if (session_status() !== PHP_SESSION_ACTIVE) @session_start();
                $_SESSION['flash_error'] = __('orders.delete_requires_return', 'Shipped orders cannot be deleted before they are returned or rejected.');
                header('Location: /admin/orders');
                exit;
            }

            // 2. تعديل العميل لحظياً بدون حذف سجله
            try {
                $reason = 'تم حذف طلب من النظام';
                (new \Core\App\Models\CustomerModel())->decrementFromOrder($order, $reason);
            } catch (\Throwable $e) {}

            // 3. تسجيل الحذف في fraud_order_outcomes
            if (in_array($st, ['confirmed', 'returned'], true)) {
                try {
                    \db()->prepare("
                        INSERT INTO fraud_order_outcomes (order_id, phone, outcome, created_at)
                        VALUES (?, ?, 'cancelled', NOW())
                        ON DUPLICATE KEY UPDATE outcome = 'cancelled'
                    ")->execute([(int)$id, $order['phone']]);
                } catch (\Throwable $e) {}
            }
        }

        $this->orders->deleteOrder((int)$id);
        header('Location: /admin/orders');
        exit;
    }

    /* =============================
       ADMIN – Export CSV
    ============================= */
    public function export(): void
    {
        $this->exportService->exportCsv();
    }

    /* =============================
       ADMIN — Send To ECOTRACK
    ============================= */
    public function sendToEcotrack($id): void
    {
        if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') {
            $this->renderShippingResult(['alert' => 'warning', 'message' => 'هذه العملية تتم فقط عبر نموذج الإرسال (POST)']);
            return;
        }
        $result = $this->shippingService->sendToEcotrack((int)$id, $_POST);
        if ($result['success']) {
            header('Location: /admin/orders/view/' . (int)$id);
            exit;
        }
        $this->renderShippingResult($result);
    }

    public function sendToYalidine($id): void
    {
        $result = $this->shippingService->sendToYalidine((int)$id, $_POST);
        if ($result['success']) {
            header('Location: /admin/orders/view/' . (int)$id);
            exit;
        }
        $this->renderShippingResult($result);
    }

    public function refreshEcotrack($id): void
    {
        $result = $this->shippingService->refreshEcotrackStatus((int)$id);
        $this->renderShippingResult($result);
    }

public function prepareZrexpress($id)
{
    $order = $this->orders->getById((int)$id);

    if (!$order) {
        http_response_code(404);
        require ROOT_PATH . '/admin/templates/header.php';
        echo '<div class="container py-4">' . __('orders.not_found', 'Order not found') . '</div>';
        require ROOT_PATH . '/admin/templates/footer.php';
        return;
    }

    $productModel = new ProductModel();
    $product = $productModel->getById((int)$order['product_id']);

    require ROOT_PATH . '/admin/templates/header.php';
    require ROOT_PATH . '/admin/templates/orders/prepare_zrexpress.php';
    require ROOT_PATH . '/admin/templates/footer.php';
}

public function prepareMaystro($id)
{
    $order = $this->orders->getById((int)$id);

    if (!$order) {
        http_response_code(404);
        require ROOT_PATH . '/admin/templates/header.php';
        echo '<div class="container py-4">' . __('orders.not_found', 'Order not found') . '</div>';
        require ROOT_PATH . '/admin/templates/footer.php';
        return;
    }

    require ROOT_PATH . '/admin/templates/header.php';
    require ROOT_PATH . '/admin/templates/orders/prepare_maystro.php';
    require ROOT_PATH . '/admin/templates/footer.php';
}

    public function sendToZrexpress($id): void
    {
        if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') {
            $this->renderShippingResult(['alert' => 'warning', 'message' => __('shipping.post_only', 'This action requires POST')]);
            return;
        }
        $result = $this->shippingService->sendToZrexpress((int)$id, $_POST);
        if ($result['success']) {
            header('Location: /admin/orders/view/' . (int)$id);
            exit;
        }
        $this->renderShippingResult($result);
    }

    public function sendToMaystro($id): void
    {
        $result = $this->shippingService->sendToMaystro((int)$id, $_POST);
        if ($result['success']) {
            header('Location: /admin/orders/view/' . (int)$id);
            exit;
        }
        $this->renderShippingResult($result);
    }

/* ── AJAX: Maystro communes by wilaya ─────────────── */
public function maystroCommunes()
{
    header('Content-Type: application/json; charset=utf-8');

    $wilayaId = (int)($_GET['wilaya_id'] ?? $_GET['wilaya'] ?? 0);
    if ($wilayaId <= 0) {
        echo json_encode([]);
        return;
    }

    $service = new MaystroLocationService();
    $result = $service->getCommunes($wilayaId);
    echo json_encode(array_values((array)($result['data'] ?? [])), JSON_UNESCAPED_UNICODE);
}

/* ── AJAX: Maystro pickup points ─────────────────── */
public function maystroPickupPoints()
{
    header('Content-Type: application/json; charset=utf-8');

    $communeId = (int)($_GET['commune_id'] ?? $_GET['commune'] ?? 0);

    $integration = (new ShippingIntegrationModel())->getByProvider('maystro');
    if (!$integration || empty($integration['api_token']) || empty($integration['is_active'])) {
        echo json_encode([]);
        return;
    }

    $client   = \Core\App\Integrations\Shipping\MaystroClient::fromIntegration($integration);
    $response = $client->getPickupPoints($communeId);
    echo json_encode($response['data'] ?? []);
}

/* ── AJAX: Maystro delivery prices ───────────────── */
public function maystroDeliveryPrices()
{
    header('Content-Type: application/json; charset=utf-8');

    // commune_id = Maystro integer commune ID (from /base/communes/)
    $communeId = (int)($_GET['commune_id'] ?? $_GET['commune'] ?? 0);

    $integration = (new ShippingIntegrationModel())->getByProvider('maystro');
    if (!$integration || empty($integration['api_token']) || empty($integration['is_active'])) {
        echo json_encode(['success' => false, 'data' => []]);
        return;
    }

    $client = \Core\App\Integrations\Shipping\MaystroClient::fromIntegration($integration);

    if ($communeId > 0) {
        // Returns all delivery options (home/desk/pickup) with prices for this commune
        $response = $client->getDeliveryOptions($communeId);
    } else {
        echo json_encode(['success' => false, 'data' => [], 'error' => 'commune_id required']);
        return;
    }

    echo json_encode([
        'success' => $response['success'] ?? false,
        'data'    => $response['data'] ?? [],
    ]);
}

/* ── Maystro label (bordereau) ───────────────────── */
public function maystroLabel($id)
{
    $order = $this->orders->getById((int)$id);
    if (!$order) { http_response_code(404); exit; }

    $extra    = is_array($order['extra']) ? $order['extra'] : json_decode($order['extra'], true);
    $shipping = $extra['shipping'] ?? [];

    if (empty($shipping['tracking']) || ($shipping['provider'] ?? '') !== 'maystro') {
        require ROOT_PATH . '/admin/templates/header.php';
        echo '<div class="container py-4 alert alert-warning">' . __('shipping.no_maystro_tracking', 'No Maystro tracking found.') . '</div>';
        require ROOT_PATH . '/admin/templates/footer.php';
        return;
    }

    $integration = (new ShippingIntegrationModel())->getByProvider('maystro');
    if (!$integration || empty($integration['api_token'])) {
        require ROOT_PATH . '/admin/templates/header.php';
        echo '<div class="container py-4 alert alert-danger">Maystro not configured.</div>';
        require ROOT_PATH . '/admin/templates/footer.php';
        return;
    }

    $tracking = (string)$shipping['tracking'];
    $client   = \Core\App\Integrations\Shipping\MaystroClient::fromIntegration($integration);

    // ── Get display_id_order (required by /starter_bordureau/) ──────────
    // First check if already stored
    $displayId = $shipping['maystro_display_id']
              ?? $shipping['display_id']
              ?? null;

    if (!$displayId) {
        // Fetch from Maystro API
        try {
            $detailRes = $client->getOrderDetails($tracking);
            if (!empty($detailRes['success']) && is_array($detailRes['data'] ?? null)) {
                $displayId = $this->shippingService->findMaystroDisplayId($detailRes['data']);
                if ($displayId) {
                    // Cache it
                    $extra['shipping']['maystro_display_id'] = $displayId;
                    \db()->prepare("UPDATE orders SET extra = :extra WHERE id = :id")
                        ->execute([':extra' => json_encode($extra, JSON_UNESCAPED_UNICODE), ':id' => (int)$id]);
                }
            }
        } catch (\Throwable $e) {}
    }

    // ── Cache PDF on disk ────────────────────────────────────────────────
    $labelDir  = ROOT_PATH . '/storage/labels/maystro';
    if (!is_dir($labelDir)) {
        mkdir($labelDir, 0750, true);
    }
    $cacheKey  = preg_replace('/[^a-zA-Z0-9_\-]/', '', $tracking);
    $labelFile = $labelDir . '/' . $cacheKey . '.pdf';

    if (is_file($labelFile) && filesize($labelFile) > 500) {
        header('Content-Type: application/pdf');
        header('Content-Disposition: inline; filename="maystro-' . $cacheKey . '.pdf"');
        readfile($labelFile);
        return;
    }

    // ── Try to get bordereau ─────────────────────────────────────────────
    $labelId = $displayId ?? $tracking; // fallback: try tracking directly
    $response = $client->getLabel((string)$labelId);

    // If display_id failed but we have tracking, try that too
    if ((empty($response['success']) || empty($response['raw'])) && $displayId && $displayId !== $tracking) {
        $response = $client->getLabel($tracking);
    }

    if (empty($response['success']) || empty($response['raw'])) {
        // Save error for debugging
        $errDetail = $response['raw'] ?? json_encode($response);
        @file_put_contents(
            $labelDir . '/last-label-error.txt',
            date('Y-m-d H:i:s') . "\nlabelId=$labelId tracking=$tracking\n" . $errDetail . "\n\n",
            FILE_APPEND
        );
        require ROOT_PATH . '/admin/templates/header.php';
        echo '<div class="container py-4 alert alert-danger">'
           . __('shipping.label_failed', 'Could not retrieve label from Maystro.')
           . '<br><small class="text-muted">labelId=' . htmlspecialchars((string)$labelId) . '</small></div>';
        require ROOT_PATH . '/admin/templates/footer.php';
        return;
    }

    // ── Parse response (PDF or JSON wrapper) ─────────────────────────────
    $raw     = $response['raw'];
    $pdfData = null;

    if (substr($raw, 0, 4) === '%PDF') {
        // Direct PDF bytes
        $pdfData = $raw;
    } else {
        $json = json_decode($raw, true);
        if (is_array($json)) {
            if (!empty($json['label']))    { $pdfData = base64_decode((string)$json['label']); }
            elseif (!empty($json['pdf']))  { $pdfData = base64_decode((string)$json['pdf']);   }
            elseif (!empty($json['url']))  { header('Location: ' . $json['url']); return;      }
            elseif (!empty($json['file_url'])) { header('Location: ' . $json['file_url']); return; }
        }
        if ($pdfData === null) {
            $pdfData = $raw;
        }
    }

    // Strip BOM/null bytes
    if (is_string($pdfData)) {
        $pdfData = ltrim($pdfData, "\xef\xbb\xbf\x00\t\n\r ");
    }

    if (!is_string($pdfData) || strpos($pdfData, '%PDF') === false) {
        @file_put_contents(
            $labelDir . '/last-label-error.txt',
            date('Y-m-d H:i:s') . " [NOT PDF]\nlabelId=$labelId\n" . substr((string)$raw, 0, 500) . "\n\n",
            FILE_APPEND
        );
        require ROOT_PATH . '/admin/templates/header.php';
        echo '<div class="container py-4 alert alert-danger">'
           . __('shipping.label_failed', 'Response is not a PDF.')
           . '<br><small>' . htmlspecialchars(substr((string)$raw, 0, 200)) . '</small></div>';
        require ROOT_PATH . '/admin/templates/footer.php';
        return;
    }

    file_put_contents($labelFile, $pdfData);

    header('Content-Type: application/pdf');
    header('Content-Disposition: inline; filename="maystro-' . $cacheKey . '.pdf"');
    echo $pdfData;
}


public function maystroExchange($id)
{
    header('Content-Type: application/json; charset=utf-8');

    $order = $this->orders->getById((int)$id);
    if (!$order) { echo json_encode(['success' => false, 'message' => 'not_found']); return; }

    $extra    = is_array($order['extra']) ? $order['extra'] : json_decode($order['extra'], true);
    $tracking = $extra['shipping']['tracking'] ?? null;

    if (!$tracking || ($extra['shipping']['provider'] ?? '') !== 'maystro') {
        echo json_encode(['success' => false, 'message' => 'no_maystro_tracking']);
        return;
    }

    $integration = (new ShippingIntegrationModel())->getByProvider('maystro');
    if (!$integration || empty($integration['api_token'])) {
        echo json_encode(['success' => false, 'message' => 'not_configured']);
        return;
    }

    $input  = json_decode(file_get_contents('php://input'), true) ?? [];
    $client = \Core\App\Integrations\Shipping\MaystroClient::fromIntegration($integration);

    $displayId = $extra['shipping']['maystro_display_id'] ?? $extra['shipping']['display_id'] ?? null;
    if (!$displayId && $tracking) {
        $trackRes = $client->getOrder((string)$tracking);
        if (!empty($trackRes['success']) && is_array($trackRes['data'] ?? null)) {
            $displayId = $this->shippingService->findMaystroDisplayId($trackRes['data']);
        }
    }

    if (!$displayId) {
        echo json_encode(['success' => false, 'error' => 'missing_display_id']);
        return;
    }

    $data = $this->shippingService->buildMaystroBasePayload($order, $extra, $input);
    $data['external_id'] = (string)$id . '-exchange-' . time();
    $data['product_price'] = (int)round((float)($input['product_price'] ?? OrderPricing::effectiveTotal($order)));
    $data['display_old_order'] = $displayId;
    $data['is_exchange'] = true;
    $res    = $client->createExchange($data);

    echo json_encode(['success' => $res['success'] ?? false, 'data' => $res['data'] ?? null, 'error' => $res['error'] ?? null]);
}

/* ── Maystro refund / return ─────────────────────── */
public function maystroRefund($id)
{
    header('Content-Type: application/json; charset=utf-8');

    $order = $this->orders->getById((int)$id);
    if (!$order) {
        echo json_encode(['success' => false, 'message' => 'not_found']);
        return;
    }

    $extra    = is_array($order['extra']) ? $order['extra'] : json_decode($order['extra'], true);
    $shipping = $extra['shipping'] ?? [];
    $tracking = $shipping['tracking'] ?? null;

    if (!$tracking || ($shipping['provider'] ?? '') !== 'maystro') {
        echo json_encode(['success' => false, 'message' => 'no_maystro_tracking']);
        return;
    }

    $integration = (new ShippingIntegrationModel())->getByProvider('maystro');
    if (!$integration || empty($integration['api_token'])) {
        echo json_encode(['success' => false, 'message' => 'not_configured']);
        return;
    }

    $client    = \Core\App\Integrations\Shipping\MaystroClient::fromIntegration($integration);
    $input     = json_decode(file_get_contents('php://input'), true) ?? [];
    $destination = $shipping['destination'] ?? [];

    // Get display_id for old_order_display_id
    $displayId = $shipping['maystro_display_id'] ?? $shipping['display_id'] ?? null;
    if (!$displayId) {
        try {
            $trackRes = $client->getOrder((string)$tracking);
            if (!empty($trackRes['success']) && is_array($trackRes['data'] ?? null)) {
                $displayId = $this->shippingService->findMaystroDisplayId($trackRes['data']);
                if ($displayId) {
                    $extra['shipping']['maystro_display_id'] = $displayId;
                    $this->orders->updateExtra((int)$id, $extra);
                }
            }
        } catch (\Throwable $e) {}
    }

    // Get commune value (integer ID preferred)
    $communeValue = (int)($destination['city_id'] ?? 0);
    if ($communeValue <= 0) {
        $communeValue = (string)($destination['city_name'] ?? '');
    }

    // delivery_type: only 1 (home) or 2 (desk) allowed for refund (not 3=pickup per doc)
    $shippingType = $shipping['type'] ?? 'home';
    $typeMap      = ['home' => 1, 'desk' => 2, 'pickup' => 1];
    $deliveryType = (int)($input['delivery_type'] ?? $typeMap[$shippingType] ?? 1);
    if (!in_array($deliveryType, [1, 2], true)) {
        $deliveryType = 1;
    }

    // Build refund payload per official doc
    $stateName    = (string)($destination['state_name'] ?? '');
    $communeName  = (string)($destination['city_name']  ?? '');
    $address      = trim($stateName . ($communeName ? ' - ' . $communeName : ''));
    $description  = (string)($extra['product']['name'] ?? ('Retour #' . (int)$id));
    $productPrice = (int)round((float)($input['product_price'] ?? OrderPricing::effectiveTotal($order)));

    $data = [
        'external_id'      => (string)$id . '-refund-' . time(),
        'customer_name'    => trim((string)($order['full_name'] ?? '')),
        'customer_phone'   => trim((string)($order['phone']     ?? '')),
        'destination_text' => $address !== '' ? $address : (string)($order['address'] ?? ''),
        'product_price'    => $productPrice,   // refund uses product_price not total_price
        'commune'          => $communeValue,
        'delivery_type'    => $deliveryType,
        'express'          => false,
        'note_to_driver'   => null,
        'is_refund'        => true,
        'details'          => [[
            'product'     => '',
            'description' => $description,
            'quantity'    => max(1, (int)($extra['quantity'] ?? 1)),
        ]],
    ];

    // Reference original order (required)
    if ($displayId) {
        $data['old_order_display_id'] = $displayId;
    } else {
        $data['old_order_external_id'] = (string)$id;
    }

    $res = $client->createRefund($data);

    if (!empty($res['success'])) {
        $extra['shipping']['refund_requested_at'] = date('Y-m-d H:i:s');
        $this->orders->updateExtra((int)$id, $extra);
        echo json_encode(['success' => true, 'data' => $res['data'] ?? null]);
        return;
    }

    // Build user-friendly error message
    $rawError  = $res['error'] ?? '';
    $rawMsg    = '';
    if (is_array($res['raw'] ?? null)) {
        $rawMsg = $res['raw']['errors']['detail'] ?? $res['raw']['message'] ?? $res['raw']['detail'] ?? '';
    }
    $errorStr = $rawMsg ?: $rawError;

    // Detect duplicate order error (code 55)
    $isDuplicate = stripos($errorStr, 'duplicate') !== false
                || stripos($errorStr, 'duplicated') !== false
                || (isset($res['raw']['errors']['code']) && (int)$res['raw']['errors']['code'] === 55);

    if ($isDuplicate) {
        // Duplicate within 24h - this is a Maystro API restriction
        $friendlyMsg = __('returns.maystro_duplicate_note',
            'تعذر إنشاء طلب الإرجاع: نفس بيانات الزبون موجودة في طلب آخر خلال آخر 24 ساعة. '
            . 'يُرجى المحاولة مرة أخرى بعد 24 ساعة من إنشاء الشحنة الأصلية، أو تواصل مع Maystro مباشرة.');
        echo json_encode([
            'success'    => false,
            'error'      => $friendlyMsg,
            'error_code' => 55,
            'is_duplicate' => true,
        ]);
        return;
    }

    echo json_encode([
        'success' => false,
        'error'   => $errorStr ?: 'Unknown error',
        'raw'     => $res['raw'] ?? null,
    ]);
}


public function ecotrackCommunes()
{
    header('Content-Type: application/json; charset=utf-8');

    $wilayaId = (int) ($_GET['wilaya_id'] ?? 0);
    if ($wilayaId <= 0) { echo json_encode([]); return; }

    $integrationModel = new \Core\App\Models\ShippingIntegrationModel();
    $eco = $integrationModel->getByProvider('ecotrack');

    if (!$eco || empty($eco['api_token']) || empty($eco['api_id']) || empty($eco['is_active'])) {
        echo json_encode([]);
        return;
    }

    $url = rtrim($eco['api_id'], '/') . '/api/v1/get/communes?wilaya_id=' . $wilayaId;

    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => [
            'Authorization: Bearer ' . $eco['api_token'],
            'Accept: application/json'
        ]
    ]);
    $res = curl_exec($ch);
    curl_close($ch);

    echo $res ?: '[]';
}

public function ecotrackLabel()
{
    $tracking = $_GET['tracking'] ?? null;
    if (!$tracking) { http_response_code(404); exit; }

    $orderModel = new \Core\App\Models\OrderModel();
    $order      = $orderModel->getByTracking($tracking);

    if (!$order) {
        require ROOT_PATH . '/admin/templates/header.php';
        echo '<div class="container py-4 alert alert-danger">Order not found</div>';
        require ROOT_PATH . '/admin/templates/footer.php';
        return;
    }

    $extra = is_array($order['extra']) ? $order['extra'] : json_decode($order['extra'], true);

    if (empty($extra['shipping']['provider']) || $extra['shipping']['provider'] !== 'ecotrack') {
        require ROOT_PATH . '/admin/templates/header.php';
        echo '<div class="container py-4 alert alert-warning">هذه الشحنة لا تتبع Ecotrack</div>';
        require ROOT_PATH . '/admin/templates/footer.php';
        return;
    }

    $integration = (new \Core\App\Models\ShippingIntegrationModel())->getByProvider('ecotrack');

    if (!$integration || empty($integration['api_token']) || empty($integration['api_id']) || empty($integration['is_active'])) {
        require ROOT_PATH . '/admin/templates/header.php';
        echo '<div class="container py-4 alert alert-danger">تكامل Ecotrack غير مفعّل أو غير مضبوط</div>';
        require ROOT_PATH . '/admin/templates/footer.php';
        return;
    }

    // Check cached label first
    $labelDir  = ROOT_PATH . '/storage/labels/ecotrack';
    $labelFile = $labelDir . '/' . preg_replace('/[^a-zA-Z0-9_\-]/', '', $tracking) . '.pdf';

    if (!is_dir($labelDir)) {
        mkdir($labelDir, 0750, true);
    }

    if (is_file($labelFile) && filesize($labelFile) > 500) {
        // Serve from cache
        header('Content-Type: application/pdf');
        header('Content-Disposition: inline; filename="label-' . $tracking . '.pdf"');
        header('X-Label-Cache: HIT');
        readfile($labelFile);
        exit;
    }

    // Fetch PDF from Ecotrack API
    // IMPORTANT: Ecotrack returns raw binary PDF — do NOT use CURLOPT_HEADER=true
    // as it mangles the binary body. Read body only.
    $apiUrl = rtrim($integration['api_id'], '/') . '/api/v1/get/order/label?tracking=' . urlencode($tracking);

    $ch = curl_init($apiUrl);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HEADER         => false,        // body only — no header prefix
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_MAXREDIRS      => 3,
        CURLOPT_HTTPHEADER     => [
            'Authorization: Bearer ' . $integration['api_token'],
            'Accept: application/pdf, application/octet-stream, */*',
        ],
        CURLOPT_TIMEOUT        => 30,
    ]);

    $body        = curl_exec($ch);
    $httpCode    = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $contentType = (string)curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
    $curlError   = curl_error($ch);
    curl_close($ch);

    // Handle cURL error
    if ($body === false || $curlError) {
        require ROOT_PATH . '/admin/templates/header.php';
        echo '<div class="content-wrapper"><section class="content pt-3"><div class="container-fluid py-4">'
           . '<div class="alert alert-danger"><i class="bi bi-wifi-off me-2"></i>'
           . 'تعذّر الاتصال بـ Ecotrack: ' . htmlspecialchars($curlError) . '</div>'
           . '<a href="/admin/shipments" class="btn btn-outline-secondary mt-2"><i class="bi bi-arrow-left me-1"></i>رجوع</a>'
           . '</div></section></div>';
        require ROOT_PATH . '/admin/templates/footer.php';
        return;
    }

    // Detect PDF by magic bytes (%PDF) OR content-type header
    $isPdf = str_contains($contentType, 'application/pdf')
          || str_contains($contentType, 'application/octet-stream')
          || (strlen($body) > 4 && substr($body, 0, 4) === '%PDF');

    if ($httpCode !== 200 || !$isPdf || strlen($body) < 100) {
        // API may return JSON error — try to decode
        $jsonError = json_decode($body, true);
        $errMsg    = $jsonError['message'] ?? $jsonError['error'] ?? null;

        require ROOT_PATH . '/admin/templates/header.php';
        echo '<div class="content-wrapper"><section class="content pt-3"><div class="container-fluid py-4">'
           . '<div class="alert alert-warning">'
           . '<i class="bi bi-hourglass-split me-2"></i>'
           . '<strong>' . __('shipping.label_not_ready', 'بوليصة الشحن قيد التوليد لدى Ecotrack.') . '</strong><br>'
           . '<span class="small text-muted">'
           . ($errMsg ? htmlspecialchars($errMsg) : __('shipping.label_retry', 'يرجى إعادة المحاولة بعد قليل أو التحقق من حالة الشحنة.'))
           . '</span>'
           . '</div>'
           . '<div class="d-flex gap-2 mt-3">'
           . '<a href="/admin/shipments" class="btn btn-outline-secondary btn-sm"><i class="bi bi-arrow-left me-1"></i>' . __('actions.back','رجوع') . '</a>'
           . '<a href="/admin/orders/ecotrack/label?tracking=' . htmlspecialchars(urlencode($tracking)) . '" class="btn btn-primary btn-sm"><i class="bi bi-arrow-clockwise me-1"></i>' . __('actions.retry','إعادة المحاولة') . '</a>'
           . '</div>'
           . '</div></section></div>';
        require ROOT_PATH . '/admin/templates/footer.php';
        return;
    }

    // Cache locally & serve
    file_put_contents($labelFile, $body);

    header('Content-Type: application/pdf');
    header('Content-Disposition: inline; filename="label-' . $tracking . '.pdf"');
    header('Content-Length: ' . strlen($body));
    header('X-Label-Cache: MISS');
    echo $body;
    exit;
}

public function ecotrackTrack()
{
    header('Content-Type: application/json; charset=utf-8');

    $tracking = $_GET['tracking'] ?? null;
    if (!$tracking) {
        echo json_encode(['success' => false, 'message' => __('shipping.tracking_missing')]);
        exit;
    }

    $orderModel = new OrderModel();
    $order = $orderModel->getByTracking($tracking);

    if (!$order) {
        echo json_encode(['success' => false, 'message' => __('orders.not_found')]);
        exit;
    }

    $extra    = is_array($order['extra']) ? $order['extra'] : json_decode($order['extra'], true);
    $shipping = $extra['shipping'] ?? [];

    if (empty($shipping['provider']) || $shipping['provider'] !== 'ecotrack') {
        echo json_encode(['success' => false, 'message' => __('shipping.invalid_provider')]);
        exit;
    }

    $integration = (new ShippingIntegrationModel())->getByProvider('ecotrack');

    if (!$integration || empty($integration['api_token']) || empty($integration['api_id']) || empty($integration['is_active'])) {
        echo json_encode(['success' => false, 'message' => __('shipping.integration_disabled')]);
        exit;
    }

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

        if (empty($response['success']) || empty($response['data']) || empty($response['data']['status'])) {
            echo json_encode(['success' => false, 'message' => __('shipping.not_ready_yet')]);
            exit;
        }

        $status = $response['data']['status'];

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

        $orderModel->updateExtra($order['id'], $extra);

        echo json_encode(['success' => true, 'status' => $status, 'updated_at' => $extra['shipping']['last_update_at']]);
        exit;

    } catch (\Throwable $e) {
        echo json_encode(['success' => false, 'message' => __('shipping.tracking_failed'), 'error' => $e->getMessage()]);
        exit;
    }
}

/* =============================
   AJAX — Export Google Sheets
============================= */
    public function exportGoogleSheets($id): void
    {
        $this->exportService->queueGoogleSheets((int)$id);
        header('Location: /admin/orders/view/' . (int)$id);
        exit;
    }

    /* =============================
       تسجيل محاولة مرفوضة في fraud_attempts
    ============================= */
    private function logFraudAttempt(
        string $phone,
        string $ip,
        string $reason,
        string $fullName,
        int    $productId,
        int    $fraudScore = 0
    ): void {
        try {
            $db = \db();
            $s  = $db->prepare("
                INSERT INTO fraud_attempts
                    (phone, ip, reason, full_name, product_id, fraud_score, created_at)
                VALUES
                    (?, ?, ?, ?, ?, ?, NOW())
            ");
            $s->execute([$phone, $ip, $reason, $fullName, $productId, $fraudScore]);
        } catch (\Throwable $e) {
            // لا نكسر أي شيء
        }
    }

    /* =============================
       ADMIN — Process Return
    ============================= */
    public function processReturn(int $id): void
    {
        $order = $this->orders->getById($id);
        if (!$order) {
            header('Location: /admin/orders');
            exit;
        }

        $extra    = $order['extra'] ?? [];
        $maxQty   = (int)($extra['quantity'] ?? 1);
        $type     = trim($_POST['return_type']   ?? '');
        $reason   = trim($_POST['return_reason'] ?? '');
        $notes    = trim($_POST['admin_notes']   ?? '');
        $qty      = max(1, min($maxQty, (int)($_POST['return_qty'] ?? 1)));
        $received = $_POST['received_at'] ?? null;

        $allowedTypes = ['shipping_refused', 'admin_request', 'partial'];
        if (!in_array($type, $allowedTypes, true)) {
            header('Location: /admin/orders/view/' . $id);
            exit;
        }

        $db = \db();

        /* ── حفظ المرتجع ── */
        $db->prepare("
            INSERT INTO order_returns
                (order_id, type, quantity, reason, admin_notes, received_at, stock_restored, created_at)
            VALUES
                (?, ?, ?, ?, ?, ?, 0, NOW())
        ")->execute([
            $id, $type, $qty, $reason, $notes,
            ($received ?: null)
        ]);

        $returnId = (int)$db->lastInsertId();

        /* ── تحديث حالة الطلب إلى returned ── */
        $db->prepare("UPDATE orders SET order_status = 'returned' WHERE id = ?")
           ->execute([$id]);

        /* ── إعادة المخزون تلقائياً ── */
        try {
            $this->restoreStockForOrder($order, 'returned');
            $freshOrderAfterRestore = $this->orders->getById((int)$id);
            $freshExtra = $freshOrderAfterRestore['extra'] ?? [];
            if (!empty($freshExtra['inventory']['stock_restored'])) {
                $db->prepare("UPDATE order_returns SET stock_restored = 1 WHERE id = ?")
                   ->execute([$returnId]);
            }
        } catch (\Throwable $e) {}

        /* ── تسجيل في fraud_order_outcomes كـ returned ── */
        try {
            $db->prepare("
                INSERT INTO fraud_order_outcomes (order_id, phone, outcome, created_at)
                VALUES (?, ?, 'returned', NOW())
                ON DUPLICATE KEY UPDATE outcome = 'returned'
            ")->execute([$id, $order['phone']]);
        } catch (\Throwable $e) {}

        /* ── إشعار للأدمن ── */
        try {
            $db->prepare("
                INSERT INTO admin_notifications (type, reference_id, title, is_read, archived)
                VALUES ('order_returned', ?, ?, 0, 0)
            ")->execute([
                $id,
                __('notifications.order_returned', 'Order returned') . ' #' . $id
            ]);
        } catch (\Throwable $e) {}

        /* ── تعديل بيانات العميل لحظياً بدون حذف السجل ── */
        try {
            (new \Core\App\Models\CustomerModel())->decrementFromOrder($order, 'تم إرجاع طلب');
        } catch (\Throwable $e) {}

        try {
            $freshReturnedOrder = $this->orders->getById((int)$id);
            if ($freshReturnedOrder) {
                $extraReturned = $this->appendActionLog($freshReturnedOrder, 'returned_manual', [
                    'return_type' => $type,
                    'return_reason' => $reason,
                ]);
                $this->orders->updateExtra((int)$id, $extraReturned);
                $this->logLifecycleEvent((int)$id, 'returned_manual', $freshReturnedOrder['extra']['shipping']['provider'] ?? null, [
                    'return_type' => $type,
                    'return_reason' => $reason,
                    'return_id' => $returnId,
                ]);
            }
        } catch (\Throwable $e) {}

        header('Location: /admin/orders/view/' . $id . '?returned=1');
        exit;
    }

    public function requestReturnToProvider(int $id): void
    {
        \Core\System\Csrf::check();

        $order = $this->orders->getById($id);
        if (!$order) {
            header('Location: /admin/orders');
            exit;
        }

        $extra = $order['extra'] ?? [];
        $shipping = $extra['shipping'] ?? [];
        $provider = (string)($shipping['provider'] ?? '');
        $tracking = (string)($shipping['tracking'] ?? '');

        if ($provider !== 'ecotrack' || $tracking === '') {
            $_SESSION['error'] = __('returns.provider_return_not_available', 'Return request is not available for this shipment.');
            header('Location: /admin/orders/view/' . $id);
            exit;
        }

        $integration = (new ShippingIntegrationModel())->getByProvider('ecotrack');
        if (!$integration || empty($integration['api_token']) || empty($integration['api_id']) || empty($integration['is_active'])) {
            $_SESSION['error'] = __('shipping.integration_disabled');
            header('Location: /admin/orders/view/' . $id);
            exit;
        }

        try {
            $client = new EcotrackClient($integration['api_token'], $integration['api_id']);
            $response = $client->askForReturn($tracking);
            if (empty($response['success'])) {
                $_SESSION['error'] = trim((string)($response['message'] ?? '')) ?: __('returns.provider_return_failed', 'Unable to request the return from the shipping provider.');
                header('Location: /admin/orders/view/' . $id);
                exit;
            }

            $ctx = $this->currentAdminContext();
            $extra['shipping']['return_requested_to_provider_at'] = date('Y-m-d H:i:s');
            $extra['shipping']['return_requested_to_provider_by_admin_id'] = $ctx['admin_id'] ?: null;
            $extra['shipping']['return_requested_to_provider_by_admin_name'] = $ctx['admin_name'];
            $extra['shipping']['return_requested_to_provider_response'] = $response;
            $this->orders->updateExtra($id, $extra);
            $this->logLifecycleEvent($id, 'return_requested_to_provider', $provider, [
                'tracking' => $tracking,
            ]);
            $_SESSION['success'] = __('returns.provider_return_requested', 'Return request sent to the shipping provider successfully.');
        } catch (\Throwable $e) {
            $_SESSION['error'] = __('returns.provider_return_failed', 'Unable to request the return from the shipping provider.');
        }

        header('Location: /admin/orders/view/' . $id);
        exit;
    }

    /* =============================
       ADMIN — View Returns for Order
    ============================= */
    public function viewReturns(int $id): void
    {
        $order = $this->orders->getById($id);
        if (!$order) {
            header('Location: /admin/orders');
            exit;
        }

        try {
            $stmt = \db()->prepare("
                SELECT * FROM order_returns WHERE order_id = ? ORDER BY created_at DESC
            ");
            $stmt->execute([$id]);
            $returns = $stmt->fetchAll(\PDO::FETCH_ASSOC);
        } catch (\Throwable $e) {
            $returns = [];
        }

        require ROOT_PATH . '/admin/templates/header.php';
        extract(compact('order', 'returns'));
        require ROOT_PATH . '/admin/templates/orders/returns.php';
        require ROOT_PATH . '/admin/templates/footer.php';
    }

    /* =============================
       ADMIN — Prepare DHD
       DHD = Ecotrack بـ token مختلف
    ============================= */
    public function prepareDhd(int $id): void
    {
        $order = $this->orders->getById($id);
        if (!$order) {
            http_response_code(404);
            require ROOT_PATH . '/admin/templates/header.php';
            echo '<div class="container py-4">' . __('orders.not_found') . '</div>';
            require ROOT_PATH . '/admin/templates/footer.php';
            return;
        }

        $productModel = new ProductModel();
        $product      = $productModel->getById((int)$order['product_id']);

        // DHD يستخدم نفس واجهة Ecotrack
        require ROOT_PATH . '/admin/templates/header.php';
        require ROOT_PATH . '/admin/templates/orders/prepare_dhd.php';
        require ROOT_PATH . '/admin/templates/footer.php';
    }

    /* =============================
       ADMIN — Send To DHD
    ============================= */
    public function sendToDhd(int $id): void
    {
        if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') {
            header('Location: /admin/orders/prepare-dhd/' . $id);
            exit;
        }
        $result = $this->shippingService->sendToDhd($id, $_POST);
        if ($result['success']) {
            header('Location: /admin/orders/view/' . $id);
            exit;
        }
        $this->renderShippingResult($result);
    }

    /* =============================
       AJAX — Yalidine Communes
    ============================= */
    public function yalidineCommunes(): void
    {
        header('Content-Type: application/json; charset=utf-8');
        $wilayaId = (int)($_GET['wilaya_id'] ?? 0);
        if ($wilayaId <= 0) { echo json_encode([]); return; }

        $integration = (new ShippingIntegrationModel())->getByProvider('yalidine');
        if (!$integration || empty($integration['is_active'])) { echo json_encode([]); return; }

        $client = new \Core\App\Integrations\Shipping\YalidineClient(
            $integration['api_token'],
            $integration['api_id'] ?? ''
        );
        $res = $client->getCommunes($wilayaId);
        echo json_encode($res['data'] ?? []);
    }

    /* =============================
       AJAX — Yalidine Stop Desks
    ============================= */
    public function yalidineStopdesks(): void
    {
        header('Content-Type: application/json; charset=utf-8');
        $wilayaId = (int)($_GET['wilaya_id'] ?? 0);

        $integration = (new ShippingIntegrationModel())->getByProvider('yalidine');
        if (!$integration || empty($integration['is_active'])) { echo json_encode([]); return; }

        $client = new \Core\App\Integrations\Shipping\YalidineClient(
            $integration['api_token'],
            $integration['api_id'] ?? ''
        );
        $res = $client->getStopDesks($wilayaId ?: null);
        echo json_encode($res['data'] ?? []);
    }

}
