<?php

namespace App\Http\Controllers;

use App\Models\Order;
use App\Models\Payment;
use App\Models\PaymentAccount;
use App\Models\Setting;
use App\Models\TaxSetting;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Schema;

class PaymentController extends Controller
{
    public function index(Request $request)
    {
        $query = Order::with(['designer', 'items', 'payments'])
            ->notCancelled()
            ->hasReceivables();

        if ($request->filled('search')) {
            $search = $request->search;
            $query->where(function ($q) use ($search) {
                $q->where('invoice_number', 'like', "%{$search}%")
                    ->orWhere('customer_name', 'like', "%{$search}%");
            });
        }

        $orders = $query->latest()->paginate(100)->withQueryString();

        return view('pages.kasir.index', [
            'title' => 'Tagihan',
            'orders' => $orders,
        ]);
    }

    public function show(Order $order)
    {
        if ($order->isCancelled()) {
            return redirect()->route('kasir.pelunasan')
                ->with('error', 'Order yang dibatalkan tidak bisa diproses pembayaran.');
        }

        if ($order->isPaid()) {
            return redirect()->route('kasir.history')
                ->with('info', 'Order ini sudah dibayar.');
        }

        $order->load(['designer', 'items']);

        $bankAccounts = PaymentAccount::active()->ofType(PaymentAccount::TYPE_BANK)->get();
        $ewalletAccounts = PaymentAccount::active()->ofType(PaymentAccount::TYPE_EWALLET)->get();
        $taxSetting = $this->getCurrentTaxSetting();

        return view('pages.kasir.payment', [
            'title' => 'Pembayaran #' . $order->invoice_number,
            'order' => $order,
            'bankAccounts' => $bankAccounts,
            'ewalletAccounts' => $ewalletAccounts,
            'taxSetting' => $taxSetting,
        ]);
    }

    public function process(Request $request, Order $order)
    {
        if ($order->isCancelled()) {
            return redirect()->route('kasir.pelunasan')
                ->with('error', 'Order yang dibatalkan tidak bisa diproses pembayaran.');
        }

        if ($order->isPaid()) {
            return redirect()->route('kasir.index')
                ->with('error', 'Order ini sudah dibayar.');
        }

        $validated = $request->validate([
            'payment_type' => ['nullable', 'in:full,dp'],
            'due_date' => ['nullable', 'date', 'after_or_equal:today', 'required_if:payment_type,dp'],
            'payments' => ['required', 'array', 'min:1'],
            'payments.*.method' => ['required', 'in:cash,transfer,ewallet'],
            'payments.*.amount' => ['required', 'numeric', 'min:1'],
            'payments.*.account_id' => ['nullable', 'exists:payment_accounts,id'],
            'use_ppn' => ['nullable'],
            'ppn_rate' => ['nullable', 'numeric', 'min:0', 'max:100'],
        ], [
            'payments.required' => 'Minimal satu pembayaran harus diisi.',
            'payments.*.amount.min' => 'Jumlah pembayaran harus lebih dari 0.',
            'due_date.required_if' => 'Tanggal jatuh tempo wajib diisi untuk pembayaran DP / Cicilan.',
            'due_date.after_or_equal' => 'Tanggal jatuh tempo tidak boleh kurang dari hari ini.',
        ]);

        $this->applyPpnBeforePayment($request, $order);
        $order->refresh();

        $remainingBalance = $order->getRemainingBalance();

        $totalPaid = collect($validated['payments'])->sum('amount');
        $paymentType = $validated['payment_type'] ?? 'full';

        if ($totalPaid < $remainingBalance) {
            if (!$order->is_credit || $paymentType !== 'dp') {
                return back()->withErrors([
                    'payments' => 'Jumlah bayar tidak boleh kurang dari total tagihan (Rp ' . number_format($remainingBalance, 0, ',', '.') . ').'
                ])->withInput();
            }
        }

        DB::transaction(function () use ($validated, $request, $order, $paymentType) {
            foreach ($validated['payments'] as $paymentData) {
                if ((float) $paymentData['amount'] > 0) {
                    $order->addPayment(
                        $request->user(),
                        (float) $paymentData['amount'],
                        $paymentData['method'],
                        null,
                        null,
                        isset($paymentData['account_id']) ? (int) $paymentData['account_id'] : null
                    );
                }
            }

            if ($paymentType === 'dp' && !empty($validated['due_date'])) {
                $order->update([
                    'due_date' => $validated['due_date'],
                ]);
            }

            if ($order->fresh()->isPaid()) {
                $order->update([
                    'due_date' => null,
                ]);
            }
        });

        if ($order->fresh()->isPaid()) {
            return redirect()->route('kasir.receipt', $order)
                ->with('success', 'Pembayaran berhasil. Order LUNAS.');
        }

        return redirect()->route('kasir.index')
            ->with('success', 'Pembayaran DP/Cicilan berhasil. Sisa tagihan: Rp ' . number_format($order->fresh()->getRemainingBalance(), 0, ',', '.'));
    }

