<?php

namespace App\Http\Controllers;

use App\Models\Order;
use App\Models\OrderItem;
use App\Models\Setting;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;

class PrintController extends Controller
{
    /**
     * PRINT PIUTANG PER CUSTOMER
     * support:
     * - /print/piutang-customer?customer=XXXX
     * - /print/piutang-customer?customer=XXXX&month=YYYY-MM
     * - /print/piutang-customer?customer=XXXX&order_ids[]=1&order_ids[]=2
     */
    public function laporanPiutangCustomer(Request $request)
    {
        $customer = trim((string) $request->query('customer', ''));

        if ($customer === '') {
            abort(404, 'Customer tidak ditemukan.');
        }

        $selectedOrderIds = collect((array) $request->query('order_ids', []))
            ->map(fn ($id) => (int) $id)
            ->filter()
            ->unique()
            ->values()
            ->all();

        $query = Order::with($this->getOrderPrintWithRelations(['payments.cashier']))
            ->where('is_cancelled', false)
            ->whereIn('payment_status', [Order::PAYMENT_UNPAID, Order::PAYMENT_PARTIAL])
            ->where('customer_name', $customer)
            ->orderBy('created_at', 'asc');

        $monthParam = $request->query('month');
        if (!empty($monthParam)) {
            try {
                $month = Carbon::createFromFormat('Y-m', $monthParam);
                $query->whereBetween('created_at', [
                    $month->copy()->startOfMonth()->startOfDay(),
                    $month->copy()->endOfMonth()->endOfDay(),
                ]);
            } catch (\Throwable $e) {
                // ignore invalid month
            }
        }

        if (!empty($selectedOrderIds)) {
            $query->whereIn('id', $selectedOrderIds);
        }

        $orders = $query->get()->map(function ($order) {
            return $this->decorateOrderForPrint($order);
        });

        $summary = [
            'total_orders' => $orders->count(),
            'total_receivables' => $orders->sum(fn ($o) => (float) ($o->report_remaining_balance ?? 0)),
        ];

        return view('print.piutang-customer', [
            'title' => 'Piutang Customer',
            'customer' => $customer,
            'orders' => $orders,
            'summary' => $summary,
            'printedAt' => now(),
            'month' => $request->query('month'),
            'shopName' => Setting::getShopName(),
            'shopAddress' => Setting::getShopAddress(),
            'shopLogo' => Setting::getShopLogo(),
        ]);
    }

    public function spk(Order $order)
    {
        if (!$order->isPaid() && !$order->is_credit) {
            return back()->with('error', 'SPK hanya bisa dicetak untuk order yang sudah dibayar atau transaksi piutang.');
        }

        $order->load($this->getOrderPrintWithRelations(['designer']));
        $order = $this->decorateOrderForPrint($order);

        if (!$order->spk_printed) {
            $order->markSPKPrinted();
        }

        return view('print.spk', [
            'order' => $order,
            'shopName' => Setting::getShopName(),
            'shopLogo' => Setting::getShopLogo(),
        ]);
    }

