<?php

namespace Core\App\Cron\Tasks;

use Core\App\Cron\CronTaskInterface;
use Core\App\Models\OrderModel;
use Core\App\Models\ShippingIntegrationModel;
use Core\App\Models\NotificationModel;
use Core\App\Models\SecurityLog;
use Core\App\Services\WhatsAppService;
use Core\App\Models\OrderLifecycleEventModel;
use Core\App\Integrations\Shipping\MaystroClient;

class UpdateShippingStatusTask implements CronTaskInterface
{
    protected OrderModel $orderModel;
    protected ShippingIntegrationModel $shippingIntegrationModel;
    protected NotificationModel $notificationModel;
    protected SecurityLog $securityLog;

    public function __construct()
    {
        $this->orderModel = new OrderModel();
        $this->shippingIntegrationModel = new ShippingIntegrationModel();
        $this->notificationModel = new NotificationModel();
        $this->securityLog = new SecurityLog();
    }

    public function name(): string
    {
        return 'update_shipping_status';
    }

    private function lifecycle(): OrderLifecycleEventModel
    {
        return new OrderLifecycleEventModel();
    }

    private function isDeliveredStatus(string $provider, string $status): bool
    {
        $provider = strtolower($provider);
        $status = strtolower($status);
        if ($provider === 'ecotrack') {
            return in_array($status, ['livred', 'livre', 'delivered'], true);
        }
        if ($provider === 'zr_express') {
            return in_array($status, ['delivered', 'livred', 'livre'], true);
        }
        if ($provider === 'maystro') {
            return MaystroClient::isDeliveredStatus($status);
        }
        return false;
    }