    public function receipt(Order $order)
    {
        if ($order->isCancelled()) {
            return redirect()->route('kasir.pelunasan')
                ->with('error', 'Order yang dibatalkan tidak memiliki struk pelunasan aktif.');
        }

        if (!$order->isPaid()) {
            return redirect()->route('kasir.show', $order)
                ->with('error', 'Order belum dibayar.');
        }

        $order->load(['designer', 'cashier', 'items']);

        return view('pages.kasir.receipt', [
            'title' => 'Cetak Struk #' . $order->invoice_number,
            'order' => $order,
        ]);
    }

    public function history(Request $request)
    {
        $query = Order::with(['designer', 'cashier', 'items', 'payments.cashier'])
            ->notCancelled()
            ->paid();

        if ($request->filled('search')) {
            $search = $request->search;
            $query->where(function ($q) use ($search) {
                $q->where('invoice_number', 'like', "%{$search}%")
                    ->orWhere('customer_name', 'like', "%{$search}%");
            });
        }

        if ($request->filled('date')) {
            $query->whereDate('paid_at', $request->date);
        }

        $orders = $query->latest('paid_at')->paginate(50)->withQueryString();
        // Pendapatan hari ini = omset bersih tanpa PPN.
        // PPN tidak dihitung sebagai omset, karena PPN adalah pajak keluaran/titipan.
        $todayRevenue = $this->getTodayNetRevenue();

        return view('pages.kasir.history', [
            'title' => 'Riwayat Pembayaran',
            'orders' => $orders,
            'todayRevenue' => $todayRevenue,
        ]);
    }

    public function pelunasan(Request $request)
    {
        $perPage = (int) $request->input('per_page', 100);
        $type = $request->input('type', 'credit');
        $tab = $request->input('tab', 'active');
        $selectedCustomer = $request->input('customer');

        $ordersQuery = Order::with(['designer', 'customer', 'items', 'payments.cashier'])
            ->notCancelled();

        if ($tab === 'history') {
            $ordersQuery->paid();
        } else {
            $ordersQuery->withReceivables();
        }

        if ($type === 'credit') {
            $ordersQuery->where('is_credit', true);
        } elseif ($type === 'non_credit') {
            $ordersQuery->where('is_credit', false);
        }

        if (filled($selectedCustomer)) {
            $ordersQuery->where('customer_name', $selectedCustomer);
        }

        $orders = $ordersQuery
            ->latest($tab === 'history' ? 'paid_at' : 'created_at')
            ->paginate($perPage)
            ->withQueryString();

        $summaryQuery = Order::with(['payments'])
            ->notCancelled()
            ->withReceivables();

        if ($type === 'credit') {
            $summaryQuery->where('is_credit', true);
        } elseif ($type === 'non_credit') {
            $summaryQuery->where('is_credit', false);
        }

        if (filled($selectedCustomer)) {
            $summaryQuery->where('customer_name', $selectedCustomer);
        }

        $summaryOrders = $summaryQuery->get();

        $summary = [
            'total_orders' => $summaryOrders->count(),
            'total_receivables' => $summaryOrders->sum(function ($order) {
                return $this->getRemainingBalanceFromLoadedOrder($order);
            }),
        ];

        $allCustomersQuery = Order::query()
            ->notCancelled()
            ->withReceivables();

        if ($type === 'credit') {
            $allCustomersQuery->where('is_credit', true);
        } elseif ($type === 'non_credit') {
            $allCustomersQuery->where('is_credit', false);
        }

        $allCustomers = $allCustomersQuery
            ->select('customer_name')
            ->distinct()
            ->orderBy('customer_name')
            ->pluck('customer_name');

        $bankAccounts = PaymentAccount::active()->ofType(PaymentAccount::TYPE_BANK)->get();
        $ewalletAccounts = PaymentAccount::active()->ofType(PaymentAccount::TYPE_EWALLET)->get();

        return view('pages.kasir.pelunasan', [
            'title' => $tab === 'history' ? 'Riwayat Pelunasan' : 'Pelunasan',
            'orders' => $orders,
            'summary' => $summary,
            'currentType' => $type,
            'currentTab' => $tab,
            'allCustomers' => $allCustomers,
            'bankAccounts' => $bankAccounts,
            'ewalletAccounts' => $ewalletAccounts,
        ]);
    }

