<?php

namespace App\Console\Commands;

use App\Models\Order;
use App\Models\Shipment;
use App\Services\Carriers\CarrierServiceFactory;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use Throwable;

class EkartApiDeliveryStatusSyncing extends Command
{
    protected $signature = 'ekart:sync-delivery-status';
    protected $description = 'Sync Ekart shipment tracking and update shipment/order statuses';

    public function handle()
    {
        $counters = [
            'processed' => 0,
            'updated' => 0,
            'skipped' => 0,
            'failed' => 0,
            'orders_updated' => 0,
        ];

        $affectedOrderIds = [];

        try {
            $carrierService = CarrierServiceFactory::make('ekart');
        } catch (Throwable $e) {
            $this->error('Unable to initialize Ekart carrier: ' . $e->getMessage());
            Log::error('Ekart sync bootstrap failed', ['error' => $e->getMessage()]);

            return Command::FAILURE;
        }

        $shipments = Shipment::query()
            ->whereNotNull('tracking_id')
            ->where('status', '!=', Shipment::STATUS_DELIVERED)
            ->whereHas('carrier', function ($query) {
                $query->where('slug', 'ekart');
            })
            ->get();

        if ($shipments->isEmpty()) {
            $this->info('No pending Ekart shipments found for syncing.');
            return Command::SUCCESS;
        }

        $shipmentIdsByTracking = $shipments
            ->groupBy('tracking_id')
            ->map(static fn ($group) => $group->pluck('id')->values()->all());

        foreach ($shipmentIdsByTracking as $trackingId => $shipmentIds) {
            $counters['processed']++;

            try {
                $trackingData = $carrierService->track((string) $trackingId);
            } catch (Throwable $e) {
                $counters['failed']++;
                Log::error('Ekart tracking request failed', [
                    'tracking_id' => $trackingId,
                    'error' => $e->getMessage(),
                ]);
                continue;
            }

            if (!$trackingData || !is_array($trackingData)) {
                $counters['skipped']++;
                Log::warning('Ekart tracking response missing/invalid', [
                    'tracking_id' => $trackingId,
                ]);
                continue;
            }

            $normalized = $this->normalizeTrackingStatus($trackingData);
            if (empty($normalized['external_status'])) {
                $counters['skipped']++;
                Log::warning('Ekart tracking status not found', [
                    'tracking_id' => $trackingId,
                    'tracking_data' => $trackingData,
                ]);
                continue;
            }

            $matchingShipments = $shipments->whereIn('id', $shipmentIds);
            if ($matchingShipments->isEmpty()) {
                $counters['skipped']++;
                continue;
            }

            foreach ($matchingShipments as $shipment) {
                $metadata = $shipment->metadata ?? [];
                if (!is_array($metadata)) {
                    $metadata = [];
                }

                $metadata['tracking_sync'] = [
                    'external_status' => $normalized['external_status'],
                    'external_substatus' => $normalized['external_substatus'],
                    'normalized_order_status' => $normalized['order_status'],
                    'normalized_shipment_status' => $normalized['shipment_status'],
                    'history' => $normalized['history'] ?? [],
                    'synced_at' => now()->toDateTimeString(),
                ];

                $newShipmentStatus = $normalized['shipment_status'] ?? Shipment::STATUS_CREATED;
                $shipmentChanged = $shipment->status !== $newShipmentStatus;

                $shipment->update([
                    'status' => $newShipmentStatus,
                    'metadata' => $metadata,
                    'raw_response' => $trackingData,
                ]);

                if ($shipmentChanged) {
                    $counters['updated']++;
                } else {
                    $counters['skipped']++;
                }

                $affectedOrderIds[$shipment->order_id] = true;
            }
        }

        if (!empty($affectedOrderIds)) {
            $orders = Order::query()
                ->whereIn('id', array_keys($affectedOrderIds))
                ->with(['shipments.carrier'])
                ->get();

            foreach ($orders as $order) {
                if ($this->syncOrderStatusFromEkartShipments($order)) {
                    $counters['orders_updated']++;
                }
            }
        }

        Log::info('Ekart delivery status sync completed', $counters);
        $this->info('Ekart sync completed: ' . json_encode($counters));

        return Command::SUCCESS;
    }

