<?php

namespace App\Services;

use App\Models\ShippingZone;
use App\Models\ShippingSetting;
use App\Models\Order;

/**
 * Service utama untuk kalkulasi ongkos kirim.
 *
 * Alur kerja:
 *   1. Ambil kode provinsi tujuan dari alamat user.
 *   2. Cari zona pengiriman yang cocok.
 *   3. Untuk setiap layanan kurir aktif, hitung biaya berdasarkan berat & zona.
 *   4. Cek apakah subtotal memenuhi gratis ongkir.
 *   5. Kembalikan semua opsi kurir + info gratis ongkir.
 */
class ShippingCalculatorService
{
    /**
     * Hitung semua opsi pengiriman yang tersedia.
     *
     * @param  string  $provinceCode  Kode provinsi tujuan (dari laravolt/indonesia)
     * @param  int     $totalWeightGram  Total berat pesanan dalam gram
     * @param  int     $subtotal      Subtotal harga produk (rupiah), untuk cek gratis ongkir
     * @return array{
     *   zone: array|null,
     *   options: array,
     *   free_shipping: array{enabled: bool, minimum: int, qualifies: bool, saved: int},
     *   total_weight: int
     * }
     */
    public function calculate(string $provinceCode, int $totalWeightGram, int $subtotal): array
    {
        $zone          = ShippingZone::findByProvinceCode($provinceCode);
        $courierList   = ShippingSetting::getCourierServices();
        $freeEnabled   = ShippingSetting::isFreeShippingEnabled();
        $freeMinimum   = ShippingSetting::getFreeShippingMinimum();
        $qualifiesFree = ShippingSetting::qualifiesForFreeShipping($subtotal);

        $options = [];

        if ($zone) {
            foreach ($courierList as $courier) {
                $basePrice    = $zone->calculatePrice($totalWeightGram, $courier['price_multiplier'] ?? 1.0);
                $finalPrice   = $qualifiesFree ? 0 : $basePrice;
                $chargeWeight = $zone->getChargeableWeight($totalWeightGram);

                $options[] = [
                    'code'           => $courier['code'],
                    'name'           => $courier['name'],
                    'short_name'     => $courier['short_name'] ?? $courier['name'],
                    'etd'            => $courier['etd'],
                    'base_price'     => $basePrice,
                    'final_price'    => $finalPrice,
                    'is_free'        => $qualifiesFree,
                    'charge_weight'  => $chargeWeight,
                    'price_display'  => $finalPrice === 0 ? 'GRATIS' : 'Rp ' . number_format($finalPrice, 0, ',', '.'),
                ];
            }
        }

        return [
            'zone'     => $zone ? [
                'code' => $zone->zone_code,
                'name' => $zone->zone_name,
            ] : null,
            'options'  => $options,
            'free_shipping' => [
                'enabled'   => $freeEnabled,
                'minimum'   => $freeMinimum,
                'qualifies' => $qualifiesFree,
                'saved'     => $qualifiesFree && !empty($options) ? ($options[0]['base_price'] ?? 0) : 0,
            ],
            'total_weight' => $totalWeightGram,
        ];
    }

    /**
     * Hitung total berat dari kumpulan item pesanan.
     * Menggunakan product->weight (gram). Jika tidak ada, pakai default dari setting.
     *
     * @param  \Illuminate\Support\Collection|\App\Models\OrderItem[]  $items
     * @return int berat total dalam gram
     */
    public function calculateTotalWeight($items): int
    {
        $defaultWeight = ShippingSetting::getDefaultProductWeight();
        $total = 0;

        foreach ($items as $item) {
            $productWeight = (int) ($item->product->weight ?? $defaultWeight);
            if ($productWeight <= 0) {
                $productWeight = $defaultWeight;
            }
            $total += $productWeight * (int) $item->quantity;
        }

        return max($total, 1); // minimal 1 gram agar tidak error di kalkulasi
    }

    /**
     * Generate nomor resi otomatis.
     * Format: {PREFIX}{YYMM}{RANDOM_8_DIGIT}
     * Contoh: FRN250134AB78C123
     */
    public function generateTrackingNumber(): string
    {
        $prefix  = ShippingSetting::getTrackingPrefix();
        $date    = date('Ym'); // 202501
        $random  = strtoupper(substr(str_shuffle('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'), 0, 8));
        return $prefix . $date . $random;
    }

    /**
     * Dapatkan URL tracking di website ekspedisi berdasarkan kode kurir.
     */
    public function getTrackingUrl(string $courierCode, string $trackingNumber): ?string
    {
        $resi = urlencode($trackingNumber);

        $urls = [
            'jne_reg'     => "https://www.jne.co.id/id/tracking/trace?awbNumber={$resi}",
            'jne_yes'     => "https://www.jne.co.id/id/tracking/trace?awbNumber={$resi}",
            'jne_oke'     => "https://www.jne.co.id/id/tracking/trace?awbNumber={$resi}",
            'jnt_reg'     => "https://www.jet.co.id/track?awb={$resi}",
            'jnt_ez'      => "https://www.jet.co.id/track?awb={$resi}",
            'sicepat_reg' => "https://www.sicepat.com/checkAwb?awb={$resi}",
            'sicepat_best'=> "https://www.sicepat.com/checkAwb?awb={$resi}",
        ];

        // Normalisasi: cari prefix kurir
        $baseCode = $courierCode;
        foreach ($urls as $code => $url) {
            if (str_starts_with($courierCode, explode('_', $code)[0])) {
                $baseCode = $code;
                break;
            }
        }

        return $urls[$baseCode] ?? null;
    }

    /**
     * Ambil nama layanan kurir yang mudah dibaca.
     */
    public function getCourierDisplayName(string $courierCode): string
    {
        $courier = ShippingSetting::getCourierByCode($courierCode);
        return $courier ? $courier['short_name'] : strtoupper($courierCode);
    }

    /**
     * Ambil estimasi hari pengiriman dari kode kurir.
     */
    public function getEstimatedDays(string $courierCode): string
    {
        $courier = ShippingSetting::getCourierByCode($courierCode);
        return $courier['etd'] ?? '-';
    }
}