    public function processPelunasanPayment(Request $request, Order $order)
    {
        if ($order->isCancelled()) {
            return redirect()->route('kasir.pelunasan')
                ->with('error', 'Order yang dibatalkan tidak bisa diproses pembayaran.');
        }

        if ($order->isPaid() || (float) $order->getRemainingBalance() <= 0) {
            return redirect()->route('kasir.pelunasan')
                ->with('error', 'Order ini sudah lunas atau tidak memiliki sisa tagihan.');
        }

        $remainingBalance = (float) $order->getRemainingBalance();

        $validated = $request->validate([
            'payments' => ['nullable', 'array', 'min:1'],
            'payments.*.method' => ['required_with:payments', 'in:cash,transfer,ewallet'],
            'payments.*.amount' => ['required_with:payments', 'numeric', 'min:1'],
            'payments.*.account_id' => ['nullable', 'exists:payment_accounts,id'],

            'amount' => ['nullable', 'numeric', 'min:1'],
            'payment_method' => ['nullable', 'in:cash,transfer,ewallet'],
            'payment_account_id' => ['nullable', 'exists:payment_accounts,id'],
            'cash_received' => ['nullable', 'numeric', 'min:0'],

            'proof' => ['nullable', 'image', 'max:2048'],
            'notes' => ['nullable', 'string', 'max:500'],
            'use_ppn' => ['nullable'],
            'ppn_rate' => ['nullable', 'numeric', 'min:0', 'max:100'],
        ], [
            'payments.*.method.required_with' => 'Metode pembayaran wajib diisi.',
            'payments.*.amount.required_with' => 'Jumlah pembayaran wajib diisi.',
            'payments.*.amount.min' => 'Jumlah tiap split pembayaran harus lebih dari 0.',
        ]);

        $this->applyPpnBeforePayment($request, $order);
        $order->refresh();
        $remainingBalance = (float) $order->getRemainingBalance();

        $payments = collect($validated['payments'] ?? [])
            ->map(function ($payment) {
                return [
                    'method' => (string) ($payment['method'] ?? 'cash'),
                    'amount' => (float) ($payment['amount'] ?? 0),
                    'account_id' => isset($payment['account_id']) && $payment['account_id'] !== ''
                        ? (int) $payment['account_id']
                        : null,
                ];
            })
            ->filter(fn ($payment) => $payment['amount'] > 0)
            ->values();

        if ($payments->isEmpty() && !empty($validated['amount']) && !empty($validated['payment_method'])) {
            $payments = collect([[
                'method' => (string) $validated['payment_method'],
                'amount' => (float) $validated['amount'],
                'account_id' => isset($validated['payment_account_id']) && $validated['payment_account_id'] !== ''
                    ? (int) $validated['payment_account_id']
                    : null,
            ]]);
        }

        if ($payments->isEmpty()) {
            return back()->withErrors([
                'payments' => 'Minimal satu pembayaran harus diisi.',
            ])->withInput();
        }

        foreach ($payments as $idx => $payment) {
            if (in_array($payment['method'], ['transfer', 'ewallet'], true) && empty($payment['account_id'])) {
                return back()->withErrors([
                    "payments.$idx.account_id" => $payment['method'] === 'transfer'
                        ? 'Rekening bank wajib dipilih untuk pembayaran transfer.'
                        : 'Akun e-wallet wajib dipilih untuk pembayaran e-wallet.',
                ])->withInput();
            }
        }

        $totalPaid = (float) $payments->sum('amount');

        if ($totalPaid > $remainingBalance) {
            return back()->withErrors([
                'payments' => 'Total pembayaran tidak boleh melebihi sisa tagihan Rp ' . number_format($remainingBalance, 0, ',', '.'),
            ])->withInput();
        }

        $cashSplitsTotal = (float) $payments
            ->filter(fn ($payment) => $payment['method'] === 'cash')
            ->sum('amount');

        $cashReceivedInput = (float) ($validated['cash_received'] ?? 0);

        if ($cashSplitsTotal > 0) {
            if ($payments->count() === 1 && $payments->first()['method'] === 'cash') {
                if ($cashReceivedInput < $cashSplitsTotal) {
                    return back()->withErrors([
                        'cash_received' => 'Uang diterima tidak boleh kurang dari jumlah bayar.',
                    ])->withInput();
                }
            } elseif ($cashReceivedInput > 0 && $cashReceivedInput < $cashSplitsTotal) {
                return back()->withErrors([
                    'cash_received' => 'Uang diterima tidak boleh kurang dari total pembayaran tunai.',
                ])->withInput();
            }
        }

        $cashReceived = 0.0;
        $changeAmount = 0.0;

        if ($cashSplitsTotal > 0) {
            if ($cashReceivedInput > 0) {
                $cashReceived = $cashReceivedInput;
                $changeAmount = max($cashReceivedInput - $cashSplitsTotal, 0);
            } else {
                $cashReceived = $cashSplitsTotal;
                $changeAmount = 0.0;
            }
        } else {
            $cashReceived = $totalPaid;
            $changeAmount = 0.0;
        }

        $proofPath = null;
        if ($request->hasFile('proof')) {
            $proofPath = $request->file('proof')->store('payments', 'public');
        }

        DB::transaction(function () use ($request, $order, $payments, $validated, $proofPath) {
            foreach ($payments as $payment) {
                $order->addPayment(
                    $request->user(),
                    (float) $payment['amount'],
                    $payment['method'],
                    $proofPath,
                    $validated['notes'] ?? null,
                    $payment['account_id'] ?? null
                );
            }
        });

        $paymentMethod = $payments->count() > 1 ? 'split' : (string) $payments->first()['method'];

        return redirect()->route('kasir.pelunasan.receiptBatch', [
            'order_ids' => [$order->id],
            'paid_amount' => $totalPaid,
            'cash_received' => $cashReceived,
            'change_amount' => $changeAmount,
            'payment_method' => $paymentMethod,
            'split_summary' => $payments->count() > 1
                ? base64_encode(json_encode($payments->values()->all()))
                : null,
        ])->with('success', 'Pembayaran berhasil dicatat.');
    }

