<?php

namespace App\Http\Controllers;

use App\Models\Order;
use App\Models\OrderItem;
use App\Models\Carts;
use App\Models\Product;
use App\Models\Payment;
use App\Models\Review;
use App\Models\StockHistory;
use App\Models\User;
use App\Notifications\NewOrderNotification;
use App\Services\ShippingCalculatorService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class OrderController extends Controller
{
    // Status yang boleh dipilih admin saat update
    const ADMIN_STATUSES = ['processing', 'shipped', 'delivered', 'completed', 'cancelled'];

    public function __construct(private readonly ShippingCalculatorService $shippingCalc) {}

    public function index()
    {
        $user = Auth::user();

        if ($user->isAdmin()) {
            $orders = Order::with(['user', 'items.product', 'payment'])
                ->orderBy('created_at', 'desc')
                ->paginate(15);
        } else {
            $orders = Order::where('user_id', $user->id)
                ->with(['items.product', 'payment'])
                ->orderBy('created_at', 'desc')
                ->paginate(10);
        }

        return view('orders.index', compact('orders'));
    }

    public function show($id)
    {
        $order = Order::with(['user', 'items.product.category', 'payment'])->findOrFail($id);
        $user  = Auth::user();

        if (!$user->isAdmin() && $order->user_id != $user->id) {
            abort(403, 'Unauthorized');
        }

        // Kumpulkan product_id yang sudah direviw oleh user ini
        $reviewedProductIds = [];
        if ($order->isCompleted()) {
            $productIds = $order->items->pluck('product_id')->toArray();
            $reviewedProductIds = Review::where('user_id', $user->id)
                ->whereIn('product_id', $productIds)
                ->pluck('product_id')
                ->toArray();
        }

        return view('orders.show', compact('order', 'reviewedProductIds'));
    }

    /**
     * Simpan review dari halaman detail pesanan.
     */
    public function storeOrderReview(Request $request, $orderId, $productId)
    {
        $user  = Auth::user();
        $order = Order::with('items')->findOrFail($orderId);

        // Pastikan order milik user ini
        if ($order->user_id != $user->id) {
            abort(403, 'Unauthorized');
        }

        // Pastikan order sudah completed
        if (!$order->isCompleted()) {
            return back()->with('error', 'Ulasan hanya bisa diberikan setelah pesanan selesai.');
        }

        // Pastikan produk ada di order ini
        $inOrder = $order->items->pluck('product_id')->contains((int) $productId);
        if (!$inOrder) {
            return back()->with('error', 'Produk tidak ditemukan dalam pesanan ini.');
        }

        // Cek sudah pernah review produk ini belum
        $alreadyReviewed = Review::where('user_id', $user->id)
            ->where('product_id', $productId)
            ->exists();

        if ($alreadyReviewed) {
            return back()->with('error', 'Kamu sudah memberikan ulasan untuk produk ini.');
        }

        $request->validate([
            'rating'  => 'required|integer|min:1|max:5',
            'comment' => 'nullable|string|max:1000',
            'media'   => 'nullable|array',
            'media.*' => 'file|mimes:jpg,jpeg,png,webp,mp4,mov|max:10240',
        ]);

        $mediaPaths = [];
        if ($request->hasFile('media')) {
            foreach ($request->file('media') as $file) {
                $mediaPaths[] = $file->store('reviews/' . $productId, 'public');
            }
        }

        Review::create([
            'product_id' => $productId,
            'user_id'    => $user->id,
            'rating'     => $request->rating,
            'comment'    => $request->comment,
            'media'      => !empty($mediaPaths) ? $mediaPaths : null,
        ]);

        return redirect()->route('order.show', $orderId)
            ->with('success', 'Ulasan berhasil dikirim! Terima kasih 🎉');
    }

    /**
     * Checkout: buat Order + Payment (dengan kalkulasi ongkir)
     */
    public function checkout(Request $request)
    {
        $user = Auth::user();

        $request->validate([
            'selected_address_id' => 'required|exists:addresses,id',
            'payment_method'      => 'required|in:cash,card,transfer,ewallet,qris',
            'courier_code'        => 'required|string',
            'shipping_cost'       => 'required|integer|min:0',
            'order_note'          => 'nullable|string',
        ]);

        $address = \App\Models\Address::where('id', $request->selected_address_id)
            ->where('user_id', $user->id)
            ->first();

        if (!$address) {
            return $this->checkoutError($request, 'Alamat pengiriman tidak valid.');
        }

        // Validasi kurir
        $courierService = \App\Models\ShippingSetting::getCourierByCode($request->courier_code);
        if (!$courierService) {
            return $this->checkoutError($request, 'Layanan kurir tidak valid.');
        }

        $shippingCalc  = app(ShippingCalculatorService::class);
        $shippingCost  = (int) $request->shipping_cost;
        $estimatedDays = $shippingCalc->getEstimatedDays($request->courier_code);

        $isBuyNow      = false;
        $buyNowProduct = null;
        $buyNowQty     = 1;

        $selectedIds    = json_decode($request->input('selected_items', '[]'), true);
        $removedCartIds = json_decode($request->input('removed_items', '[]'), true) ?: [];

        $quantitiesOverride = json_decode($request->input('quantities', '{}'), true) ?: [];
        if (isset($quantitiesOverride['buynow'])) {
            $isBuyNow  = true;
            $buyNowQty = max(1, (int) $quantitiesOverride['buynow']);
        }

        if (!$isBuyNow && empty($selectedIds)) {
            $buyNowProductId = session('buynow_product_id');
            if ($buyNowProductId) {
                $isBuyNow = true;
            }
        }

        // ── BUY NOW ───────────────────────────────────────────────────────────
        if ($isBuyNow) {
            $buyNowProductId = session('buynow_product_id');
            if (!$buyNowProductId) {
                return $this->checkoutError($request, 'Sesi Buy Now tidak valid. Silakan coba lagi.');
            }

            $buyNowProduct = \App\Models\Product::find($buyNowProductId);
            if (!$buyNowProduct) {
                return $this->checkoutError($request, 'Produk tidak ditemukan.');
            }
            if ($buyNowProduct->stock < $buyNowQty) {
                return $this->checkoutError(
                    $request,
                    "Stok {$buyNowProduct->product_name} tidak mencukupi! Tersedia: {$buyNowProduct->stock}"
                );
            }

            // Hitung berat
            $defaultWeight = \App\Models\ShippingSetting::getDefaultProductWeight();
            $productWeight = max((int)($buyNowProduct->weight ?? $defaultWeight), 1);
            $totalWeight   = $productWeight * $buyNowQty;

            DB::beginTransaction();
            try {
                $productPrice = $buyNowProduct->price;
                $subtotal     = $productPrice * $buyNowQty;
                $totalPrice   = $subtotal + $shippingCost;

                // Cari zona pengiriman
                $zone         = \App\Models\ShippingZone::findByProvinceCode($address->province_code ?? '');
                $shippingZone = $zone?->zone_code ?? 'unknown';
                $courierName  = $courierService['short_name'] ?? $request->courier_code;

                // Generate nomor resi
                $trackingNumber = $shippingCalc->generateTrackingNumber();

                $order = Order::create([
                    'user_id'         => $user->id,
                    'status'          => Order::STATUS_PROCESSING,
                    'total_price'     => $totalPrice,
                    'order_note'      => $request->filled('order_note') ? trim($request->order_note) : null,
                    'tracking_number' => $trackingNumber,
                    'courier_code'    => $request->courier_code,
                    'courier_name'    => $courierName,
                    'shipping_cost'   => $shippingCost,
                    'total_weight'    => $totalWeight,
                    'estimated_days'  => $estimatedDays,
                    'shipping_zone'   => $shippingZone,
                    'address_id'      => $address->id,
                ]);

                if (!$order || !$order->order_id) {
                    throw new \Exception('Gagal membuat order.');
                }

                OrderItem::create([
                    'order_id'   => $order->order_id,
                    'product_id' => $buyNowProduct->product_id,
                    'quantity'   => $buyNowQty,
                    'subtotal'   => $subtotal,
                ]);

                $product     = Product::lockForUpdate()->find($buyNowProduct->product_id);
                $stockBefore = $product->stock;
                $product->decrement('stock', $buyNowQty);
                $stockAfter  = $product->fresh()->stock;

                StockHistory::create([
                    'product_id'     => $product->product_id,
                    'user_id'        => Auth::id(),
                    'type'           => 'out',
                    'source'         => 'order',
                    'quantity'       => $buyNowQty,
                    'stock_before'   => $stockBefore,
                    'stock_after'    => $stockAfter,
                    'reference_id'   => $order->order_id,
                    'reference_type' => Order::class,
                    'note'           => 'Buy Now - Order #' . $order->order_id,
                ]);

                $paymentMethod = $request->payment_method;
                $payment = Payment::create([
                    'order_id'       => $order->order_id,
                    'payment_method' => $paymentMethod,
                    'amount'         => $totalPrice,
                    'status'         => 'unpaid',
                ]);

                session()->forget(['buynow_product_id', 'checkout_selected_ids', 'selected_address_id']);

                if ($paymentMethod === 'cash') {
                    DB::commit();
                    $this->notifyAdmin($order);

                    if ($request->expectsJson() || $request->ajax()) {
                        return response()->json([
                            'success'      => true,
                            'redirect_url' => route('order.show', $order->order_id),
                            'message'      => 'Pesanan COD berhasil dibuat!',
                        ]);
                    }

                    return redirect()->route('order.show', $order->order_id)
                        ->with('success', 'Pesanan berhasil dibuat! Pembayaran dilakukan saat barang tiba.');
                }

                $snapData = $this->createMidtransSnap($order, $payment);
                $payment->update([
                    'midtrans_order_id'    => $snapData['midtrans_order_id'],
                    'midtrans_snap_token'  => $snapData['snap_token'],
                    'midtrans_payment_url' => $snapData['payment_url'],
                    'expired_at'           => now()->addDay(),
                ]);

                DB::commit();
                $this->notifyAdmin($order);

                return response()->json([
                    'success'          => true,
                    'snap_token'       => $snapData['snap_token'],
                    'payment_url'      => $snapData['payment_url'],
                    'order_show_url'   => route('order.show', $order->order_id),
                    'payment_show_url' => route('payment.show', $payment->id),
                ]);

            } catch (\Exception $e) {
                DB::rollBack();
                Log::error('Buy Now checkout failed: ' . $e->getMessage(), ['user_id' => $user->id]);
                return $this->checkoutError($request, 'Gagal membuat pesanan: ' . $e->getMessage());
            }
        }

        // ── CART ──────────────────────────────────────────────────────────────
        if (empty($selectedIds)) {
            $selectedIds = session('checkout_selected_ids', []);
        }

        $selectedIds = array_values(array_filter(
            (array) $selectedIds,
            fn($id) => !in_array((int)$id, array_map('intval', $removedCartIds))
        ));

        if (empty($selectedIds)) {
            return $this->checkoutError($request, 'Pilih minimal 1 produk untuk checkout!');
        }

        $cartItems = Carts::with('product')
            ->where('user_id', $user->id)
            ->whereIn('cart_id', $selectedIds)
            ->get();

        if ($cartItems->isEmpty()) {
            return $this->checkoutError($request, 'Keranjang belanja kosong!');
        }

        foreach ($cartItems as $cart) {
            $key = (string) $cart->cart_id;
            if (isset($quantitiesOverride[$key])) {
                $cart->quantity = max(1, (int) $quantitiesOverride[$key]);
            }
        }

        foreach ($cartItems as $cart) {
            if (!$cart->product) {
                return $this->checkoutError($request, 'Produk tidak ditemukan.');
            }
            if ($cart->product->stock < $cart->quantity) {
                return $this->checkoutError(
                    $request,
                    "Stok {$cart->product->product_name} tidak mencukupi! Tersedia: {$cart->product->stock}"
                );
            }
        }

        DB::beginTransaction();
        try {
            $productSubtotal = $cartItems->sum(fn($c) => $c->product->price * $c->quantity);
            $totalPrice      = $productSubtotal + $shippingCost;

            // Hitung total berat
            $defaultWeight = \App\Models\ShippingSetting::getDefaultProductWeight();
            $totalWeight   = $cartItems->sum(function ($c) use ($defaultWeight) {
                $w = max((int)($c->product->weight ?? $defaultWeight), 1);
                return $w * $c->quantity;
            });

            // Zona & kurir
            $zone         = \App\Models\ShippingZone::findByProvinceCode($address->province_code ?? '');
            $shippingZone = $zone?->zone_code ?? 'unknown';
            $courierName  = $courierService['short_name'] ?? $request->courier_code;

            // Generate nomor resi
            $trackingNumber = $shippingCalc->generateTrackingNumber();

            $order = Order::create([
                'user_id'         => $user->id,
                'status'          => Order::STATUS_PROCESSING,
                'total_price'     => $totalPrice,
                'order_note'      => $request->filled('order_note') ? trim($request->order_note) : null,
                'tracking_number' => $trackingNumber,
                'courier_code'    => $request->courier_code,
                'courier_name'    => $courierName,
                'shipping_cost'   => $shippingCost,
                'total_weight'    => $totalWeight,
                'estimated_days'  => $estimatedDays,
                'shipping_zone'   => $shippingZone,
                'address_id'      => $address->id,
            ]);

            if (!$order || !$order->order_id) {
                throw new \Exception('Gagal membuat order.');
            }

            foreach ($cartItems as $cart) {
                OrderItem::create([
                    'order_id'   => $order->order_id,
                    'product_id' => $cart->product_id,
                    'quantity'   => $cart->quantity,
                    'subtotal'   => $cart->product->price * $cart->quantity,
                ]);

                $product     = Product::lockForUpdate()->find($cart->product_id);
                $stockBefore = $product->stock;
                $product->decrement('stock', $cart->quantity);
                $stockAfter  = $product->fresh()->stock;

                StockHistory::create([
                    'product_id'     => $product->product_id,
                    'user_id'        => Auth::id(),
                    'type'           => 'out',
                    'source'         => 'order',
                    'quantity'       => $cart->quantity,
                    'stock_before'   => $stockBefore,
                    'stock_after'    => $stockAfter,
                    'reference_id'   => $order->order_id,
                    'reference_type' => Order::class,
                    'note'           => 'Stok dikurangi saat Order #' . $order->order_id . ' dibuat',
                ]);
            }

            Carts::where('user_id', $user->id)
                ->whereIn('cart_id', $selectedIds)
                ->delete();

            $paymentMethod = $request->payment_method;
            $payment = Payment::create([
                'order_id'       => $order->order_id,
                'payment_method' => $paymentMethod,
                'amount'         => $totalPrice,
                'status'         => 'unpaid',
            ]);

            if ($paymentMethod === 'cash') {
                DB::commit();
                session()->forget(['checkout_selected_ids', 'selected_address_id']);
                $this->notifyAdmin($order);

                if ($request->expectsJson() || $request->ajax()) {
                    return response()->json([
                        'success'      => true,
                        'redirect_url' => route('order.show', $order->order_id),
                        'message'      => 'Pesanan COD berhasil dibuat!',
                    ]);
                }

                return redirect()->route('order.show', $order->order_id)
                    ->with('success', 'Pesanan berhasil dibuat! Pembayaran dilakukan saat barang tiba.');
            }

            $snapData = $this->createMidtransSnap($order, $payment);
            $payment->update([
                'midtrans_order_id'    => $snapData['midtrans_order_id'],
                'midtrans_snap_token'  => $snapData['snap_token'],
                'midtrans_payment_url' => $snapData['payment_url'],
                'expired_at'           => now()->addDay(),
            ]);

            DB::commit();
            session()->forget(['checkout_selected_ids', 'selected_address_id']);
            $this->notifyAdmin($order);

            return response()->json([
                'success'          => true,
                'snap_token'       => $snapData['snap_token'],
                'payment_url'      => $snapData['payment_url'],
                'order_show_url'   => route('order.show', $order->order_id),
                'payment_show_url' => route('payment.show', $payment->id),
            ]);

        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Checkout failed: ' . $e->getMessage(), [
                'user_id' => $user->id,
                'file'    => $e->getFile(),
                'line'    => $e->getLine(),
            ]);
            return $this->checkoutError($request, 'Gagal membuat pesanan: ' . $e->getMessage());
        }
    }

    /**
     * Buy Now
     */
    public function buyNow(Request $request)
    {
        $user    = Auth::user();
        $product = Product::findOrFail($request->product_id);

        $addresses         = $user->addresses()->with(['province', 'city', 'district', 'village'])->latest()->get();
        $selectedAddressId = session('selected_address_id');

        $fakeCartItem = (object)[
            'product'  => $product,
            'quantity' => 1,
            'cart_id'  => null,
        ];

        session([
            'buynow_product_id'     => $product->product_id,
            'checkout_selected_ids' => ['buynow'], // ← tambah ini
        ]);

        return view('checkout.index', [
            'cartItems'         => collect([$fakeCartItem]),
            'selectedIds'       => [],
            'addresses'         => $addresses,
            'selectedAddressId' => $selectedAddressId,
            'isBuyNow'          => true,
            'buyNowProductId'   => $product->product_id,
        ]);
    }

    /**
     * Batalkan pesanan oleh user
     */
    public function cancel($id)
    {
        $order = Order::with('items')->lockForUpdate()->findOrFail($id);
        $user  = Auth::user();

        if (!$user->isAdmin() && $order->user_id != $user->id) {
            abort(403, 'Unauthorized');
        }

        if ($order->isCompleted() || $order->isCancelled()) {
            return back()->with('error', 'Order tidak bisa dibatalkan.');
        }

        DB::beginTransaction();
        try {
            $order->markAsCancelled();
            $this->restoreStock($order, 'Pengembalian stok akibat pembatalan Order #' . $order->order_id);

            if ($order->payment) {
                $order->payment->update(['status' => 'failed']);
            }

            DB::commit();
            return back()->with('success', 'Order berhasil dibatalkan.');
        } catch (\Exception $e) {
            DB::rollBack();
            Log::error('Cancel order failed: ' . $e->getMessage());
            return back()->with('error', 'Gagal membatalkan order.');
        }
    }

    /**
     * Update status order (admin)
     */
    public function updateStatus($order, Request $request)
    {
        $request->validate([
            'status' => 'required|in:' . implode(',', self::ADMIN_STATUSES),
        ]);

        $order = Order::with('items')->lockForUpdate()->findOrFail($order);

        DB::beginTransaction();
        try {
            $oldStatus = $order->status;
            $newStatus = $request->status;

            $order->update(['status' => $newStatus]);

            // COD completed → auto-paid
            if ($newStatus === 'completed'
                && $order->payment
                && $order->payment->payment_method === 'cash'
                && !$order->payment->isPaid()
            ) {
                $order->payment->update([
                    'status'       => 'paid',
                    'payment_date' => now(),
                ]);

                // ── Kirim invoice setelah COD otomatis dikonfirmasi paid akibat pesanan selesai ──
                /** @var \App\Http\Controllers\InvoiceController $invoiceCtrl */
                $invoiceCtrl = app(\App\Http\Controllers\InvoiceController::class);
                $invoiceCtrl->sendInvoiceAfterPayment($order->fresh());
            }

            // Cancelled → kembalikan stok
            if ($newStatus === 'cancelled' && $oldStatus !== 'cancelled') {
                $this->restoreStock($order, 'Pengembalian stok akibat pembatalan Order #' . $order->order_id);
                if ($order->payment) {
                    $order->payment->update(['status' => 'failed']);
                }
            }

            DB::commit();
            return back()->with('success', 'Status order berhasil diupdate.');
        } catch (\Exception $e) {
            DB::rollBack();
            return back()->with('error', 'Gagal update status: ' . $e->getMessage());
        }
    }

    /**
     * Update nomor resi dan/atau kurir oleh admin.
     * PUT /admin/orders/{order}/tracking
     */
    public function updateTracking(Request $request, $order)
    {
        $request->validate([
            'tracking_number' => 'nullable|string|max:50',
            'courier_code'    => 'nullable|string|max:30',
            'auto_generate'   => 'nullable|boolean',
        ]);

        $order = Order::findOrFail($order);

        $trackingNumber = $order->tracking_number;

        if ($request->boolean('auto_generate')) {
            $calc           = app(ShippingCalculatorService::class);
            $trackingNumber = $calc->generateTrackingNumber();
        } elseif ($request->filled('tracking_number')) {
            $trackingNumber = strtoupper(trim($request->tracking_number));
        }

        $updateData = ['tracking_number' => $trackingNumber];

        if ($request->filled('courier_code')) {
            $courier = \App\Models\ShippingSetting::getCourierByCode($request->courier_code);
            $updateData['courier_code']    = $request->courier_code;
            $updateData['courier_name']    = $courier['short_name'] ?? $request->courier_code;
            $updateData['estimated_days']  = $courier['etd'] ?? '-';
        }

        $order->update($updateData);

        if ($request->expectsJson() || $request->ajax()) {
            return response()->json([
                'success'         => true,
                'tracking_number' => $order->fresh()->tracking_number,
                'message'         => 'Nomor resi berhasil diperbarui.',
            ]);
        }

        return back()->with('success', 'Nomor resi berhasil diperbarui: ' . $trackingNumber);
    }

    public function adminIndex(Request $request)
    {
        $query = Order::with(['user', 'items', 'payment'])
            ->withCount('items')
            ->orderBy('created_at', 'desc');

        if ($request->filled('search')) {
            $s = $request->search;
            $query->where(function ($q) use ($s) {
                $q->where('order_id', 'like', "%{$s}%")
                  ->orWhereHas('user', fn($u) => $u->where('name', 'like', "%{$s}%"));
            });
        }

        if ($request->filled('status')) {
            $query->where('status', $request->status);
        }

        $orders = $query->paginate(15);
        return view('orders.admin.index', compact('orders'));
    }

    public function adminShow($order)
    {
        $order = Order::with(['user', 'items.product', 'payment'])->findOrFail($order);
        return view('orders.admin.show', compact('order'));
    }

    public function adminCancel($order)
    {
        $order = Order::with('items')->lockForUpdate()->findOrFail($order);

        if ($order->isCancelled()) {
            return back()->with('error', 'Order sudah dibatalkan.');
        }

        DB::beginTransaction();
        try {
            $order->markAsCancelled();
            $this->restoreStock($order, 'Admin membatalkan Order #' . $order->order_id);

            if ($order->payment) {
                $order->payment->update(['status' => 'failed']);
            }

            DB::commit();
            return back()->with('success', 'Order berhasil dibatalkan.');
        } catch (\Exception $e) {
            DB::rollBack();
            return back()->with('error', 'Gagal membatalkan order: ' . $e->getMessage());
        }
    }

    // ── PRIVATE HELPERS ───────────────────────────────────────────────────────

    private function restoreStock(Order $order, string $note): void
    {
        foreach ($order->items as $item) {
            $product = Product::lockForUpdate()->find($item->product_id);
            if (!$product) continue;

            $stockBefore = $product->stock;
            $product->increment('stock', $item->quantity);
            $stockAfter = $product->fresh()->stock;

            StockHistory::create([
                'product_id'     => $product->product_id,
                'user_id'        => Auth::id(),
                'type'           => 'in',
                'source'         => 'cancel',
                'quantity'       => $item->quantity,
                'stock_before'   => $stockBefore,
                'stock_after'    => $stockAfter,
                'reference_id'   => $order->order_id,
                'reference_type' => Order::class,
                'note'           => $note,
            ]);
        }
    }

    private function notifyAdmin(Order $order): void
    {
        try {
            $admin = User::where('role', 'LIKE', 'admin')->first();
            if ($admin) $admin->notify(new NewOrderNotification($order));
        } catch (\Exception $e) {
            Log::warning('Notifikasi gagal: ' . $e->getMessage());
        }
    }

    private function createMidtransSnap(Order $order, Payment $payment): array
    {
        \Midtrans\Config::$serverKey    = config('services.midtrans.server_key');
        \Midtrans\Config::$isProduction = config('services.midtrans.is_production');
        \Midtrans\Config::$isSanitized  = true;
        \Midtrans\Config::$is3ds        = true;

        $midtransOrderId = strtoupper($payment->payment_method) . '-' . $order->order_id . '-' . time();

        $enabledPayments = [
            'card'     => ['credit_card'],
            'qris'     => config('services.midtrans.is_production') ? ['qris'] : ['gopay'],
            'transfer' => ['bca_va', 'bni_va', 'bri_va', 'echannel', 'permata_va', 'other_va'],
            'ewallet'  => config('services.midtrans.is_production')
                ? ['gopay', 'shopeepay', 'qris', 'dana', 'linkaja', 'ovo']
                : ['gopay', 'shopeepay'],
        ][$payment->payment_method] ?? [];

        $itemDetails = $order->items->map(fn($item) => [
            'id'       => $item->product_id,
            'price'    => (int) ($item->subtotal / $item->quantity),
            'quantity' => $item->quantity,
            'name'     => substr($item->product->product_name, 0, 50),
        ])->toArray();

        // Tambahkan ongkos kirim sebagai item agar sum item_details = gross_amount
        if ($order->shipping_cost > 0) {
            $itemDetails[] = [
                'id'       => 'SHIPPING',
                'price'    => (int) $order->shipping_cost,
                'quantity' => 1,
                'name'     => 'Ongkos Kirim (' . ($order->courier_name ?? $order->courier_code ?? 'Kurir') . ')',
            ];
        }

        $params = [
            'transaction_details' => [
                'order_id'     => $midtransOrderId,
                'gross_amount' => (int) $order->total_price,
            ],
            'item_details'     => $itemDetails,
            'enabled_payments' => $enabledPayments,
            'customer_details' => [
                'first_name' => $order->user->name,
                'email'      => $order->user->email,
            ],
            'expiry' => [
                'duration' => $payment->payment_method === 'qris' ? 15 : 24,
                'unit'     => $payment->payment_method === 'qris' ? 'minutes' : 'hours',
            ],
        ];

        $snapToken  = \Midtrans\Snap::getSnapToken($params);
        $paymentUrl = (config('services.midtrans.is_production')
            ? 'https://app.midtrans.com/snap/v2/vtweb/'
            : 'https://app.sandbox.midtrans.com/snap/v2/vtweb/') . $snapToken;

        return [
            'midtrans_order_id' => $midtransOrderId,
            'snap_token'        => $snapToken,
            'payment_url'       => $paymentUrl,
        ];
    }

    private function checkoutError(Request $request, string $message)
    {
        if ($request->expectsJson() || $request->ajax()) {
            return response()->json(['success' => false, 'message' => $message], 422);
        }
        return redirect()->route('cart.checkout.get')->with('error', $message);
    }
}