    public function nota(Order $order)
    {
        if (!$order->isPaid()) {
            return back()->with('error', 'Nota hanya bisa dicetak untuk order yang sudah dibayar.');
        }

        $order->load($this->getOrderPrintWithRelations(['designer', 'cashier']));
        $order = $this->decorateOrderForPrint($order);

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

    public function faktur(Order $order)
    {
        if (!$order->isPaid()) {
            return back()->with('error', 'Faktur hanya bisa dicetak untuk order yang sudah dibayar.');
        }

        $order->load($this->getOrderPrintWithRelations(['designer', 'cashier']));
        $order = $this->decorateOrderForPrint($order);

        return view('print.faktur', [
            'order' => $order,
            'shopName' => Setting::getShopName(),
            'shopAddress' => Setting::getShopAddress(),
            'shopPhone' => Setting::getShopPhone(),
            'shopLogo' => Setting::getShopLogo(),
        ]);
    }

    /**
     * Cetak nota gabungan dari invoice yang dicentang di Riwayat Pembayaran
     * Format query:
     * /print/nota-gabungan-batch?order_ids=1,2,3
     */
    public function notaGabunganBatch(Request $request)
    {
        $orderIds = collect(explode(',', (string) $request->query('order_ids')))
            ->map(fn ($id) => (int) trim($id))
            ->filter(fn ($id) => $id > 0)
            ->unique()
            ->values();

        abort_if($orderIds->isEmpty(), 404, 'Order tidak dipilih.');

        $orders = Order::with($this->getOrderPrintWithRelations(['designer', 'cashier', 'payments.cashier']))
            ->whereIn('id', $orderIds->all())
            ->where('is_cancelled', false)
            ->get()
            ->sortBy(function ($order) use ($orderIds) {
                return $orderIds->search($order->id);
            })
            ->values()
            ->map(function ($order) {
                return $this->decorateOrderForPrint($order);
            });

        abort_if($orders->isEmpty(), 404, 'Data order tidak ditemukan.');

        $grandTotal = (float) $orders->sum(fn ($order) => (float) ($order->report_total_price ?? $order->total_price ?? 0));
        $grandPaid = (float) $orders->sum(fn ($order) => (float) $this->getPrintablePaidAmount($order));

        return view('pages.print.nota-gabungan-batch', [
            'title' => 'Nota Gabungan Pembayaran',
            'orders' => $orders,
            'grandTotal' => $grandTotal,
            'grandPaid' => $grandPaid,
            'shopName' => Setting::getShopName(),
            'shopAddress' => Setting::getShopAddress(),
            'shopPhone' => Setting::getShopPhone(),
            'shopFooter' => Setting::getShopFooter(),
            'shopLogo' => Setting::getShopLogo(),
            'printedAt' => now(),
        ]);
    }

    public function laporan(Request $request)
    {
        $startDate = $request->input('start_date', now()->startOfMonth()->toDateString());
        $endDate = $request->input('end_date', now()->toDateString());
        $machineId = $request->input('machine_id');
        $categoryId = $request->input('category_id');

        $ordersQuery = Order::with($this->getOrderPrintWithRelations(['designer', 'cashier']))
            ->where('is_cancelled', false)
            ->where(function ($query) use ($startDate, $endDate) {
                $query->where(function ($paidQuery) use ($startDate, $endDate) {
                    $paidQuery->where('payment_status', Order::PAYMENT_PAID)
                        ->whereNotNull('paid_at')
                        ->whereBetween('paid_at', [$startDate . ' 00:00:00', $endDate . ' 23:59:59']);
                })->orWhere(function ($receivableQuery) use ($startDate, $endDate) {
                    $receivableQuery->whereIn('payment_status', [
                        Order::PAYMENT_UNPAID,
                        Order::PAYMENT_PARTIAL,
                    ])->whereBetween('created_at', [$startDate . ' 00:00:00', $endDate . ' 23:59:59']);
                });
            });

        $this->applyMachineFilterIfAvailable($ordersQuery, $request);

        if ($request->filled('category_id')) {
            $ordersQuery->whereHas('items.material', function ($q) use ($categoryId) {
                $q->where('category_id', $categoryId);
            });
        }

        $orders = $ordersQuery->get()
            ->map(function ($order) use ($request) {
                return $this->decorateOrderForPrint($order, $request);
            })
            ->sortByDesc(function ($order) {
                if ($order->payment_status === Order::PAYMENT_PAID && !empty($order->paid_at)) {
                    return optional($order->paid_at)->timestamp ?? 0;
                }

                return optional($order->created_at)->timestamp ?? 0;
            })
            ->values();

        $totalQty = (float) $orders->sum('report_total_qty');

        $paidOrdersCollection = $orders->filter(function ($order) {
            if (method_exists($order, 'isPaid')) {
                return $order->isPaid();
            }

            return $order->payment_status === Order::PAYMENT_PAID;
        })->values();

        $receivableOrdersCollection = $orders->filter(function ($order) {
            if (method_exists($order, 'isPaid')) {
                return !$order->isPaid();
            }

            return in_array($order->payment_status, [
                Order::PAYMENT_UNPAID,
                Order::PAYMENT_PARTIAL,
            ], true);
        })->values();

        $paidRevenue = (float) $paidOrdersCollection->sum('report_total_price');
        $receivableRevenue = (float) $receivableOrdersCollection->sum('report_total_price');

        $paidCustomers = $this->buildCustomerSummary($paidOrdersCollection);
        $receivableCustomers = $this->buildCustomerSummary($receivableOrdersCollection);

        $dailyData = $orders
            ->groupBy(function ($order) {
                if ($order->payment_status === Order::PAYMENT_PAID && !empty($order->paid_at)) {
                    return optional($order->paid_at)->format('Y-m-d');
                }

                return optional($order->created_at)->format('Y-m-d');
            })
            ->map(function ($group, $date) {
                return (object) [
                    'date' => $date,
                    'total_orders' => $group->count(),
                    'total_revenue' => (float) $group->sum('report_total_price'),
                ];
            })
            ->sortBy('date')
            ->values();

        $summary = [
            'total_orders' => $orders->count(),
            'total_revenue' => $paidRevenue + $receivableRevenue,
            'total_qty' => $totalQty,

            'paid_orders' => $paidOrdersCollection->count(),
            'paid_revenue' => $paidRevenue,

            'receivable_orders' => $receivableOrdersCollection->count(),
            'receivable_revenue' => $receivableRevenue,

            'paid_customers' => $paidCustomers->count(),
            'receivable_customers' => $receivableCustomers->count(),
        ];

        return view('print.laporan', [
            'title' => 'Omset Penjualan',
            'orders' => $orders,
            'summary' => $summary,
            'dailyData' => $dailyData,
            'paidCustomers' => $paidCustomers,
            'receivableCustomers' => $receivableCustomers,
            'startDate' => $startDate,
            'endDate' => $endDate,
            'machineId' => $machineId,
            'categoryId' => $categoryId,
            'shopName' => Setting::getShopName(),
            'shopAddress' => Setting::getShopAddress(),
            'shopLogo' => Setting::getShopLogo(),
        ]);
    }

    public function laporanProduksi(Request $request)
    {
        $startDate = $request->input('start_date', now()->startOfMonth()->toDateString());
        $endDate = $request->input('end_date', now()->toDateString());

        $orders = Order::with($this->getOrderPrintWithRelations(['designer', 'operator']))
            ->completed()
            ->whereBetween('finished_at', [$startDate . ' 00:00:00', $endDate . ' 23:59:59'])
            ->latest('finished_at')
            ->get()
            ->map(function ($order) {
                return $this->decorateOrderForPrint($order);
            });

        $summary = [
            'total_completed' => $orders->count(),
            'total_qty' => (float) $orders->sum('report_total_qty'),
        ];

        return view('print.laporan-produksi', [
            'orders' => $orders,
            'summary' => $summary,
            'startDate' => $startDate,
            'endDate' => $endDate,
            'shopName' => Setting::getShopName(),
            'shopAddress' => Setting::getShopAddress(),
            'shopLogo' => Setting::getShopLogo(),
        ]);
    }

    /**
     * Print receivables by month
     */
    public function laporanPiutang(Request $request)
    {
        $month = $request->input('month', now()->format('Y-m'));
        $startDate = Carbon::createFromFormat('Y-m', $month)->startOfMonth();
        $endDate = Carbon::createFromFormat('Y-m', $month)->endOfMonth();

        $orders = Order::with($this->getOrderPrintWithRelations(['designer', 'customer']))
            ->hasReceivables()
            ->whereBetween('created_at', [$startDate->copy()->startOfDay(), $endDate->copy()->endOfDay()])
            ->latest()
            ->get()
            ->map(function ($order) {
                return $this->decorateOrderForPrint($order);
            });

        $totalReceivables = $orders->sum(fn ($order) => (float) ($order->report_remaining_balance ?? 0));

        $summary = [
            'total_orders' => $orders->count(),
            'total_receivables' => $totalReceivables,
        ];

        return view('print.piutang', [
            'orders' => $orders,
            'summary' => $summary,
            'month' => $month,
            'shopName' => Setting::getShopName(),
            'shopAddress' => Setting::getShopAddress(),
            'shopLogo' => Setting::getShopLogo(),
        ]);
    }

    /**
     * Print all receivables
     */
    public function laporanPiutangAll()
    {
        $orders = Order::with($this->getOrderPrintWithRelations(['designer', 'customer']))
            ->hasReceivables()
            ->latest()
            ->get()
            ->map(function ($order) {
                return $this->decorateOrderForPrint($order);
            });

        $totalReceivables = $orders->sum(fn ($order) => (float) ($order->report_remaining_balance ?? 0));

        $summary = [
            'total_orders' => $orders->count(),
            'total_receivables' => $totalReceivables,
        ];

        return view('print.piutang', [
            'orders' => $orders,
            'summary' => $summary,
            'shopName' => Setting::getShopName(),
            'shopAddress' => Setting::getShopAddress(),
            'shopLogo' => Setting::getShopLogo(),
        ]);
    }

    /**
     * Print purchase debt by month
     */
    public function laporanHutang(Request $request)
    {
        $month = $request->input('month', now()->format('Y-m'));
        $startDate = Carbon::createFromFormat('Y-m', $month)->startOfMonth();
        $endDate = Carbon::createFromFormat('Y-m', $month)->endOfMonth();

        $purchases = \App\Models\Purchase::with(['material', 'supplierRelation', 'payments'])
            ->whereIn('payment_status', ['unpaid', 'partial'])
            ->whereBetween('purchase_date', [$startDate->copy()->startOfDay(), $endDate->copy()->endOfDay()])
            ->latest('purchase_date')
            ->get();

        $totalAmount = $purchases->sum('total_price');
        $totalPaid = $purchases->sum('paid_amount');
        $totalDebt = $totalAmount - $totalPaid;

        $summary = [
            'total_purchases' => $purchases->count(),
            'total_amount' => $totalAmount,
            'total_paid' => $totalPaid,
            'total_debt' => $totalDebt,
        ];

        return view('print.hutang', [
            'purchases' => $purchases,
            'summary' => $summary,
            'month' => $month,
            'shopName' => Setting::getShopName(),
            'shopAddress' => Setting::getShopAddress(),
            'shopLogo' => Setting::getShopLogo(),
        ]);
    }

    /**
     * Print all purchase debt
     */
    public function laporanHutangAll()
    {
        $purchases = \App\Models\Purchase::with(['material', 'supplierRelation', 'payments'])
            ->whereIn('payment_status', ['unpaid', 'partial'])
            ->latest('purchase_date')
            ->get();

        $totalAmount = $purchases->sum('total_price');
        $totalPaid = $purchases->sum('paid_amount');
        $totalDebt = $totalAmount - $totalPaid;

        $summary = [
            'total_purchases' => $purchases->count(),
            'total_amount' => $totalAmount,
            'total_paid' => $totalPaid,
            'total_debt' => $totalDebt,
        ];

        return view('print.hutang', [
            'purchases' => $purchases,
            'summary' => $summary,
            'shopName' => Setting::getShopName(),
            'shopAddress' => Setting::getShopAddress(),
            'shopLogo' => Setting::getShopLogo(),
        ]);
    }

    /**
     * Print revenue report (Rincian Pendapatan)
     */
    public function laporanPendapatan(Request $request)
{
    $startDate = $request->input('start_date', now()->startOfMonth()->toDateString());
    $endDate = $request->input('end_date', now()->toDateString());
    $tab = $request->input('tab', 'transaksi');

    $start = Carbon::parse($startDate)->startOfDay();
    $end = Carbon::parse($endDate)->endOfDay();

    $ordersQuery = Order::with(
        $this->hasMaterialMachinesRelation()
            ? ['items.product', 'items.material.machines', 'designer', 'customer', 'cashier', 'payments.cashier', 'payments.paymentAccount']
            : ['items.product', 'items.material', 'designer', 'customer', 'cashier', 'payments.cashier', 'payments.paymentAccount']
    )
        ->where('is_cancelled', false)
        ->whereHas('payments', function ($q) use ($start, $end) {
            $q->whereBetween('created_at', [$start, $end]);
        });

    $this->applyMachineFilterIfAvailable($ordersQuery, $request);

    if ($request->filled('category_id')) {
        $ordersQuery->whereHas('items.material', function ($q) use ($request) {
            $q->where('category_id', $request->category_id);
        });
    }

    $orders = $ordersQuery->get()
        ->map(function ($order) use ($request) {
            return $this->decorateOrderForPrint($order, $request);
        })
        ->values();

    $byTransaction = $orders
        ->flatMap(function ($order) use ($start, $end) {
            $payments = collect($order->payments ?? [])
                ->filter(function ($payment) use ($start, $end) {
                    $paidAt = $payment->created_at ? Carbon::parse($payment->created_at) : null;
                    return $paidAt && $paidAt->between($start, $end);
                })
                ->sortBy('created_at')
                ->values();

            $runningPaid = 0;
            $orderTotal = (float) ($order->report_total_price ?? $order->total_price ?? 0);

            return $payments->map(function ($payment, $index) use ($order, $payments, &$runningPaid, $orderTotal) {
                $amount = (float) ($payment->amount ?? 0);
                $runningPaid += $amount;

                $statusLabel = 'Lunas';
                $statusBadge = 'success';

                if ($payments->count() > 1) {
                    if ($runningPaid < $orderTotal) {
                        $statusLabel = 'Cicilan';
                        $statusBadge = 'warning';
                    } elseif ($runningPaid >= $orderTotal && $index > 0) {
                        $statusLabel = 'Pelunasan';
                        $statusBadge = 'brand';
                    }
                } elseif ($amount < $orderTotal) {
                    $statusLabel = 'Cicilan';
                    $statusBadge = 'warning';
                }

                return (object) [
                    'order_id' => $order->id,
                    'payment_id' => $payment->id,
                    'payment_date' => $payment->created_at,
                    'invoice_number' => $order->invoice_number,
                    'customer_name' => $order->customer_name,
                    'payment_method' => $payment->payment_method ?? null,
                    'payment_method_label' => method_exists($payment, 'getPaymentMethodLabel')
                        ? $payment->getPaymentMethodLabel()
                        : ucfirst((string) ($payment->payment_method ?? '-')),
                    'payment_account_label' => $payment->paymentAccount
                        ? (method_exists($payment->paymentAccount, 'getShortLabel')
                            ? $payment->paymentAccount->getShortLabel()
                            : ($payment->paymentAccount->name ?? '-'))
                        : '-',
                    'cashier_name' => $payment->cashier->name ?? ($order->cashier->name ?? '-'),
                    'items_label' => collect($order->items)->pluck('product_name')->filter()->unique()->implode(', '),
                    'qty' => (float) (collect($order->items)->sum('qty') ?? 0),
                    'amount' => $amount,
                    'status_label' => $statusLabel,
                    'status_badge' => $statusBadge,
                    'notes' => $payment->notes ?? null,
                ];
            });
        })
        ->sortByDesc(function ($row) {
            return $row->payment_date ? Carbon::parse($row->payment_date)->timestamp : 0;
        })
        ->values();

    $byProduct = $orders
        ->flatMap(function ($order) use ($start, $end) {
            $items = collect($order->items ?? []);
            $orderTotal = (float) ($order->report_total_price ?? $order->total_price ?? 0);

            if ($orderTotal <= 0 || $items->isEmpty()) {
                return collect();
            }

            $paymentsInPeriod = collect($order->payments ?? [])
                ->filter(function ($payment) use ($start, $end) {
                    $paidAt = $payment->created_at ? Carbon::parse($payment->created_at) : null;
                    return $paidAt && $paidAt->between($start, $end);
                });

            $paidInPeriod = (float) $paymentsInPeriod->sum('amount');

            if ($paidInPeriod <= 0) {
                return collect();
            }

            $ratio = min(1, $paidInPeriod / $orderTotal);

            return $items->map(function ($item) use ($order, $ratio) {
                $itemSubtotal = $this->calculatePrintableItemSubtotal($item);

                return [
                    'order_id' => $order->id,
                    'product_id' => $item->product_id,
                    'name' => $item->product_name ?: (optional($item->product)->name ?? '-'),
                    'qty' => (float) ($item->qty ?? 0),
                    'total_revenue' => round($itemSubtotal * $ratio, 2),
                ];
            });
        })
        ->groupBy(function ($row) {
            return ($row['product_id'] ?? 'null') . '|' . ($row['name'] ?? '-');
        })
        ->map(function ($rows) {
            $first = $rows->first();

            return (object) [
                'id' => $first['product_id'],
                'name' => $first['name'],
                'total_qty' => (float) $rows->sum('qty'),
                'total_revenue' => (float) $rows->sum('total_revenue'),
                'total_orders' => $rows->pluck('order_id')->unique()->count(),
            ];
        })
        ->sortByDesc('total_revenue')
        ->values();

    $summary = [
        'total_revenue' => (float) $byTransaction->sum('amount'),
        'total_qty' => (float) $byTransaction->sum('qty'),
        'total_products' => $byProduct->count(),
        'total_transactions' => $byTransaction->count(),
    ];

    return view('print.laporan-pendapatan', [
        'byProduct' => $byProduct,
        'byTransaction' => $byTransaction,
        'summary' => $summary,
        'startDate' => $startDate,
        'endDate' => $endDate,
        'tab' => $tab,
        'shopName' => Setting::getShopName(),
        'shopAddress' => Setting::getShopAddress(),
        'shopLogo' => Setting::getShopLogo(),
    ]);
}

    /**
     * Print click report (Laporan Klik)
     */
    public function laporanKlik(Request $request)
    {
        $startDate = $request->input('start_date', now()->startOfMonth()->toDateString());
        $endDate = $request->input('end_date', now()->toDateString());
        $machineId = $request->input('machine_id');
        $categoryId = $request->input('category_id');

        $trackedProducts = \App\Models\Product::where('track_clicks', true)->pluck('id')->toArray();
        $trackedMaterials = \App\Models\Material::where('track_clicks', true)->pluck('id')->toArray();

        $itemsQuery = OrderItem::with(
            $this->hasMaterialMachinesRelation()
                ? ['order.payments', 'material.machines']
                : ['order.payments', 'material']
        )
            ->whereHas('order', function ($q) use ($startDate, $endDate) {
                $q->whereBetween('created_at', [$startDate . ' 00:00:00', $endDate . ' 23:59:59'])
                    ->where('is_cancelled', false);
            })
            ->where(function ($query) use ($trackedProducts, $trackedMaterials) {
                $query->whereIn('product_id', $trackedProducts)
                    ->orWhereIn('material_id', $trackedMaterials);
            });

        if ($categoryId) {
            $itemsQuery->whereHas('material', function ($q) use ($categoryId) {
                $q->where('category_id', $categoryId);
            });
        }

        if ($machineId && $this->hasMaterialMachinesRelation()) {
            $itemsQuery->whereHas('material.machines', function ($q) use ($machineId) {
                $q->where('machines.id', $machineId);
            });
        }

        $items = $itemsQuery->get();

        $reportData = $items
            ->filter(function ($item) {
                return !$this->isItemCancelled($item);
            })
            ->groupBy(function ($item) {
                return ($item->product_id ?? 'null') . '|' . ($item->product_name ?? '-') . '|' . ($item->material_id ?? 'null') . '|' . ($item->material_name ?? '-');
            })
            ->map(function ($group) {
                $first = $group->first();

                return (object) [
                    'product_id' => $first->product_id,
                    'product_name' => $first->product_name,
                    'material_id' => $first->material_id,
                    'material_name' => $first->material_name,
                    'total_orders' => $group->pluck('order_id')->unique()->count(),
                    'total_qty' => (float) $group->sum(fn ($item) => (float) ($item->qty ?? 0)),
                    'total_clicks' => (float) $group->sum(fn ($item) => ((float) ($item->qty ?? 0)) * ((float) ($item->sisi ?? 1))),
                    'total_size' => (float) $group->sum(fn ($item) => (float) $this->resolvePrintableItemSizeValue($item)),
                    'total_sales' => (float) $group->sum(fn ($item) => $this->calculatePrintableItemSubtotal($item)),
                ];
            })
            ->sortByDesc('total_sales')
            ->values();

        $materialMachines = [];
        if ($this->hasMaterialMachinesRelation()) {
            $reportMaterialIds = $reportData->pluck('material_id')->unique()->filter()->toArray();

            if (!empty($reportMaterialIds)) {
                $materialsWithMachines = \App\Models\Material::whereIn('id', $reportMaterialIds)
                    ->with('machines')
                    ->get();

                foreach ($materialsWithMachines as $mat) {
                    $materialMachines[$mat->id] = $mat->machines->pluck('name')->implode(', ');
                }
            }
        }

        $machineStats = [];
        $totalClickCost = 0;

        if ($this->hasMaterialMachinesRelation()) {
            $machines = \App\Models\Machine::active()->orderBy('name')->get();
            $reportMaterialIds = $reportData->pluck('material_id')->unique()->filter()->toArray();

            foreach ($machines as $machine) {
                $linkedMaterialIds = $machine->materials()
                    ->whereIn('materials.id', $reportMaterialIds)
                    ->pluck('materials.id')
                    ->toArray();

                if (empty($linkedMaterialIds)) {
                    $machineStats[] = [
                        'id' => $machine->id,
                        'name' => $machine->name,
                        'cost_per_click' => $machine->cost_per_click,
                        'total_clicks' => 0,
                        'total_cost' => 0,
                    ];
                    continue;
                }

                $clicks = $reportData->whereIn('material_id', $linkedMaterialIds)->sum('total_clicks');
                $cost = $clicks * $machine->cost_per_click;
                $totalClickCost += $cost;

                $machineStats[] = [
                    'id' => $machine->id,
                    'name' => $machine->name,
                    'cost_per_click' => $machine->cost_per_click,
                    'total_clicks' => $clicks,
                    'total_cost' => $cost,
                ];
            }
        }

        $summary = [
            'total_orders' => $reportData->sum('total_orders'),
            'total_qty' => $reportData->sum('total_qty'),
            'total_clicks' => $reportData->sum('total_clicks'),
            'total_cost' => $totalClickCost,
            'total_size' => $reportData->sum('total_size'),
            'total_sales' => $reportData->sum('total_sales'),
        ];

        return view('print.laporan-klik', [
            'reportData' => $reportData,
            'summary' => $summary,
            'machineStats' => $machineStats,
            'materialMachines' => $materialMachines,
            'startDate' => $startDate,
            'endDate' => $endDate,
            'shopName' => Setting::getShopName(),
            'shopAddress' => Setting::getShopAddress(),
            'shopLogo' => Setting::getShopLogo(),
        ]);
    }

    public function laporanTransaksi(Request $request)
    {
        $startDate = $request->input('start_date', now()->startOfMonth()->toDateString());
        $endDate = $request->input('end_date', now()->toDateString());

        $ordersQuery = Order::with($this->getOrderPrintWithRelations(['designer', 'cashier', 'operator']))
            ->whereBetween('created_at', [$startDate . ' 00:00:00', $endDate . ' 23:59:59']);

        $this->applyMachineFilterIfAvailable($ordersQuery, $request);

        if ($request->filled('category_id')) {
            $ordersQuery->whereHas('items.material', function ($q) use ($request) {
                $q->where('category_id', $request->category_id);
            });
        }

        $orders = $ordersQuery->latest()
            ->get()
            ->map(function ($order) use ($request) {
                return $this->decorateOrderForPrint($order, $request);
            });

        $summary = [
            'total_orders' => $orders->count(),
            'paid_orders' => $orders->where('payment_status', 'paid')->count(),
            'completed_orders' => $orders->where('production_status', 'selesai')->count(),
        ];

        return view('print.laporan-transaksi', [
            'orders' => $orders,
            'summary' => $summary,
            'startDate' => $startDate,
            'endDate' => $endDate,
            'shopName' => Setting::getShopName(),
            'shopAddress' => Setting::getShopAddress(),
            'shopLogo' => Setting::getShopLogo(),
        ]);
    }

    public function laporanPendapatanBersih(Request $request)
    {
        return back()->with('error', 'Fitur ini belum diubah di versi replace cepat.');
    }

    private function hasMaterialMachinesRelation(): bool
    {
        return method_exists(\App\Models\Material::class, 'machines');
    }

    private function getOrderPrintWithRelations(array $extra = []): array
    {
        $relations = array_merge(['items', 'payments'], $extra);

        return array_values(array_unique($relations));
    }

    private function applyMachineFilterIfAvailable($query, Request $request): void
    {
        if (!$request->filled('machine_id')) {
            return;
        }

        if (!$this->hasMaterialMachinesRelation()) {
            return;
        }

        $query->whereHas('items.material.machines', function ($q) use ($request) {
            $q->where('machines.id', $request->machine_id);
        });
    }

    private function normalizePrintableSizeUnit(?string $unit): string
    {
        $unit = strtolower(trim((string) $unit));

        return match ($unit) {
            'm²', 'm2' => 'm2',
            'm', 'meter' => 'meter',
            'pcs', 'pc', 'piece', 'lembar' => 'lembar',
            default => $unit !== '' ? $unit : 'lembar',
        };
    }

    private function parsePrintableM2FromDescription(?string $description): float
    {
        $description = strtolower(trim((string) $description));

        if ($description === '') {
            return 1;
        }

        $description = str_replace(['×', '*'], 'x', $description);

        if (!preg_match('/^\s*(\d+(?:\.\d+)?)\s*x\s*(\d+(?:\.\d+)?)\s*$/', $description, $matches)) {
            return 1;
        }

        $a = (float) $matches[1];
        $b = (float) $matches[2];

        if ($a >= 10 || $b >= 10) {
            $a = $a / 100;
            $b = $b / 100;
        }

        return round($a * $b, 4);
    }

    private function isItemCancelled($item): bool
    {
        if (method_exists($item, 'isCancelled')) {
            return $item->isCancelled();
        }

        return (bool) ($item->is_cancelled ?? false);
    }

    private function itemMatchesPrintFilter($item, ?Request $request = null): bool
    {
        if (!$request) {
            return true;
        }

        $material = $item->relationLoaded('material')
            ? $item->material
            : ($item->material_id ? $item->material()->first() : null);

        if ($request->filled('category_id')) {
            if (!$material || (int) $material->category_id !== (int) $request->category_id) {
                return false;
            }
        }

        if ($request->filled('machine_id')) {
            if (!$this->hasMaterialMachinesRelation()) {
                return true;
            }

            if (!$material) {
                return false;
            }

            $hasMachine = $material->relationLoaded('machines')
                ? $material->machines->contains(fn ($machine) => (int) $machine->id === (int) $request->machine_id)
                : $material->machines()->where('machines.id', $request->machine_id)->exists();

            if (!$hasMachine) {
                return false;
            }
        }

        return true;
    }

    private function getPrintableItemsForOrder(Order $order, ?Request $request = null): Collection
    {
        $items = $order->relationLoaded('items')
            ? $order->items
            : $order->items()->get();

        return $items
            ->filter(function ($item) use ($request) {
                return !$this->isItemCancelled($item) && $this->itemMatchesPrintFilter($item, $request);
            })
            ->values();
    }

    private function resolvePrintableItemSizeValue($item): float
    {
        $unit = $this->normalizePrintableSizeUnit($item->size_unit ?? 'lembar');
        $sizeValue = (float) ($item->size_value ?? 0);

        if ($unit === 'm2') {
            if ($sizeValue <= 0) {
                $sizeValue = $this->parsePrintableM2FromDescription($item->size_description ?? '');
            }
        } else {
            if ($sizeValue <= 0) {
                $sizeValue = 1;
            }
        }

        return $sizeValue > 0 ? $sizeValue : 1;
    }

    private function getPrintableMinimumOrderAmount(): float
    {
        return 10000;
    }

    private function getPrintableRoundingStep(): float
    {
        return 1000;
    }

    private function applyPrintableMinimumAndRounding(float $amount): float
    {
        $amount = max($amount, 0);

        if ($amount <= 0) {
            return 0;
        }

        $minimum = $this->getPrintableMinimumOrderAmount();
        $roundingStep = $this->getPrintableRoundingStep();

        if ($minimum > 0 && $amount < $minimum) {
            $amount = $minimum;
        }

        if ($roundingStep > 0) {
            $amount = ceil($amount / $roundingStep) * $roundingStep;
        }

        return round($amount, 2);
    }

    private function calculatePrintableItemSubtotal($item): float
    {
        $unit = $this->normalizePrintableSizeUnit($item->size_unit ?? 'lembar');
        $qty = max((float) ($item->qty ?? 0), 0);
        $basePrice = max((float) ($item->base_price ?? 0), 0);
        $manualPrice = max((float) ($item->manual_price ?? 0), 0);
        $storedSubtotal = max((float) ($item->subtotal ?? 0), 0);
        $sizeValue = $this->resolvePrintableItemSizeValue($item);

        if ($manualPrice > 0) {
            return $this->applyPrintableMinimumAndRounding($manualPrice);
        }

        if ($unit === 'm2') {
            return $this->applyPrintableMinimumAndRounding($basePrice * $sizeValue * $qty);
        }

        if ($unit === 'meter') {
            $amount = $storedSubtotal > 0
                ? $storedSubtotal
                : ($basePrice * $qty);

            return $this->applyPrintableMinimumAndRounding($amount);
        }

        $amount = $storedSubtotal > 0
            ? $storedSubtotal
            : ($basePrice * $qty);

        return $this->applyPrintableMinimumAndRounding($amount);
    }

    private function calculatePrintableOrderTotal(Order $order, ?Request $request = null): float
    {
        $items = $this->getPrintableItemsForOrder($order, $request);

        if ($items->isEmpty()) {
            return $this->applyPrintableMinimumAndRounding((float) ($order->total_price ?? 0));
        }

        return round($items->sum(function ($item) {
            return $this->calculatePrintableItemSubtotal($item);
        }), 2);
    }

    private function calculatePrintableOrderQty(Order $order, ?Request $request = null): float
    {
        $items = $this->getPrintableItemsForOrder($order, $request);

        return (float) $items->sum(function ($item) {
            return (float) ($item->qty ?? 0);
        });
    }

    private function getPrintablePaidAmount(Order $order): float
    {
        if ($order->relationLoaded('payments')) {
            return (float) $order->payments->sum('amount');
        }

        return (float) ($order->paid_amount ?? 0);
    }

    private function calculatePrintableRemainingBalance(Order $order, ?Request $request = null): float
    {
        $remaining = $this->calculatePrintableOrderTotal($order, $request) - $this->getPrintablePaidAmount($order);

        return $remaining > 0 ? round($remaining, 2) : 0;
    }

    private function decorateOrderForPrint(Order $order, ?Request $request = null): Order
    {
        $items = $this->getPrintableItemsForOrder($order, $request);

        $items->each(function ($item) {
            $normalizedUnit = $this->normalizePrintableSizeUnit($item->size_unit ?? 'lembar');
            $resolvedSizeValue = $this->resolvePrintableItemSizeValue($item);
            $resolvedSubtotal = $this->calculatePrintableItemSubtotal($item);

            $item->setAttribute('size_unit', $normalizedUnit);
            $item->setAttribute('size_value', $resolvedSizeValue);
            $item->setAttribute('subtotal', $resolvedSubtotal);
        });

        $order->setRelation('items', $items);

        $reportTotal = round((float) $items->sum('subtotal'), 2);
        $reportQty = (float) $items->sum('qty');
        $reportRemainingBalance = $this->calculatePrintableRemainingBalance($order, $request);

        $order->setAttribute('total_price', $reportTotal);
        $order->setAttribute('report_total_price', $reportTotal);
        $order->setAttribute('report_total_qty', $reportQty);
        $order->setAttribute('remaining_balance', $reportRemainingBalance);
        $order->setAttribute('report_remaining_balance', $reportRemainingBalance);

        return $order;
    }

    private function buildCustomerSummary(Collection $orders): Collection
    {
        return $orders
            ->filter(fn ($order) => !empty($order->customer_name))
            ->groupBy('customer_name')
            ->map(function ($group, $customerName) {
                return [
                    'customer_name' => $customerName,
                    'total_orders' => $group->count(),
                    'total_amount' => (float) $group->sum('report_total_price'),
                ];
            })
            ->sortByDesc('total_amount')
            ->values();
    }
}