    public function processPelunasanBatchPayment(Request $request)
    {
        $validated = $request->validate([
            'order_ids' => ['required', 'string'],
            'payments' => ['required', 'array', 'min:1'],
            'payments.*.method' => ['required', 'in:cash,transfer,ewallet'],
            'payments.*.amount' => ['required', 'numeric', 'min:1'],
            'payments.*.account_id' => ['nullable', 'exists:payment_accounts,id'],
            'proof' => ['nullable', 'image', 'max:2048'],
            'notes' => ['nullable', 'string', 'max:500'],
        ], [
            'payments.required' => 'Minimal satu split pembayaran harus diisi.',
            'payments.*.amount.min' => 'Jumlah tiap split pembayaran harus lebih dari 0.',
        ]);

        $orderIds = json_decode($validated['order_ids'], true);
        if (!is_array($orderIds) || empty($orderIds)) {
            return back()->withErrors([
                'order_ids' => 'Tidak ada order yang dipilih.',
            ])->withInput();
        }

        $orders = Order::with(['payments'])
            ->notCancelled()
            ->withReceivables()
            ->whereIn('id', $orderIds)
            ->get()
            ->sortBy(function ($order) use ($orderIds) {
                return array_search($order->id, $orderIds, true);
            })
            ->values();

        if ($orders->isEmpty()) {
            return back()->withErrors([
                'order_ids' => 'Order tidak ditemukan, sudah lunas, atau sudah dibatalkan.',
            ])->withInput();
        }

        $payments = collect($validated['payments'])
            ->map(function ($payment) {
                return [
                    'method' => $payment['method'],
                    'amount' => (float) ($payment['amount'] ?? 0),
                    'account_id' => isset($payment['account_id']) && $payment['account_id'] !== ''
                        ? (int) $payment['account_id']
                        : null,
                ];
            })
            ->filter(fn ($payment) => $payment['amount'] > 0)
            ->values();

        if ($payments->isEmpty()) {
            return back()->withErrors([
                'payments' => 'Minimal satu split pembayaran harus memiliki nominal lebih dari 0.',
            ])->withInput();
        }

        foreach ($payments as $idx => $payment) {
            if (in_array($payment['method'], ['transfer', 'ewallet'], true) && empty($payment['account_id'])) {
                return back()->withErrors([
                    "payments.$idx.account_id" => $payment['method'] === 'transfer'
                        ? 'Rekening bank wajib dipilih untuk split pembayaran transfer.'
                        : 'Akun e-wallet wajib dipilih untuk split pembayaran e-wallet.',
                ])->withInput();
            }
        }

        $totalRemaining = (float) $orders->sum(function ($order) {
            return (float) $order->getRemainingBalance();
        });

        $totalPaid = (float) $payments->sum('amount');
        $changeAmount = max($totalPaid - $totalRemaining, 0);

        $proofPath = null;
        if ($request->hasFile('proof')) {
            $proofPath = $request->file('proof')->store('payments', 'public');
        }

        $remainingSplits = $payments
            ->map(function ($payment) {
                return [
                    'method' => $payment['method'],
                    'amount_left' => (float) $payment['amount'],
                    'account_id' => $payment['account_id'],
                ];
            })
            ->values()
            ->all();

        $paidOrderIds = [];

        DB::transaction(function () use ($orders, $request, $validated, &$remainingSplits, &$paidOrderIds, $proofPath) {
            foreach ($orders as $order) {
                $orderRemaining = (float) $order->getRemainingBalance();

                if ($orderRemaining <= 0) {
                    continue;
                }

                $paidOnThisOrder = 0;

                foreach ($remainingSplits as $index => $split) {
                    if ($orderRemaining <= 0) {
                        break;
                    }

                    $amountLeft = (float) ($remainingSplits[$index]['amount_left'] ?? 0);

                    if ($amountLeft <= 0) {
                        continue;
                    }

                    $paymentForOrder = min($amountLeft, $orderRemaining);

                    if ($paymentForOrder <= 0) {
                        continue;
                    }

                    $order->addPayment(
                        $request->user(),
                        $paymentForOrder,
                        $split['method'],
                        $proofPath,
                        $validated['notes'] ?? null,
                        $split['account_id'] ?? null
                    );

                    $remainingSplits[$index]['amount_left'] = $amountLeft - $paymentForOrder;
                    $orderRemaining -= $paymentForOrder;
                    $paidOnThisOrder += $paymentForOrder;
                }

                if ($paidOnThisOrder > 0) {
                    $paidOrderIds[] = $order->id;
                }
            }
        });

        if (empty($paidOrderIds)) {
            return back()->withErrors([
                'order_ids' => 'Tidak ada pembayaran yang berhasil dialokasikan.',
            ])->withInput();
        }

        return redirect()->route('kasir.pelunasan.receiptBatch', [
            'order_ids' => $paidOrderIds,
            'paid_amount' => $totalPaid,
            'cash_received' => $totalPaid,
            'change_amount' => $changeAmount,
            'payment_method' => 'split',
            'split_summary' => base64_encode(json_encode($payments->values()->all())),
        ])->with('success', 'Pembayaran batch split berhasil dicatat.');
    }

