<?php

namespace App\Services\Carriers;

use App\Exceptions\CarrierCreateFailedException;
use App\Models\Order;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\Log;

class DelhiveryCarrier implements CarrierInterface
{
    protected Client $client;
    protected array $config;

    public function __construct(array $config)
    {
        $this->config = $config;
        $this->client = new Client([
            'base_uri' => $config['api_url'] ?? config('services.delhivery.api_url', 'https://staging-express.delhivery.com'),
            'timeout' => 30,
            'verify' => public_path('cacert.pem'),
        ]);
    }

    public function createShipment(Order $order, array $formData = []): array
    {
        try {
            $address = $order->address_snapshot;
            $pickup = app(\App\Services\LocationService::class)
                ->courierPickupSettings($order->fulfillment_location_id ? (int) $order->fulfillment_location_id : null);

            $data = [
                'shipments' => [
                    [
                        'name' => $address['name'],
                        'add' => $address['line1'] . ' ' . ($address['line2'] ?? ''),
                        'pin' => $address['pin'],
                        'city' => $address['city'],
                        'state' => $address['state'],
                        'country' => 'India',
                        'phone' => $address['mobile'],
                        'order' => (string) $order->id,
                        'payment_mode' => $formData['payment_mode'] ?? 'Prepaid',
                        'return_pin' => $pickup['warehouse_pin'] ?? $this->config['warehouse_pin'] ?? config('services.delhivery.warehouse_pin'),
                        'return_city' => $pickup['city'] ?? $this->config['city'] ?? config('services.delhivery.city'),
                        'return_phone' => $pickup['phone'] ?? $this->config['phone'] ?? config('services.delhivery.phone'),
                        'return_add' => $pickup['address_line1'] ?? $this->config['address_line1'] ?? config('services.delhivery.address_line1'),
                        'return_state' => $pickup['state'] ?? $this->config['state'] ?? config('services.delhivery.state'),
                        'return_country' => 'India',
                        'products_desc' => $this->getProductsDescription($order),
                        'hsn_code' => $formData['hsn_code'] ?? '',
                        'cod_amount' => $formData['cod_amount'] ?? 0,
                        'order_date' => $order->placed_at ? $order->placed_at->format('Y-m-d H:i:s') : now()->format('Y-m-d H:i:s'),
                        'total_amount' => $order->total_payable,
                        'seller_add' => $pickup['address_line1'] ?? $this->config['address_line1'] ?? config('services.delhivery.address_line1'),
                        'seller_name' => $pickup['pickup_name'] ?? $this->config['pickup_name'] ?? config('services.delhivery.pickup_name'),
                        'seller_inv' => (string) $order->id,
                        'quantity' => $order->items->sum('qty'),
                        'waybill' => '',
                        "shipment_length"=> $formData['length'] ?? 10,
                        'shipment_width' => $formData['width'] ?? 5,
                        'shipment_height' => $formData['height'] ?? 3,
                        'weight' => $formData['weight'] ?? 50, // in grams
                        'seller_gst_tin' => $formData['gst_tin'] ?? '',
                        'shipping_mode' => $formData['shipping_mode'] ?? 'Surface',
                        'address_type' => $formData['address_type'] ?? 'home',
                    ],
                ],
                'pickup_location' => [
                    'name' => $pickup['pickup_name'] ?? $this->config['pickup_name'] ?? config('services.delhivery.pickup_name'),
                ],
            ];

            $response = $this->client->post('/api/cmu/create.json', [
                'headers' => [
                    'Authorization' => 'Token ' . ($this->config['api_token'] ?? config('services.delhivery.api_token')),
                ],
                'form_params' => [
                    'format' => 'json',
                    'data' => json_encode($data),
                ],
                'http_errors' => false,
                'timeout' => 60,
                'connect_timeout' => 20,
            ]);

            $statusCode = $response->getStatusCode();
            $rawBody = (string) $response->getBody();
            $result = json_decode($rawBody, true);
            if (!is_array($result)) {
                $result = [];
            }

            if ($statusCode >= 400) {
                $message = $this->extractDelhiveryErrorMessage($result, $rawBody, $statusCode);
                $failureReason = $this->detectDelhiveryFailureReason($result);

                Log::error('Delhivery shipment creation failed', [
                    'order_id' => $order->id,
                    'http_status' => $statusCode,
                    'response' => $result,
                    'raw_body' => $rawBody,
                    'failure_reason' => $failureReason,
                ]);

                throw new CarrierCreateFailedException(
                    'Failed to create Delhivery shipment: ' . $message,
                    0,
                    null,
                    $result,
                    $failureReason
                );
            }

            $waybill = $result['packages'][0]['waybill'] ?? '';
            if (!empty($waybill)) {
                $awb = $waybill;
                $labelUrl = $this->generateLabel($awb);

                Log::info('Delhivery shipment created', [
                    'order_id' => $order->id,
                    'awb' => $awb,
                ]);

                return [
                    'tracking_id' => $awb,
                    'label_url' => $labelUrl,
                    'raw_response' => $result,
                    'metadata' => [
                        'package' => $result['packages'][0] ?? [],
                    ],
                ];
            }

            $message = $this->extractDelhiveryErrorMessage($result);

            $failureReason = $this->detectDelhiveryFailureReason($result);

            Log::error('Delhivery shipment creation failed', [
                'order_id' => $order->id,
                'response' => $result,
                'failure_reason' => $failureReason,
            ]);

            throw new CarrierCreateFailedException(
                'Failed to create Delhivery shipment: ' . $message,
                0,
                null,
                $result,
                $failureReason
            );
        } catch (CarrierCreateFailedException $e) {
            throw $e;
        } catch (\Exception $e) {
            Log::error('Delhivery shipment creation exception', [
                'order_id' => $order->id,
                'error' => $e->getMessage(),
            ]);

            throw $e;
        }
    }