   public function run(): array
{
    $report = [
        'checked' => 0,
        'updated' => 0,
        'skipped' => [],
    ];

    $orders = $this->orderModel->getOrdersWithShippingTracking();

    foreach ($orders as $order) {
        $report['checked']++;

        $extra    = $order['extra'] ?? [];
        $shipping = $extra['shipping'] ?? null;

        if (
            !$shipping ||
            empty($shipping['provider']) ||
            empty($shipping['tracking'])
        ) {
            $report['skipped'][] = [
                'order_id' => $order['id'],
                'reason'   => 'missing_shipping_data',
            ];
            continue;
        }

        /* =============================
           LOCK PROTECTION
        ============================= */
        if ($this->orderModel->isShippingLocked($extra)) {

            $this->securityLog->add(
                'shipping',
                'CRON',
                'Order #' . $order['id'] . ' skipped (locked)',
                $shipping['provider'] . ' / ' . $shipping['tracking']
            );

            $report['skipped'][] = [
                'order_id' => $order['id'],
                'reason'   => 'locked',
            ];
            continue;
        }

        if (!in_array($shipping['provider'], ['ecotrack', 'maystro', 'zr_express'], true)) {
            $report['skipped'][] = [
                'order_id' => $order['id'],
                'reason'   => 'unsupported_provider',
            ];
            continue;
        }

        $oldStatus = $shipping['status'] ?? null;

        try {

            switch ($shipping['provider']) {

                /* =============================
                   ECOTRACK
                ============================= */
                case 'ecotrack':

                    $integration = $this->shippingIntegrationModel->getByProvider('ecotrack');

                    if (
                        !$integration ||
                        empty($integration['api_token']) ||
                        empty($integration['api_id'])
                    ) {
                        throw new \RuntimeException('ecotrack_not_configured');
                    }

                    /**
                     * 🧠 SMART SOFT-SKIP
                     */
                    if (in_array($oldStatus, ['not_sent', 'sent'], true)) {
                        $report['skipped'][] = [
                            'order_id' => $order['id'],
                            'reason'   => 'ecotrack_not_ready',
                        ];
                        continue 2;
                    }

                    $response = $this->fetchEcotrackStatus(
                        $integration['api_id'],
                        $integration['api_token'],
                        $shipping['tracking']
                    );

                    if (
                        !$response ||
                        !isset($response['data']) ||
                        !isset($response['data'][$shipping['tracking']])
                    ) {
                        throw new \RuntimeException('ecotrack_not_ready');
                    }

                    $trackingData = $response['data'][$shipping['tracking']];

                    if (empty($trackingData['status'])) {
                        throw new \RuntimeException('ecotrack_empty_status');
                    }

                    // === ORIGINAL LOGIC (UNCHANGED)
                    $extra['shipping']['status']   = $trackingData['status'];
                    $extra['shipping']['response'] = $trackingData;

                    if (
                        !empty($trackingData['activity'][0]['date']) &&
                        !empty($trackingData['activity'][0]['time'])
                    ) {
                        $extra['shipping']['last_update_at'] =
                            $trackingData['activity'][0]['date'] . ' ' .
                            $trackingData['activity'][0]['time'];
                    } else {
                        $extra['shipping']['last_update_at'] = date('Y-m-d H:i:s');
                    }

                    /* =================================================
                       >>> ADDITION: OFFICIAL ECOTRACK TRACKING INFO
                       (NON BLOCKING / NO THROW)
                    ================================================= */
                    try {
                        $client = new \Core\App\Integrations\Shipping\EcotrackClient(
                            $integration['api_token'],
                            $integration['api_id']
                        );

                        $info = $client->getTrackingInfo($shipping['tracking']);

                        if (
                            !empty($info['success']) &&
                            isset($info['data']) &&
                            is_array($info['data'])
                        ) {
                            // Official lifecycle (raw – no interpretation)
                            $extra['shipping']['tracking_lifecycle'] = $info['data'];

                            if (!empty($info['data']['code'])) {
                                $extra['shipping']['lifecycle_status'] = $info['data']['code'];
                            }
                        }

                    } catch (\Throwable $e) {
                        // SILENT — do not break CRON
                    }
                    /* ================= END ADDITION ================= */

                    break;

                /* =============================
                   MAYSTRO
                ============================= */
                case 'maystro':

                    $integration = $this->shippingIntegrationModel->getByProvider('maystro');

                    if (!$integration || empty($integration['api_token'])) {
                        throw new \RuntimeException('maystro_not_configured');
                    }

                    $client   = MaystroClient::fromIntegration($integration);
                    $response = $client->track($shipping['tracking']); // getOrder()

                    if (empty($response['success'])) {
                        throw new \RuntimeException('invalid_maystro_response');
                    }

                    $orderData = $response['data'] ?? [];

                    // Extract status – Maystro returns integer status code
                    // e.g. 1=CREATED, 10=READY, 30=DELIVERED, 40=RETURNED, 50=CANCELLED
                    $rawStatus = null;
                    if (isset($orderData['status'])) {
                        $s = $orderData['status'];
                        if (is_int($s) || (is_string($s) && ctype_digit($s))) {
                            // Integer code → convert to human label
                            $rawStatus = MaystroClient::statusLabel((int)$s);
                            $extra['shipping']['status_code'] = (int)$s;
                        } elseif (is_string($s)) {
                            $rawStatus = $s;
                        }
                    } elseif (isset($orderData['state'])) {
                        $rawStatus = (string)$orderData['state'];
                    }

                    if ($rawStatus !== null) {
                        $extra['shipping']['status'] = $rawStatus;
                    }

                    // Delivery price update
                    if (isset($orderData['delivery_price'])) {
                        $extra['shipping']['delivery_price'] = $orderData['delivery_price'];
                    }

                    // Lifecycle events from any known field
                    $events = $orderData['events']
                        ?? $orderData['history']
                        ?? $orderData['activity']
                        ?? [];

                    if (!empty($events) && is_array($events)) {
                        $extra['shipping']['tracking_lifecycle'] = $events;
                        $latestEvent = end($events);
                        if (!empty($latestEvent['date'])) {
                            $extra['shipping']['last_update_at'] =
                                $latestEvent['date'] . ' ' . ($latestEvent['time'] ?? '00:00:00');
                        } else {
                            $extra['shipping']['last_update_at'] = date('Y-m-d H:i:s');
                        }
                    } else {
                        $extra['shipping']['last_update_at'] = date('Y-m-d H:i:s');
                    }

                    $extra['shipping']['response'] = $orderData;
                    break;

                /* =============================
                   ZR EXPRESS (ProColis)
                ============================= */
                case 'zr_express':

                    $integration = $this->shippingIntegrationModel->getByProvider('zr_express');

                    if (
                        !$integration ||
                        empty($integration['api_token']) ||
                        empty($integration['api_id'])
                    ) {
                        throw new \RuntimeException('zr_express_not_configured');
                    }

                    $zrClient = new \Core\App\Integrations\Shipping\ZrExpressClient(
                        $integration['api_token'],
                        $integration['api_id']
                    );

                    $zrResult = $zrClient->track($shipping['tracking']);

                    if (empty($zrResult['success'])) {
                        throw new \RuntimeException('zr_express_tracking_failed');
                    }

                    $zrStatus = $zrResult['status'] ?? '';

                    if ($zrStatus !== '') {
                        $extra['shipping']['status']   = $zrStatus;
                        $extra['shipping']['response'] = $zrResult['raw'] ?? [];
                    }

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

            /* =============================
               SUCCESS → CLEAR FAILURES
            ============================= */
            $this->orderModel->clearShippingFailures(
                $order['id'],
                $extra
            );

        } catch (\Throwable $e) {

            if (in_array($e->getMessage(), [
                'ecotrack_not_ready',
                'ecotrack_empty_status'
            ], true)) {
                $report['skipped'][] = [
                    'order_id' => $order['id'],
                    'reason'   => $e->getMessage(),
                ];
                continue;
            }

            $this->orderModel->registerShippingFailure(
                $order['id'],
                $extra,
                $e->getMessage()
            );

            $this->securityLog->add(
                'shipping',
                'CRON',
                'Order #' . $order['id'] . ' error: ' . $e->getMessage(),
                $shipping['provider'] . ' / ' . $shipping['tracking']
            );

            $report['skipped'][] = [
                'order_id' => $order['id'],
                'reason'   => $e->getMessage(),
            ];
            continue;
        }

        $newStatus = $extra['shipping']['status'] ?? null;

        /* =============================
           AUTO RETURN DETECTION
           Ecotrack & ZR Express
        ============================= */
        $autoReturned = false;

        if ($newStatus && $newStatus !== $oldStatus) {

            $provider = $shipping['provider'] ?? '';

            // ── Ecotrack: return statuses from official doc ──
            if ($provider === 'ecotrack') {
                $ecoReturnStatuses = [
                    'retour_recu',
                    'return_received',
                    'retour_archive',
                    'retour_en_traitement',
                    'retour_transit_entrepot',
                    'retour_chez_livreur',
                    'return_asked',
                    'annule',
                ];
                if (in_array(strtolower($newStatus), $ecoReturnStatuses, true)) {
                    $autoReturned = true;
                }
                // Also check lifecycle_status from tracking info
                $lc = strtolower((string)($extra['shipping']['lifecycle_status'] ?? ''));
                if ($lc && \Core\App\Services\Shipping\EcotrackStatusMapper::isReturnOrCancelled($lc)) {
                    $autoReturned = true;
                }
            }

            // ── ZR Express: return statuses ──
            if ($provider === 'zr_express') {
                if (\Core\App\Integrations\Shipping\ZrExpressClient::isReturnStatus($newStatus)) {
                    $autoReturned = true;
                }
            }

            // ── Maystro: return statuses (check both code and label) ──
            if ($provider === 'maystro') {
                $statusCode = $extra['shipping']['status_code'] ?? null;
                if (
                    ($statusCode !== null && MaystroClient::isReturnStatus((int)$statusCode)) ||
                    MaystroClient::isReturnStatus((string)$newStatus)
                ) {
                    $autoReturned = true;
                }
            }

            // ── Apply auto-return to order_status ──
            if ($autoReturned && ($order['order_status'] ?? '') !== 'returned') {
                try {
                    $this->orderModel->updateStatus((int)$order['id'], 'returned');
                    $extra['shipping']['auto_returned_at'] = date('Y-m-d H:i:s');
                    $extra['shipping']['auto_returned_status'] = $newStatus;
                    $this->lifecycle()->log((int)$order['id'], 'returned_auto_provider', null, $provider, [
                        'tracking' => $shipping['tracking'] ?? '',
                        'status' => $newStatus,
                    ]);

                    // Notify admin
                    $this->notificationModel->create([
                        'type'    => 'return',
                        'title'   => __('notifications.order_returned', 'Order Returned'),
                        'message' => sprintf(
                            'Order #%d marked as returned automatically (%s: %s)',
                            $order['id'],
                            $provider,
                            $newStatus
                        ),
                        'data' => json_encode([
                            'order_id' => $order['id'],
                            'provider' => $provider,
                            'tracking' => $shipping['tracking'],
                            'status'   => $newStatus,
                        ], JSON_UNESCAPED_UNICODE),
                    ]);

                    $this->securityLog->add(
                        'shipping',
                        'CRON',
                        sprintf('Order #%d auto-returned: provider=%s status=%s', $order['id'], $provider, $newStatus),
                        $shipping['tracking']
                    );

                    $report['updated']++;
                } catch (\Throwable $e) {
                    // silent — don't break cron
                }
            }
        }

        if ($newStatus && $this->isDeliveredStatus((string)($shipping['provider'] ?? ''), (string)$newStatus)) {
            if (!$this->lifecycle()->exists((int)$order['id'], 'delivered')) {
                $this->lifecycle()->log((int)$order['id'], 'delivered', null, $shipping['provider'] ?? null, [
                    'tracking' => $shipping['tracking'] ?? '',
                    'status' => $newStatus,
                ]);
            }
        }

        /* =============================
           NOTIFICATION + LOG ON CHANGE
        ============================= */
        if ($newStatus && $newStatus !== $oldStatus && !$autoReturned) {
            try {
                $waService = WhatsAppService::fromSettings();
                if ($waService && $waService->isEnabled('status_updated') && !empty($order['phone'])) {
                    $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']['status_updated'] ?? ''));
                    if ($tpl !== '') {
                        $orderForWa = $order;
                        $orderForWa['extra'] = $extra;
                        $msg = WhatsAppService::buildMessage($tpl, $orderForWa, $waCfg);
                        $waService->send((string)$order['phone'], $msg, [
                            'event'    => 'status_updated',
                            'order_id' => (int)$order['id'],
                            'type'     => 'auto',
                        ]);
                    }
                }
            } catch (\Throwable $e) {
                // silent — don't break cron
            }

            $this->notificationModel->create([
                'type'    => 'shipping',
                'title'   => __('notifications.shipping_updated'),
                'message' => sprintf(
                    __('notifications.shipping_changed'),
                    $order['id'],
                    (string)$oldStatus,
                    (string)$newStatus
                ),
                'data'    => json_encode([
                    'order_id' => $order['id'],
                    'provider' => $shipping['provider'],
                    'tracking' => $shipping['tracking'],
                    'old'      => $oldStatus,
                    'new'      => $newStatus,
                ], JSON_UNESCAPED_UNICODE),
            ]);

            $this->securityLog->add(
                'shipping',
                'CRON',
                sprintf(
                    'Order #%d status changed: %s → %s',
                    $order['id'],
                    (string)$oldStatus,
                    (string)$newStatus
                ),
                $shipping['provider'] . ' / ' . $shipping['tracking']
            );
        }

        /* =============================
           FINAL SAVE
        ============================= */
        $this->orderModel->updateExtra(
            (int)$order['id'],
            $extra
        );

        $report['updated']++;
    }

    return $report;
}

    protected function fetchEcotrackStatus(
        string $baseUrl,
        string $token,
        string $tracking
    ): ?array {
        $url = rtrim($baseUrl, '/') . '/api/v1/get/orders/status'
            . '?api_token=' . urlencode($token)
            . '&trackings=' . urlencode($tracking)
            . '&status=all';

        $ch = curl_init($url);

        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 30,
            CURLOPT_HTTPHEADER => [
                'Authorization: Bearer ' . $token,
                'Accept: application/json',
            ],
        ]);

        $result = curl_exec($ch);
        curl_close($ch);

        if ($result === false) {
            return null;
        }

        return json_decode($result, true);
    }
}