    public function receiptBatch(Request $request)
    {
        $orderIds = $request->input('order_ids');
        if (!is_array($orderIds)) {
            $orderIds = [$orderIds];
        }

        $orders = Order::with(['designer', 'cashier', 'items', 'payments.cashier', 'payments.paymentAccount'])
            ->notCancelled()
            ->whereIn('id', $orderIds)
            ->get();

        if ($orders->isEmpty()) {
            return redirect()->route('kasir.pelunasan')->with('error', 'Tagihan tidak ditemukan.');
        }

        $rawSplitSummary = $request->input('split_summary');
        $splitSummary = [];

        if (!empty($rawSplitSummary)) {
            $decoded = json_decode(base64_decode((string) $rawSplitSummary), true);
            if (is_array($decoded)) {
                $splitSummary = collect($decoded)
                    ->map(function ($item) {
                        $method = (string) ($item['method'] ?? 'cash');

                        $label = match ($method) {
                            'cash' => 'Tunai',
                            'transfer' => 'Transfer Bank',
                            'ewallet' => 'E-Wallet',
                            default => ucfirst($method),
                        };

                        $accountLabel = null;
                        if (!empty($item['account_id'])) {
                            $account = PaymentAccount::find((int) $item['account_id']);
                            if ($account) {
                                $accountLabel = method_exists($account, 'getFullLabel')
                                    ? $account->getFullLabel()
                                    : ($account->name ?? null);
                            }
                        }

                        return [
                            'method' => $method,
                            'method_label' => $label,
                            'amount' => (float) ($item['amount'] ?? 0),
                            'account_id' => isset($item['account_id']) && $item['account_id'] !== ''
                                ? (int) $item['account_id']
                                : null,
                            'account_label' => $accountLabel,
                        ];
                    })
                    ->filter(fn ($item) => $item['amount'] > 0)
                    ->values()
                    ->all();
            }
        }

        $paymentMethod = (string) $request->input('payment_method', 'cash');

        $singleMethodLabel = match ($paymentMethod) {
            'cash' => 'Tunai',
            'transfer' => 'Transfer Bank',
            'ewallet' => 'E-Wallet',
            'split' => 'Split Payment',
            default => ucfirst($paymentMethod),
        };

        $batchPaymentInfo = [
            'paid_amount' => (float) $request->input('paid_amount', 0),
            'cash_received' => (float) $request->input('cash_received', 0),
            'change_amount' => (float) $request->input('change_amount', 0),
            'payment_method' => $paymentMethod,
            'payment_method_label' => $singleMethodLabel,
            'is_split_payment' => !empty($splitSummary),
            'split_summary' => $splitSummary,
            'split_total' => collect($splitSummary)->sum('amount'),
            'order_count' => $orders->count(),
        ];

        return view('pages.kasir.receipt_batch', [
            'title' => 'Cetak Struk Batch',
            'orders' => $orders,
            'batchPaymentInfo' => $batchPaymentInfo,
        ]);
    }