    public function generateLabel(string $trackingId): ?string
    {
        try {
            $apiUrl = $this->config['api_url'] ?? config('services.delhivery.api_url', 'https://staging-express.delhivery.com');
            $labelUrl = $apiUrl . '/api/p/packing_slip?wbns=' . $trackingId;

            Log::info('Delhivery label generated', [
                'awb' => $trackingId,
                'url' => $labelUrl,
            ]);

            return $labelUrl;
        } catch (\Exception $e) {
            Log::error('Delhivery label generation failed', [
                'awb' => $trackingId,
                'error' => $e->getMessage(),
            ]);

            return null;
        }
    }

    public function track(string $trackingId): ?array
    {
        try {
            $response = $this->client->get('/api/v1/packages/json/', [
                'headers' => [
                    'Authorization' => 'Token ' . ($this->config['api_token'] ?? config('services.delhivery.api_token')),
                ],
                'query' => [
                    'waybill' => $trackingId,
                ],
            ]);

            $result = json_decode($response->getBody()->getContents(), true);

            return $result['ShipmentData'][0] ?? null;
        } catch (\Exception $e) {
            Log::error('Delhivery tracking failed', [
                'awb' => $trackingId,
                'error' => $e->getMessage(),
            ]);

            return null;
        }
    }

    /**
     * Create a pickup request with Delhivery
     *
     * @param string $pickupLocation The warehouse/pickup location name
     * @param string $pickupDate Pickup date in Y-m-d format (e.g., '2023-12-29')
     * @param string $pickupTime Pickup time in H:i:s format (e.g., '11:00:00')
     * @param int $expectedPackageCount Expected number of packages
     * @return array Returns array with 'success' (bool), 'data' (array), and 'message' (string)
     */
    public function createPickupRequest(
        string $pickupLocation,
        string $pickupDate,
        string $pickupTime,
        int $expectedPackageCount
    ): array {
        try {
            $data = [
                'pickup_location' => $pickupLocation,
                'pickup_date' => $pickupDate,
                'pickup_time' => $pickupTime,
                'expected_package_count' => $expectedPackageCount,
            ];

            $response = $this->client->post('/fm/request/new/', [
                'headers' => [
                    'Authorization' => 'Token ' . ($this->config['api_token'] ?? config('services.delhivery.api_token')),
                    'Content-Type' => 'application/json',
                ],
                'json' => $data,
                'timeout' => 30,
            ]);

            $result = json_decode($response->getBody()->getContents(), true);

            Log::info('Delhivery pickup request created', [
                'pickup_location' => $pickupLocation,
                'pickup_date' => $pickupDate,
                'pickup_time' => $pickupTime,
                'expected_package_count' => $expectedPackageCount,
                'response' => $result,
            ]);

            return [
                'success' => true,
                'data' => $result,
                'message' => 'Pickup request created successfully',
            ];
        } catch (\Exception $e) {
            Log::error('Delhivery pickup request creation failed', [
                'pickup_location' => $pickupLocation,
                'pickup_date' => $pickupDate,
                'pickup_time' => $pickupTime,
                'expected_package_count' => $expectedPackageCount,
                'error' => $e->getMessage(),
            ]);

            return [
                'success' => false,
                'data' => null,
                'message' => 'Failed to create pickup request: ' . $e->getMessage(),
            ];
        }
    }

