<?php

namespace App\Services\Carriers;

use App\Models\Order;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;

class EkartCarrier implements CarrierInterface
{
    protected Client $client;
    protected array $config;
    protected ?string $token = null;

    public function __construct(array $config)
    {
        $this->config = $config;
        $this->client = new Client([
            'base_uri' => $config['api_url'] ?? 'https://app.elite.ekartlogistics.in',
            'timeout' => 30,
            'verify' => public_path('cacert.pem'),
        ]);
    }

    public function createShipment(Order $order, array $formData = []): array
    {
        try {
            $token = $this->getToken();
            $address = $order->address_snapshot;
            $codAmount = $formData['cod_amount'] ?? ($order->payment_method === 'cod' ? $order->total_payable : 0);
            $paymentMode = $formData['payment_mode'] ?? ($codAmount > 0 ? 'COD' : 'Prepaid');
            
            // Calculate tax values
            $taxValue = $formData['tax_value'] ?? ($order->tax_total ?? 0);
            $totalAmount = $order->total_payable;
            $taxableAmount = $totalAmount - $taxValue;
            $commodityValue = (string) $taxableAmount;

            // Build location objects
            $dropLocation = [
                'name' => $address['name'],
                'address' => $address['line1'] . ' ' . ($address['line2'] ?? ''),
                'city' => $address['city'] ?? '',
                'state' => $address['state'] ?? '',
                'country' => 'India',
                'phone' => (int) $address['mobile'],
                'pin' => (int) $address['pin'],
                'location_type' => $formData['address_type'] ?? 'Home',
            ];

            $pickupLocation = [
                'name' => $formData['name'] ?? config('services.ekart.pickup_name') ?? 'Seller',
                'address' => $formData['consigner_address'] ?? config('services.ekart.consigner_address') ?? '',
                'city' => $formData['consigner_city'] ?? config('services.ekart.consigner_city') ?? '',
                'state' => $formData['consigner_state'] ?? config('services.ekart.consigner_state') ?? '',
                'country' => 'India',
                'phone' => (int) ($formData['consigner_phone'] ?? config('services.ekart.consigner_phone') ?? 0),
                'pin' => (int) ($formData['consigner_pincode'] ?? config('services.ekart.consigner_pincode') ?? 0),
                'location_type' => 'Office',
            ];

            $data = [
                'order_number' => '#' . $order->id,
                'invoice_number' => $formData['invoice_number'] ?? ('INV-' . $order->id),
                'invoice_date' => $formData['invoice_date'] ?? ($order->placed_at ? $order->placed_at->format('Y-m-d') : now()->format('Y-m-d')),
                'consignee_name' => $address['name'],
                'consignee_gst_tin' => $formData['consignee_gst_tin'] ?? '',
                'consignee_gst_amount' => $formData['consignee_gst_amount'] ?? 0,
                'seller_name' => $formData['name'] ?? config('services.ekart.pickup_name') ?? 'Seller',
                'seller_address' => $formData['consigner_address'] ?? config('services.ekart.consigner_address') ?? '',
                'seller_gst_tin' => $formData['seller_gst_tin'] ?? ($this->config['seller_gst_tin'] ?? ''),
                'seller_gst_amount' => $formData['seller_gst_amount'] ?? 0,
                'payment_mode' => $paymentMode,
                'category_of_goods' => $formData['category_of_goods'] ?? 'General',
                'products_desc' => $this->getProductsDescription($order),
                'items' => $this->mapProducts($order),
                'hsn_code' => $formData['hsn_code'] ?? '',
                'total_amount' => (int) $totalAmount,
                'cod_amount' => $codAmount,
                'taxable_amount' => $taxableAmount,
                'tax_value' => (int) $taxValue,
                'commodity_value' => $commodityValue,
                'return_reason' => $formData['return_reason'] ?? ($paymentMode === 'Pickup' ? 'Return' : ''),
                'quantity' => $order->items->sum('qty'),
                'weight' => $formData['weight'] ?? 55,
                'length' => $formData['length'] ?? 10,
                'width' => $formData['width'] ?? 5,
                'height' => $formData['height'] ?? 3,
                'drop_location' => $dropLocation,
            ];

            // Add optional fields
            if ($pickupLocation) {
                $data['pickup_location'] = $pickupLocation;
                $data['return_location'] = $pickupLocation;
            }
            if (!empty($formData['templateName'])) {
                $data['templateName'] = $formData['templateName'];
            }

            // log the data being sent
            Log::info('Ekart create shipment data', [
                'order_id' => $order->id,
                'data' => $data,
            ]);

            $response = $this->client->put('/api/v1/package/create', $this->withAuth($token, $data));

            $result = json_decode($response->getBody()->getContents(), true);

            if (isset($result['status']) && $result['status'] && isset($result['tracking_id'])) {
                $trackingId = $result['tracking_id'];
                $labelUrl = $this->generateLabel($trackingId);

                Log::info('Ekart shipment created', [
                    'order_id' => $order->id,
                    'tracking_id' => $trackingId,
                ]);

                return [
                    'tracking_id' => $trackingId,
                    'label_url' => $labelUrl,
                    'raw_response' => $result,
                    'metadata' => [
                        'vendor' => $result['vendor'] ?? null,
                        'barcodes' => $result['barcodes'] ?? null,
                    ],
                ];
            }

            Log::error('Ekart shipment creation failed', [
                'order_id' => $order->id,
                'response' => $result,
            ]);

            throw new \Exception('Failed to create Ekart shipment: ' . ($result['remark'] ?? $result['message'] ?? 'Unknown error'));
        } catch (\Exception $e) {
            Log::error('Ekart shipment creation exception', [
                'order_id' => $order->id,
                'error' => $e->getMessage(),
            ]);

            throw $e;
        }
    }