    public function printBatch(Request $request)
    {
        $orderIds = $request->input('order_ids');
        if (!is_array($orderIds)) {
            $orderIds = explode(',', (string) $orderIds);
        }

        $orders = Order::with(['designer', 'cashier', 'items', 'payments.cashier', 'payments.paymentAccount'])
            ->notCancelled()
            ->whereIn('id', $orderIds)
            ->get();

        if ($orders->isEmpty()) {
            return back()->with('error', 'Tidak ada data untuk dicetak.');
        }

        $rawSplitSummary = $request->input('split_summary');
        $splitSummary = [];

        if (!empty($rawSplitSummary)) {
            $decoded = json_decode(base64_decode((string) $rawSplitSummary), true);
            if (is_array($decoded)) {
                $splitSummary = collect($decoded)
                    ->map(function ($item) {
                        $method = (string) ($item['method'] ?? 'cash');

                        $label = match ($method) {
                            'cash' => 'Tunai',
                            'transfer' => 'Transfer Bank',
                            'ewallet' => 'E-Wallet',
                            default => ucfirst($method),
                        };

                        $accountLabel = null;
                        if (!empty($item['account_id'])) {
                            $account = PaymentAccount::find((int) $item['account_id']);
                            if ($account) {
                                $accountLabel = method_exists($account, 'getFullLabel')
                                    ? $account->getFullLabel()
                                    : ($account->name ?? null);
                            }
                        }

                        return [
                            'method' => $method,
                            'method_label' => $label,
                            'amount' => (float) ($item['amount'] ?? 0),
                            'account_id' => isset($item['account_id']) && $item['account_id'] !== ''
                                ? (int) $item['account_id']
                                : null,
                            'account_label' => $accountLabel,
                        ];
                    })
                    ->filter(fn ($item) => $item['amount'] > 0)
                    ->values()
                    ->all();
            }
        }

        $paymentMethod = (string) $request->input('payment_method', 'cash');

        $singleMethodLabel = match ($paymentMethod) {
            'cash' => 'Tunai',
            'transfer' => 'Transfer Bank',
            'ewallet' => 'E-Wallet',
            'split' => 'Split Payment',
            default => ucfirst($paymentMethod),
        };

        $batchPaymentInfo = [
            'paid_amount' => (float) $request->input('paid_amount', 0),
            'cash_received' => (float) $request->input('cash_received', 0),
            'change_amount' => (float) $request->input('change_amount', 0),
            'payment_method' => $paymentMethod,
            'payment_method_label' => $singleMethodLabel,
            'is_split_payment' => !empty($splitSummary),
            'split_summary' => $splitSummary,
            'split_total' => collect($splitSummary)->sum('amount'),
            'order_count' => $orders->count(),
        ];

        return view('print.nota_batch', [
            'orders' => $orders,
            'shopName' => Setting::getShopName(),
            'shopAddress' => Setting::getShopAddress(),
            'shopPhone' => Setting::getShopPhone(),
            'shopFooter' => Setting::getShopFooter(),
            'shopLogo' => Setting::getShopLogo(),
            'batchPaymentInfo' => $batchPaymentInfo,
        ]);
    }