    public function validateConfig(array $config): bool
    {
        return !empty($config['api_token']) && !empty($config['api_url']);
    }

    /**
     * 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 {
            $apiToken = $this->config['api_token'] ?? config('services.delhivery.api_token');
            
            $response = $this->client->get('/c/api/pin-codes/json/', [
                'headers' => [
                    'Authorization' => 'Token ' . $apiToken,
                    'Content-Type' => 'application/json',
                ],
                'query' => [
                    'token' => $apiToken,
                    'filter_codes' => $pincode,
                ],
                'timeout' => 10,
            ]);

            $result = json_decode($response->getBody()->getContents(), true);
            
            // Check if delivery_codes array exists and has at least one entry
            if (isset($result['delivery_codes']) && is_array($result['delivery_codes']) && count($result['delivery_codes']) >= 1) {
                return [
                    'available' => true,
                    'message' => 'Delivery available for this pincode',
                ];
            }

            return [
                'available' => false,
                'message' => 'Delivery not available for this pincode',
            ];
        } catch (\Exception $e) {
            Log::error('Delhivery 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.',
            ];
        }
    }

    /**
     * Detect Delhivery create-shipment failure reason from API response for different handling (e.g. insufficient balance vs non-serviceable pincode).
     */
    protected function detectDelhiveryFailureReason(array $result): string
    {
        $remarks = $result['packages'][0]['remarks'] ?? [];
        if (!is_array($remarks)) {
            return CarrierCreateFailedException::FAILURE_UNKNOWN;
        }
        $text = implode(' ', $remarks);
        $lower = strtolower($text);
        if (str_contains($lower, 'insufficient balance')) {
            return CarrierCreateFailedException::FAILURE_INSUFFICIENT_BALANCE;
        }
        if (str_contains($lower, 'non serviceable pincode') || str_contains($lower, 'non-serviceable pincode')) {
            return CarrierCreateFailedException::FAILURE_NON_SERVICEABLE_PINCODE;
        }
        return CarrierCreateFailedException::FAILURE_UNKNOWN;
    }

    protected function extractDelhiveryErrorMessage(array $result, string $rawBody = '', ?int $statusCode = null): string
    {
        $message = $result['rmk'] ?? '';
        $remarks = $result['packages'][0]['remarks'] ?? [];
        if (is_array($remarks) && count($remarks) > 0) {
            $message = $message ? $message . ' ' . implode(' ', $remarks) : implode(' ', $remarks);
        }

        if ($message === '') {
            $message = $result['message'] ?? '';
        }

        if ($message === '' && $rawBody !== '') {
            $message = trim($rawBody);
        }

        if ($message === '') {
            $message = 'Unknown error';
        }

        if ($statusCode !== null && !str_contains($message, 'HTTP ')) {
            $message = 'HTTP ' . $statusCode . ': ' . $message;
        }

        return $message;
    }

    protected function getProductsDescription(Order $order): string
    {
        $items = $order->items->map(function ($item) {
            return $item->name . ' (x' . $item->qty . ')';
        })->take(3)->toArray();

        return implode(', ', $items);
    }
}