    private function normalizeTrackingStatus(array $trackingData): array
    {
        $statusText = (string) ($trackingData['status'] ?? '');
        $descText = (string) ($trackingData['desc'] ?? '');
        $normalizedStatusText = strtolower(trim($statusText));
        $historyNode = $trackingData['details'] ?? [];

        $orderStatus = Order::STATUS_AWAITING_PICKUP;

        if ($normalizedStatusText === 'delivered') {
            $orderStatus = Order::STATUS_DELIVERED;
        } elseif ($normalizedStatusText === 'rto delivered') {
            $orderStatus = Order::STATUS_RETURNED;
        } elseif (in_array($normalizedStatusText, [
            'cancelled',
            'seller cancelled',
            'pickup cancelled',
            'lost',
            'damaged',
            'not serviceable',
        ], true)) {
            $orderStatus = Order::STATUS_CANCELLED;
        } elseif (in_array($normalizedStatusText, [
            'in transit',
            'out for delivery',
            'picked up',
            'shipment delayed',
            'undelivered',
            'out for pickup',
            'rto requested',
            'seller rto requested',
            'rto in transit',
            'rto out for delivery',
            'rto failed',
            'rto shipment delayed',
        ], true)) {
            $orderStatus = Order::STATUS_SHIPPED;
        } elseif (in_array($normalizedStatusText, [
            'order placed',
            'pickup pending',
            'pickup scheduled',
            'not picked',
        ], true)) {
            $orderStatus = Order::STATUS_AWAITING_PICKUP;
        }

        $shipmentStatus = $normalizedStatusText !== ''
            ? str_replace('_', ' ', $normalizedStatusText)
            : Shipment::STATUS_CREATED;

        return [
            'external_status' => $statusText,
            'external_substatus' => $descText,
            'order_status' => $orderStatus,
            'shipment_status' => $shipmentStatus,
            'history' => is_array($historyNode) ? $historyNode : [],
        ];
    }

    private function syncOrderStatusFromEkartShipments(Order $order): bool
    {
        $ekartShipments = $order->shipments
            ->filter(static fn ($shipment) => optional($shipment->carrier)->slug === 'ekart');

        if ($ekartShipments->isEmpty()) {
            return false;
        }

        $hasDelivered = false;
        $hasReturned = false;
        $hasCancelled = false;
        $hasShipped = false;
        $hasAwaitingPickup = false;

        foreach ($ekartShipments as $shipment) {
            $sync = $shipment->metadata['tracking_sync'] ?? null;
            $normalizedOrderStatus = is_array($sync) ? ($sync['normalized_order_status'] ?? null) : null;

            if ($normalizedOrderStatus === Order::STATUS_DELIVERED) {
                $hasDelivered = true;
            } elseif ($normalizedOrderStatus === Order::STATUS_RETURNED) {
                $hasReturned = true;
            } elseif ($normalizedOrderStatus === Order::STATUS_CANCELLED) {
                $hasCancelled = true;
            } elseif ($normalizedOrderStatus === Order::STATUS_SHIPPED) {
                $hasShipped = true;
            } elseif ($normalizedOrderStatus === Order::STATUS_AWAITING_PICKUP) {
                $hasAwaitingPickup = true;
            }
        }

        $derivedStatus = null;
        if ($hasDelivered) {
            $derivedStatus = Order::STATUS_DELIVERED;
        } elseif ($hasReturned) {
            $derivedStatus = Order::STATUS_RETURNED;
        } elseif ($hasCancelled) {
            $derivedStatus = Order::STATUS_CANCELLED;
        } elseif ($hasShipped) {
            $derivedStatus = Order::STATUS_SHIPPED;
        } elseif ($hasAwaitingPickup) {
            $derivedStatus = Order::STATUS_AWAITING_PICKUP;
        }

        if ($derivedStatus === null) {
            return false;
        }

        $current = $order->status;
        $next = $this->resolveNextOrderStatus($current, $derivedStatus);
        if ($next === $current) {
            return false;
        }

        $order->update(['status' => $next]);
        return true;
    }

    private function resolveNextOrderStatus(string $current, string $derived): string
    {
        if ($derived === Order::STATUS_DELIVERED) {
            return Order::STATUS_DELIVERED;
        }

        if ($derived === Order::STATUS_RETURNED) {
            return $current === Order::STATUS_DELIVERED ? $current : Order::STATUS_RETURNED;
        }

        if ($derived === Order::STATUS_CANCELLED) {
            return in_array($current, [Order::STATUS_DELIVERED, Order::STATUS_RETURNED], true)
                ? $current
                : Order::STATUS_CANCELLED;
        }

        if (in_array($current, [Order::STATUS_DELIVERED, Order::STATUS_CANCELLED, Order::STATUS_RETURNED], true)) {
            return $current;
        }

        return $derived;
    }
}