    public function updatePayment(Request $request, Payment $payment)
    {
        $validated = $request->validate([
            'amount' => ['required', 'numeric', 'min:1'],
            'payment_method' => ['required', 'in:cash,transfer,ewallet'],
            'notes' => ['nullable', 'string', 'max:500'],
            'proof' => ['nullable', 'image', 'max:2048'],
        ]);

        DB::transaction(function () use ($request, $payment, $validated) {
            $updateData = [
                'amount' => $validated['amount'],
                'payment_method' => $validated['payment_method'],
                'notes' => $validated['notes'] ?? null,
            ];

            if ($request->hasFile('proof')) {
                if ($payment->proof_path) {
                    Storage::disk('public')->delete($payment->proof_path);
                }

                $updateData['proof_path'] = $request->file('proof')->store('payments', 'public');
            }

            $payment->update($updateData);

            if ($payment->order && !$payment->order->isCancelled()) {
                $this->syncOrderPaymentStatus($payment->order, $request->user()->id);
            }
        });

        $order = $payment->order?->fresh();

        if ($order && $this->isOrderFullyPaid($order)) {
            return redirect()->route('kasir.history')
                ->with('success', 'Pembayaran berhasil diperbarui.');
        }

        return redirect()->route('kasir.index')
            ->with('success', 'Pembayaran berhasil diperbarui. Order kembali ke daftar tagihan.');
    }

    public function destroyPayment(Payment $payment)
    {
        $order = $payment->order;

        DB::transaction(function () use ($payment, $order) {
            if ($payment->proof_path) {
                Storage::disk('public')->delete($payment->proof_path);
            }

            $payment->delete();

            if ($order && !$order->isCancelled()) {
                $this->syncOrderPaymentStatus($order);
            }
        });

        $order = $order?->fresh();

        if ($order && $this->isOrderFullyPaid($order)) {
            return redirect()->route('kasir.history')
                ->with('success', 'Refund berhasil. Status order tetap lunas.');
        }

        return redirect()->route('kasir.index')
            ->with('success', 'Refund berhasil. Order kembali ke daftar tagihan.');
    }

    private function syncOrderPaymentStatus(Order $order, ?int $cashierId = null): void
    {
        $order->refresh();

        $totalPaid = method_exists($order, 'getTotalPaid')
            ? (float) $order->getTotalPaid()
            : (float) $order->payments()->sum('amount');

        $totalPrice = (float) ($order->total_price ?? 0);
        $remaining = max($totalPrice - $totalPaid, 0);

        if ($totalPaid <= 0) {
            $order->update([
                'payment_status' => $this->resolveUnpaidStatus($order),
                'paid_at' => null,
                'cashier_id' => null,
            ]);
            return;
        }

        if ($remaining <= 0) {
            $order->update([
                'payment_status' => $this->resolvePaidStatus($order),
                'paid_at' => $order->paid_at ?: now(),
                'cashier_id' => $cashierId ?? $order->cashier_id,
            ]);
            return;
        }

        $order->update([
            'payment_status' => $this->resolvePartialStatus($order),
            'paid_at' => null,
            'cashier_id' => $cashierId ?? $order->cashier_id,
        ]);
    }