    public function generateLabel(string $trackingId): ?string
    {
        try {
            $token = $this->getToken();
            $response = $this->client->post('/api/v1/package/label', $this->withAuth($token, [
                'ids' => [$trackingId],
            ]));

            // The API returns a PDF binary, so we return the URL to download it
            $apiUrl = $this->config['api_url'] ?? 'https://app.elite.ekartlogistics.in';
            $labelUrl = $apiUrl . '/api/v1/package/label?json_only=false&ids[]=' . $trackingId;

            Log::info('Ekart label generated', [
                'tracking_id' => $trackingId,
                'url' => $labelUrl,
            ]);

            return $labelUrl;
        } catch (\Exception $e) {
            Log::error('Ekart label generation failed', [
                'tracking_id' => $trackingId,
                'error' => $e->getMessage(),
            ]);

            return null;
        }
    }

    public function track(string $trackingId): ?array
    {
        try {
            // Track endpoint is public (no auth required per spec.yaml)
            $response = $this->client->get('/api/v1/track/' . $trackingId);

            $result = json_decode($response->getBody()->getContents(), true);

            return $result['track'] ?? $result ?? null;
        } catch (\Exception $e) {
            Log::error('Ekart tracking failed', [
                'tracking_id' => $trackingId,
                'error' => $e->getMessage(),
            ]);

            return null;
        }
    }

    public function validateConfig(array $config): bool
    {
        return !empty($config['username']) && !empty($config['password']) && !empty($config['client_id']);
    }

    /**
     * Check delivery availability for a given pincode
     *
     * @param string $pincode
     * @return array Returns array with 'available' (bool) and 'message' (string)
     */
    public function checkDeliveryAvailability(string $pincode): array
    {
        try {
            $token = $this->getToken();
            // Ekart serviceability API endpoint - requires authentication
            $authOptions = $this->withAuth($token);
            // For GET request, only use headers (remove json key if present)
            $response = $this->client->get('/api/v2/serviceability/' . $pincode, [
                'headers' => $authOptions['headers'],
                'timeout' => 10,
            ]);

            $result = json_decode($response->getBody()->getContents(), true);
            
            // Check if status field exists and is true
            if (isset($result['status']) && $result['status'] === true) {
                return [
                    'available' => true,
                    'message' => 'Delivery available for this pincode',
                ];
            }

            $remark = $result['remark'] ?? 'Delivery not available for this pincode';
            return [
                'available' => false,
                'message' => $remark,
            ];
        } catch (\Exception $e) {
            Log::error('Ekart delivery availability check failed', [
                'pincode' => $pincode,
                'error' => $e->getMessage(),
            ]);

            // On error, assume not available to be safe
            return [
                'available' => false,
                'message' => 'Unable to verify delivery availability. Please try again later.',
            ];
        }
    }

    protected function getProductsDescription(Order $order): string
    {
        $items = $order->items->map(function ($item) {
            return $item->name . ' (x' . $item->qty . ')';
        })->take(3)->toArray();

        return implode(', ', $items);
    }

    protected function mapProducts(Order $order): array
    {
        return $order->items->map(function ($item) {
            return [
                'product_name' => $item->name,
                'quantity' => (int) $item->qty,
                'price' => (int) $item->line_total,
                'sku' => $item->sku ?? 'N/A',
            ];
        })->values()->toArray();
    }

    protected function getToken(): string
    {
        if ($this->token) {
            return $this->token;
        }

        $cacheKey = 'ekart_token_' . md5(($this->config['username'] ?? '') . '_' . ($this->config['client_id'] ?? ''));

        // Try to get from cache first
        $cached = Cache::get($cacheKey);
        if ($cached) {
            return $this->token = $cached;
        }

        if (empty($this->config['username']) || empty($this->config['password']) || empty($this->config['client_id'])) {
            throw new \RuntimeException('Ekart credentials missing');
        }

        $response = $this->client->post('/integrations/v2/auth/token/' . $this->config['client_id'], [
            'headers' => [
                'Content-Type' => 'application/json',
            ],
            'json' => [
                'username' => $this->config['username'],
                'password' => $this->config['password'],
            ],
        ]);

        $body = json_decode($response->getBody()->getContents(), true);
        $token = $body['access_token'] ?? null;
        $expiresIn = $body['expires_in'] ?? 86400;

        if (!$token) {
            throw new \RuntimeException('Ekart authentication failed: ' . ($body['message'] ?? 'Unknown error'));
        }

        // Cache for expires_in - 1 hour (safety margin)
        $cacheDuration = max(3600, $expiresIn - 3600);
        Cache::put($cacheKey, $token, now()->addSeconds($cacheDuration));

        return $this->token = $token;
    }

    protected function withAuth(string $token, array $json = []): array
    {
        return [
            'headers' => [
                'Authorization' => 'Bearer ' . $token,
                'Content-Type' => 'application/json',
            ],
            'json' => $json,
        ];
    }
}