    private function getTodayNetRevenue(): float
    {
        $query = Order::today()->notCancelled()->paid();

        try {
            if (
                Schema::hasTable('orders')
                && Schema::hasColumn('orders', 'use_ppn')
                && Schema::hasColumn('orders', 'subtotal_before_ppn')
                && Schema::hasColumn('orders', 'ppn_amount')
                && Schema::hasColumn('orders', 'total_price')
            ) {
                return (float) $query->sum(DB::raw("
                    CASE
                        WHEN use_ppn = 1 AND subtotal_before_ppn IS NOT NULL AND subtotal_before_ppn > 0
                            THEN subtotal_before_ppn
                        WHEN use_ppn = 1 AND ppn_amount IS NOT NULL AND ppn_amount > 0 AND total_price >= ppn_amount
                            THEN total_price - ppn_amount
                        ELSE total_price
                    END
                "));
            }
        } catch (\Throwable $e) {
            // fallback ke total_price jika struktur kolom belum tersedia
        }

        return (float) $query->sum('total_price');
    }

    private function getCurrentTaxSetting(): ?TaxSetting
    {
        try {
            return TaxSetting::current();
        } catch (\Throwable $e) {
            return null;
        }
    }

    private function applyPpnBeforePayment(Request $request, Order $order): void
    {
        if (!Schema::hasTable('orders')) {
            return;
        }

        $requiredColumns = ['use_ppn', 'ppn_rate', 'ppn_amount', 'subtotal_before_ppn', 'total_price'];

        foreach ($requiredColumns as $column) {
            if (!Schema::hasColumn('orders', $column)) {
                return;
            }
        }

        $totalPaid = method_exists($order, 'getTotalPaid')
            ? (float) $order->getTotalPaid()
            : (float) $order->payments()->sum('amount');

        // PPN hanya boleh dipilih saat pembayaran pertama agar data piutang/laporan tidak berubah di tengah jalan.
        if ($totalPaid > 0) {
            return;
        }

        $taxSetting = $this->getCurrentTaxSetting();

        $usePpn = $request->boolean('use_ppn');
        $rate = $usePpn
            ? (float) ($request->input('ppn_rate') ?: ($taxSetting->rate ?? 11))
            : 0;

        $baseTotal = $this->getOrderBaseTotalForPpn($order);
        $ppnAmount = $usePpn ? round($baseTotal * ($rate / 100), 2) : 0;

        $order->forceFill([
            'subtotal_before_ppn' => $baseTotal,
            'use_ppn' => $usePpn,
            'ppn_rate' => $usePpn ? $rate : null,
            'ppn_amount' => $ppnAmount,
            'total_price' => $baseTotal + $ppnAmount,
        ])->save();
    }

    private function getOrderBaseTotalForPpn(Order $order): float
    {
        $subtotalBeforePpn = (float) ($order->subtotal_before_ppn ?? 0);

        if ($subtotalBeforePpn > 0) {
            return $subtotalBeforePpn;
        }

        $totalPrice = (float) ($order->total_price ?? 0);
        $ppnAmount = (float) ($order->ppn_amount ?? 0);

        if ((bool) ($order->use_ppn ?? false) && $ppnAmount > 0 && $totalPrice >= $ppnAmount) {
            return $totalPrice - $ppnAmount;
        }

        return $totalPrice;
    }

    private function getRemainingBalanceFromLoadedOrder(Order $order): float
    {
        $totalPrice = (float) ($order->total_price ?? 0);

        if ($order->relationLoaded('payments')) {
            $totalPaid = (float) $order->payments->sum('amount');
            return max($totalPrice - $totalPaid, 0);
        }

        if (method_exists($order, 'getRemainingBalance')) {
            return (float) $order->getRemainingBalance();
        }

        $totalPaid = (float) $order->payments()->sum('amount');

        return max($totalPrice - $totalPaid, 0);
    }

    private function isOrderFullyPaid(Order $order): bool
    {
        $order->refresh();

        if (method_exists($order, 'isPaid')) {
            return (bool) $order->isPaid();
        }

        return (string) $order->payment_status === (string) $this->resolvePaidStatus($order);
    }

    private function resolvePaidStatus(Order $order): string
    {
        $class = get_class($order);

        if (defined($class . '::PAYMENT_PAID')) {
            return constant($class . '::PAYMENT_PAID');
        }

        return 'paid';
    }

    private function resolvePartialStatus(Order $order): string
    {
        $class = get_class($order);

        if (defined($class . '::PAYMENT_PARTIAL')) {
            return constant($class . '::PAYMENT_PARTIAL');
        }

        return 'partial';
    }

    private function resolveUnpaidStatus(Order $order): string
    {
        $class = get_class($order);

        if (defined($class . '::PAYMENT_UNPAID')) {
            return constant($class . '::PAYMENT_UNPAID');
        }

        if (defined($class . '::PAYMENT_PENDING')) {
            return constant($class . '::PAYMENT_PENDING');
        }

        return 'unpaid';
    }
}