<?php

namespace App\Http\Controllers\Admin;

use App\CentralLogics\Helpers;
use App\Http\Controllers\Controller;
use App\Models\ManualPaymentVerification;
use App\Services\Accounting\AccountingVoucherService;
use App\Services\Payments\BanglaQrPaymentService;
use Carbon\Carbon;
use Illuminate\Database\Query\Builder;
use Illuminate\Http\Request;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;

class ShowroomReportController extends Controller
{
    private int $perPage = 25;

    public function __construct(private readonly AccountingVoucherService $accountingVoucherService)
    {
    }

    public function posSales(Request $request)
    {
        if (! $this->hasTableAndColumns('orders', ['sale_channel'])) {
            return $this->missingTable('POS Sales List', 'admin.showroom.pos-sales');
        }

        $query = DB::table('orders')
            ->leftJoin('users as customers', 'customers.id', '=', 'orders.user_id')
            ->leftJoin('admins as creators', 'creators.id', '=', 'orders.pos_created_by_admin_id')
            ->where('orders.sale_channel', 'showroom');

        $this->dateFilter($query, $request, 'orders.created_at');
        $this->searchFilter($query, $request, ['orders.id', 'orders.payment_status', 'orders.order_status', 'customers.phone', 'customers.f_name', 'customers.l_name']);

        $summary = clone $query;
        $records = $query
            ->select([
                'orders.id',
                'orders.created_at',
                'orders.order_amount',
                'orders.payment_method',
                'orders.payment_status',
                'orders.order_status',
                'orders.accounting_voucher_id',
                'customers.phone as customer_phone',
                DB::raw("TRIM(CONCAT(COALESCE(customers.f_name, ''), ' ', COALESCE(customers.l_name, ''))) as customer_name"),
                DB::raw("TRIM(CONCAT(COALESCE(creators.f_name, ''), ' ', COALESCE(creators.l_name, ''))) as created_by_name"),
            ])
            ->orderByDesc('orders.id');

        return $this->listView('POS Sales List', 'admin.showroom.pos-sales', [
            ['key' => 'invoice', 'label' => 'Invoice'],
            ['key' => 'date', 'label' => 'Date'],
            ['key' => 'customer', 'label' => 'Customer'],
            ['key' => 'salesman', 'label' => 'Salesman'],
            ['key' => 'payment_method', 'label' => 'Payment Method'],
            ['key' => 'payment_status', 'label' => 'Payment Status'],
            ['key' => 'order_status', 'label' => 'Order Status'],
            ['key' => 'voucher', 'label' => 'Voucher'],
            ['key' => 'amount', 'label' => 'Amount', 'class' => 'text-right'],
        ], $this->paginate($records, fn ($row) => [
            'invoice' => '#' . $row->id,
            'date' => $this->dateTime($row->created_at),
            'customer' => $this->person($row->customer_name, $row->customer_phone),
            'salesman' => $row->created_by_name ?: '-',
            'payment_method' => $this->label($row->payment_method),
            'payment_status' => $this->label($row->payment_status),
            'order_status' => $this->label($row->order_status),
            'voucher' => $row->accounting_voucher_id ? '#' . $row->accounting_voucher_id : '-',
            'amount' => $this->money($row->order_amount),
        ]), [
            ['label' => 'POS Invoices', 'value' => (clone $summary)->count()],
            ['label' => 'Total Sales', 'value' => $this->money((clone $summary)->sum('orders.order_amount'))],
            ['label' => 'Paid Sales', 'value' => $this->money((clone $summary)->where('orders.payment_status', 'paid')->sum('orders.order_amount'))],
        ]);
    }

    public function returns(Request $request)
    {
        if (! $this->hasTableAndColumns('refunds', ['refund_amount']) || ! $this->hasTableAndColumns('orders', ['sale_channel'])) {
            return $this->missingTable('Showroom Return List', 'admin.showroom.returns');
        }

        $query = DB::table('refunds')
            ->leftJoin('orders', 'orders.id', '=', 'refunds.order_id')
            ->leftJoin('users as customers', 'customers.id', '=', 'refunds.user_id')
            ->where('orders.sale_channel', 'showroom');

        $this->dateFilter($query, $request, 'refunds.created_at');
        $this->searchFilter($query, $request, ['refunds.id', 'refunds.order_id', 'refunds.refund_status', 'refunds.refund_method', 'customers.phone', 'customers.f_name', 'customers.l_name']);

        $summary = clone $query;
        $records = $query
            ->select([
                'refunds.id',
                'refunds.order_id',
                'refunds.created_at',
                'refunds.refund_amount',
                'refunds.refund_status',
                'refunds.refund_method',
                'refunds.customer_reason',
                'customers.phone as customer_phone',
                DB::raw("TRIM(CONCAT(COALESCE(customers.f_name, ''), ' ', COALESCE(customers.l_name, ''))) as customer_name"),
            ])
            ->orderByDesc('refunds.id');

        return $this->listView('Showroom Return List', 'admin.showroom.returns', [
            ['key' => 'return_no', 'label' => 'Return'],
            ['key' => 'invoice', 'label' => 'Invoice'],
            ['key' => 'date', 'label' => 'Date'],
            ['key' => 'customer', 'label' => 'Customer'],
            ['key' => 'method', 'label' => 'Refund Method'],
            ['key' => 'status', 'label' => 'Status'],
            ['key' => 'reason', 'label' => 'Reason'],
            ['key' => 'amount', 'label' => 'Amount', 'class' => 'text-right'],
        ], $this->paginate($records, fn ($row) => [
            'return_no' => '#' . $row->id,
            'invoice' => '#' . $row->order_id,
            'date' => $this->dateTime($row->created_at),
            'customer' => $this->person($row->customer_name, $row->customer_phone),
            'method' => $this->label($row->refund_method),
            'status' => $this->label($row->refund_status),
            'reason' => $row->customer_reason ?: '-',
            'amount' => $this->money($row->refund_amount),
        ]), [
            ['label' => 'Return Count', 'value' => (clone $summary)->count()],
            ['label' => 'Return Amount', 'value' => $this->money((clone $summary)->sum('refunds.refund_amount'))],
        ]);
    }

    public function exchanges(Request $request)
    {
        if (! $this->hasTableAndColumns('order_exchanges', ['difference_amount']) || ! $this->hasTableAndColumns('orders', ['sale_channel'])) {
            return $this->missingTable('Exchange List', 'admin.showroom.exchanges');
        }

        $query = DB::table('order_exchanges')
            ->leftJoin('orders', 'orders.id', '=', 'order_exchanges.order_id')
            ->leftJoin('users as customers', 'customers.id', '=', 'order_exchanges.user_id')
            ->leftJoin('admins as creators', 'creators.id', '=', 'order_exchanges.created_by_admin_id')
            ->where('orders.sale_channel', 'showroom');

        $this->dateFilter($query, $request, 'order_exchanges.exchanged_at');
        $this->searchFilter($query, $request, ['order_exchanges.exchange_no', 'order_exchanges.order_id', 'order_exchanges.exchange_status', 'customers.phone', 'customers.f_name', 'customers.l_name']);

        $summary = clone $query;
        $records = $query
            ->select([
                'order_exchanges.exchange_no',
                'order_exchanges.order_id',
                'order_exchanges.exchanged_at',
                'order_exchanges.returned_amount',
                'order_exchanges.replacement_amount',
                'order_exchanges.difference_amount',
                'order_exchanges.exchange_status',
                'customers.phone as customer_phone',
                DB::raw("TRIM(CONCAT(COALESCE(customers.f_name, ''), ' ', COALESCE(customers.l_name, ''))) as customer_name"),
                DB::raw("TRIM(CONCAT(COALESCE(creators.f_name, ''), ' ', COALESCE(creators.l_name, ''))) as created_by_name"),
            ])
            ->orderByDesc('order_exchanges.id');

        return $this->listView('Exchange List', 'admin.showroom.exchanges', [
            ['key' => 'exchange_no', 'label' => 'Exchange'],
            ['key' => 'invoice', 'label' => 'Invoice'],
            ['key' => 'date', 'label' => 'Date'],
            ['key' => 'customer', 'label' => 'Customer'],
            ['key' => 'created_by', 'label' => 'Created By'],
            ['key' => 'status', 'label' => 'Status'],
            ['key' => 'returned', 'label' => 'Returned', 'class' => 'text-right'],
            ['key' => 'replacement', 'label' => 'Replacement', 'class' => 'text-right'],
            ['key' => 'difference', 'label' => 'Difference', 'class' => 'text-right'],
        ], $this->paginate($records, fn ($row) => [
            'exchange_no' => $row->exchange_no ?: '-',
            'invoice' => '#' . $row->order_id,
            'date' => $this->dateTime($row->exchanged_at),
            'customer' => $this->person($row->customer_name, $row->customer_phone),
            'created_by' => $row->created_by_name ?: '-',
            'status' => $this->label($row->exchange_status),
            'returned' => $this->money($row->returned_amount),
            'replacement' => $this->money($row->replacement_amount),
            'difference' => $this->money($row->difference_amount),
        ]), [
            ['label' => 'Exchange Count', 'value' => (clone $summary)->count()],
            ['label' => 'Returned Amount', 'value' => $this->money((clone $summary)->sum('order_exchanges.returned_amount'))],
            ['label' => 'Replacement Amount', 'value' => $this->money((clone $summary)->sum('order_exchanges.replacement_amount'))],
        ]);
    }

    public function cashClosings(Request $request)
    {
        if (! Schema::hasTable('showroom_cash_closings')) {
            return $this->missingTable('Daily Cash Closing', 'admin.showroom.cash-closings');
        }

        $query = DB::table('showroom_cash_closings')
            ->leftJoin('admins as closers', 'closers.id', '=', 'showroom_cash_closings.closed_by_admin_id');

        $this->dateFilter($query, $request, 'showroom_cash_closings.business_date');
        $this->searchFilter($query, $request, ['showroom_cash_closings.closing_no', 'showroom_cash_closings.showroom_id', 'showroom_cash_closings.status', 'closers.f_name', 'closers.l_name']);

        $summary = clone $query;
        $records = $query
            ->select([
                'showroom_cash_closings.closing_no',
                'showroom_cash_closings.business_date',
                'showroom_cash_closings.showroom_id',
                'showroom_cash_closings.cash_sales',
                'showroom_cash_closings.cash_refunds',
                'showroom_cash_closings.expected_cash',
                'showroom_cash_closings.counted_cash',
                'showroom_cash_closings.difference_amount',
                'showroom_cash_closings.status',
                DB::raw("TRIM(CONCAT(COALESCE(closers.f_name, ''), ' ', COALESCE(closers.l_name, ''))) as closed_by_name"),
            ])
            ->orderByDesc('showroom_cash_closings.business_date')
            ->orderByDesc('showroom_cash_closings.id');

        return $this->listView('Daily Cash Closing', 'admin.showroom.cash-closings', [
            ['key' => 'closing_no', 'label' => 'Closing No'],
            ['key' => 'date', 'label' => 'Business Date'],
            ['key' => 'showroom', 'label' => 'Showroom'],
            ['key' => 'closed_by', 'label' => 'Closed By'],
            ['key' => 'status', 'label' => 'Status'],
            ['key' => 'cash_sales', 'label' => 'Cash Sales', 'class' => 'text-right'],
            ['key' => 'cash_refunds', 'label' => 'Cash Refunds', 'class' => 'text-right'],
            ['key' => 'expected', 'label' => 'Expected', 'class' => 'text-right'],
            ['key' => 'counted', 'label' => 'Counted', 'class' => 'text-right'],
            ['key' => 'difference', 'label' => 'Difference', 'class' => 'text-right'],
        ], $this->paginate($records, fn ($row) => [
            'closing_no' => $row->closing_no,
            'date' => $this->date($row->business_date),
            'showroom' => $row->showroom_id ?: '-',
            'closed_by' => $row->closed_by_name ?: '-',
            'status' => $this->label($row->status),
            'cash_sales' => $this->money($row->cash_sales),
            'cash_refunds' => $this->money($row->cash_refunds),
            'expected' => $this->money($row->expected_cash),
            'counted' => $this->money($row->counted_cash),
            'difference' => $this->money($row->difference_amount),
        ]), [
            ['label' => 'Closings', 'value' => (clone $summary)->count()],
            ['label' => 'Expected Cash', 'value' => $this->money((clone $summary)->sum('showroom_cash_closings.expected_cash'))],
            ['label' => 'Counted Cash', 'value' => $this->money((clone $summary)->sum('showroom_cash_closings.counted_cash'))],
            ['label' => 'Difference', 'value' => $this->money((clone $summary)->sum('showroom_cash_closings.difference_amount'))],
        ]);
    }

    public function salesmanSales(Request $request)
    {
        if (! $this->hasTableAndColumns('orders', ['sale_channel'])) {
            return $this->missingTable('Salesman-wise Sales Report', 'admin.showroom.salesman-sales');
        }

        $query = DB::table('orders')
            ->leftJoin('admins as creators', 'creators.id', '=', 'orders.pos_created_by_admin_id')
            ->where('orders.sale_channel', 'showroom');

        $this->dateFilter($query, $request, 'orders.created_at');
        $this->searchFilter($query, $request, ['creators.f_name', 'creators.l_name', 'creators.email', 'orders.payment_status']);

        $summary = clone $query;
        $records = $query
            ->select([
                'orders.pos_created_by_admin_id',
                DB::raw("TRIM(CONCAT(COALESCE(creators.f_name, ''), ' ', COALESCE(creators.l_name, ''))) as salesman_name"),
                'creators.email',
                DB::raw('COUNT(orders.id) as invoice_count'),
                DB::raw('SUM(orders.order_amount) as total_sales'),
                DB::raw("SUM(CASE WHEN orders.payment_status = 'paid' THEN orders.order_amount ELSE 0 END) as paid_sales"),
            ])
            ->groupBy('orders.pos_created_by_admin_id', 'creators.f_name', 'creators.l_name', 'creators.email')
            ->orderByDesc('total_sales');

        return $this->listView('Salesman-wise Sales Report', 'admin.showroom.salesman-sales', [
            ['key' => 'salesman', 'label' => 'Salesman'],
            ['key' => 'email', 'label' => 'Email'],
            ['key' => 'invoice_count', 'label' => 'Invoices', 'class' => 'text-right'],
            ['key' => 'total_sales', 'label' => 'Total Sales', 'class' => 'text-right'],
            ['key' => 'paid_sales', 'label' => 'Paid Sales', 'class' => 'text-right'],
        ], $this->paginate($records, fn ($row) => [
            'salesman' => $row->salesman_name ?: ('Admin #' . ($row->pos_created_by_admin_id ?: '-')),
            'email' => $row->email ?: '-',
            'invoice_count' => $row->invoice_count,
            'total_sales' => $this->money($row->total_sales),
            'paid_sales' => $this->money($row->paid_sales),
        ]), [
            ['label' => 'POS Invoices', 'value' => (clone $summary)->count()],
            ['label' => 'Total Sales', 'value' => $this->money((clone $summary)->sum('orders.order_amount'))],
        ]);
    }

    public function paymentMethods(Request $request)
    {
        if (! $this->hasTableAndColumns('order_payments', ['payment_method']) || ! $this->hasTableAndColumns('orders', ['sale_channel'])) {
            return $this->missingTable('Payment Method Report', 'admin.showroom.payment-methods');
        }

        $query = DB::table('order_payments')
            ->leftJoin('orders', 'orders.id', '=', 'order_payments.order_id')
            ->where('orders.sale_channel', 'showroom');

        $this->dateFilter($query, $request, 'order_payments.created_at');
        $this->searchFilter($query, $request, ['order_payments.payment_method', 'order_payments.provider_name', 'order_payments.payment_status']);
        $summary = clone $query;

        $records = $query
            ->select([
                'order_payments.payment_method',
                'order_payments.provider_name',
                'order_payments.payment_status',
                DB::raw('COUNT(order_payments.id) as transaction_count'),
                DB::raw('SUM(order_payments.amount) as total_amount'),
            ])
            ->groupBy('order_payments.payment_method', 'order_payments.provider_name', 'order_payments.payment_status')
            ->orderByDesc('total_amount');

        return $this->listView('Payment Method Report', 'admin.showroom.payment-methods', [
            ['key' => 'method', 'label' => 'Method'],
            ['key' => 'provider', 'label' => 'Provider'],
            ['key' => 'status', 'label' => 'Status'],
            ['key' => 'transactions', 'label' => 'Transactions', 'class' => 'text-right'],
            ['key' => 'amount', 'label' => 'Amount', 'class' => 'text-right'],
        ], $this->paginate($records, fn ($row) => [
            'method' => $this->label($row->payment_method),
            'provider' => $this->label($row->provider_name),
            'status' => $this->label($row->payment_status),
            'transactions' => $row->transaction_count,
            'amount' => $this->money($row->total_amount),
        ]), [
            ['label' => 'Transactions', 'value' => (clone $summary)->count()],
            ['label' => 'Collection', 'value' => $this->money((clone $summary)->sum('order_payments.amount'))],
        ]);
    }

    public function productSales(Request $request)
    {
        if (! Schema::hasTable('order_details') || ! $this->hasTableAndColumns('orders', ['sale_channel'])) {
            return $this->missingTable('Product-wise POS Sales', 'admin.showroom.product-sales');
        }

        $detailColumns = collect(Schema::getColumnListing('order_details'))->flip();
        $itemIdColumn = $this->firstExistingColumn($detailColumns, ['item_id', 'product_id', 'food_id', 'item_campaign_id']);
        $detailsColumn = $this->firstExistingColumn($detailColumns, ['item_details', 'product_details', 'food_details']);
        $quantityColumn = $this->firstExistingColumn($detailColumns, ['quantity', 'qty']);
        $priceColumn = $this->firstExistingColumn($detailColumns, ['price', 'item_price']);

        if (! $itemIdColumn || ! $quantityColumn || ! $priceColumn) {
            return $this->missingTable('Product-wise POS Sales', 'admin.showroom.product-sales');
        }

        $itemIdExpression = "order_details.{$itemIdColumn}";
        $detailsSelect = $detailsColumn
            ? DB::raw("MIN(order_details.{$detailsColumn}) as item_details")
            : DB::raw("NULL as item_details");
        $searchColumns = array_filter([
            $itemIdExpression,
            $detailsColumn ? "order_details.{$detailsColumn}" : null,
        ]);

        $query = DB::table('order_details')
            ->leftJoin('orders', 'orders.id', '=', 'order_details.order_id')
            ->where('orders.sale_channel', 'showroom');

        $this->dateFilter($query, $request, 'orders.created_at');
        $this->searchFilter($query, $request, $searchColumns);
        $summary = clone $query;

        $records = $query
            ->select([
                DB::raw("{$itemIdExpression} as item_id"),
                $detailsSelect,
                DB::raw("SUM(order_details.{$quantityColumn}) as quantity"),
                DB::raw("SUM(order_details.{$priceColumn} * order_details.{$quantityColumn}) as gross_sales"),
                DB::raw('COUNT(DISTINCT order_details.order_id) as invoice_count'),
            ])
            ->groupBy($itemIdExpression)
            ->orderByDesc('gross_sales');

        return $this->listView('Product-wise POS Sales', 'admin.showroom.product-sales', [
            ['key' => 'item', 'label' => 'Product'],
            ['key' => 'item_id', 'label' => 'Item ID'],
            ['key' => 'invoice_count', 'label' => 'Invoices', 'class' => 'text-right'],
            ['key' => 'quantity', 'label' => 'Quantity', 'class' => 'text-right'],
            ['key' => 'gross_sales', 'label' => 'Gross Sales', 'class' => 'text-right'],
        ], $this->paginate($records, fn ($row) => [
            'item' => $this->itemName($row->item_details),
            'item_id' => $row->item_id ?: '-',
            'invoice_count' => $row->invoice_count,
            'quantity' => $row->quantity,
            'gross_sales' => $this->money($row->gross_sales),
        ]), [
            ['label' => 'Sold Quantity', 'value' => (clone $summary)->sum("order_details.{$quantityColumn}")],
            ['label' => 'Gross Sales', 'value' => $this->money($this->sumExpression(clone $summary, "order_details.{$priceColumn} * order_details.{$quantityColumn}"))],
        ]);
    }

    public function banglaQrTransactions(Request $request)
    {
        return $this->paymentTransactionList($request, 'Bangla QR Transactions', 'admin.showroom.bangla-qr.transactions');
    }

    public function banglaQrPending(Request $request)
    {
        return $this->paymentTransactionList($request, 'Bangla QR Pending Payments', 'admin.showroom.bangla-qr.pending', ['pending', 'initiated']);
    }

    public function banglaQrPendingSettlements(Request $request)
    {
        if (! $this->hasBanglaQrSettlementTables()) {
            return $this->missingTable('Bangla QR Pending Settlement', 'admin.showroom.bangla-qr.pending-settlements');
        }

        $query = $this->unsettledBanglaQrTransactions();
        $this->dateFilter($query, $request, 'payment_transactions.paid_at');
        $this->searchFilter($query, $request, [
            'payment_transactions.transaction_id',
            'payment_transactions.provider_transaction_id',
            'payment_transactions.qr_reference',
            'payment_transactions.source_reference',
            'payment_transactions.provider_name',
        ]);

        $summary = clone $query;
        $records = $query
            ->select([
                'payment_transactions.id',
                'payment_transactions.transaction_id',
                'payment_transactions.provider_name',
                'payment_transactions.provider_transaction_id',
                'payment_transactions.qr_reference',
                'payment_transactions.source_type',
                'payment_transactions.source_id',
                'payment_transactions.source_reference',
                'payment_transactions.amount',
                'payment_transactions.paid_at',
            ])
            ->orderByDesc('payment_transactions.paid_at')
            ->orderByDesc('payment_transactions.id');

        return $this->listView('Bangla QR Pending Settlement', 'admin.showroom.bangla-qr.pending-settlements', [
            ['key' => 'transaction', 'label' => 'Transaction'],
            ['key' => 'paid_at', 'label' => 'Paid At'],
            ['key' => 'provider', 'label' => 'Provider'],
            ['key' => 'provider_txn', 'label' => 'Provider Txn'],
            ['key' => 'qr_reference', 'label' => 'QR Reference'],
            ['key' => 'source', 'label' => 'Source'],
            ['key' => 'amount', 'label' => 'Amount', 'class' => 'text-right'],
        ], $this->paginate($records, fn ($row) => [
            'transaction' => '#' . $row->id . ' / ' . Str::limit($row->transaction_id, 18),
            'paid_at' => $this->dateTime($row->paid_at),
            'provider' => $this->label($row->provider_name),
            'provider_txn' => $row->provider_transaction_id ?: '-',
            'qr_reference' => $row->qr_reference ?: '-',
            'source' => $this->label($row->source_type) . ' #' . ($row->source_id ?: '-') . ($row->source_reference ? ' / ' . $row->source_reference : ''),
            'amount' => $this->money($row->amount),
        ]), [
            ['label' => 'Unsettled Transactions', 'value' => (clone $summary)->count()],
            ['label' => 'Pending Settlement Amount', 'value' => $this->money((clone $summary)->sum('payment_transactions.amount'))],
        ]);
    }

    public function createBanglaQrSettlement(Request $request)
    {
        if (! $this->hasBanglaQrSettlementTables()) {
            return $this->missingTable('Create Bangla QR Settlement', 'admin.showroom.bangla-qr.settlements.create');
        }

        $query = $this->unsettledBanglaQrTransactions();
        $this->dateFilter($query, $request, 'payment_transactions.paid_at');
        $this->searchFilter($query, $request, [
            'payment_transactions.transaction_id',
            'payment_transactions.provider_transaction_id',
            'payment_transactions.qr_reference',
            'payment_transactions.provider_name',
        ]);

        $summaryQuery = clone $query;
        $transactions = $query
            ->select([
                'payment_transactions.id',
                'payment_transactions.transaction_id',
                'payment_transactions.provider_name',
                'payment_transactions.provider_transaction_id',
                'payment_transactions.qr_reference',
                'payment_transactions.amount',
                'payment_transactions.paid_at',
            ])
            ->orderByDesc('payment_transactions.paid_at')
            ->orderByDesc('payment_transactions.id')
            ->paginate($this->perPage)
            ->withQueryString();

        return view('admin-views.showroom.bangla-qr-settlement-create', [
            'transactions' => $transactions,
            'summary' => [
                'count' => $transactions->total(),
                'amount' => $summaryQuery->sum('payment_transactions.amount'),
            ],
        ]);
    }

    public function storeBanglaQrSettlement(Request $request)
    {
        $data = $request->validate([
            'payment_transaction_ids' => 'required|array|min:1',
            'payment_transaction_ids.*' => 'required|integer',
            'settlement_date' => 'required|date',
            'charge_amount' => 'nullable|numeric|min:0',
            'bank_reference' => 'nullable|string|max:160',
        ]);

        if (! $this->hasBanglaQrSettlementTables()) {
            throw ValidationException::withMessages([
                'settlement' => translate('Bangla QR settlement tables are not available.'),
            ]);
        }

        $admin = auth('admin')->user();
        $settlement = DB::transaction(function () use ($data, $admin) {
            $ids = collect($data['payment_transaction_ids'])->map(fn ($id) => (int) $id)->unique()->values();
            $transactionQuery = DB::table('payment_transactions')
                ->whereIn('payment_transactions.id', $ids)
                ->where('payment_transactions.payment_method', 'bangla_qr')
                ->where('payment_transactions.status', 'paid');

            if (Schema::hasTable('manual_payment_verifications')) {
                $transactionQuery->leftJoin('manual_payment_verifications', 'manual_payment_verifications.payment_transaction_id', '=', 'payment_transactions.id')
                    ->where(function ($query) {
                        $query->where('payment_transactions.verified_by_system', true)
                            ->orWhere('manual_payment_verifications.status', 'approved');
                    });
            } else {
                $transactionQuery->where('payment_transactions.verified_by_system', true);
            }

            $transactions = $transactionQuery
                ->select('payment_transactions.*')
                ->lockForUpdate()
                ->get();

            if ($transactions->count() !== $ids->count()) {
                throw ValidationException::withMessages([
                    'payment_transaction_ids' => translate('One or more Bangla QR transactions are not paid/verified/approved or were not found.'),
                ]);
            }

            $alreadySettled = DB::table('bangla_qr_settlement_items')
                ->whereIn('payment_transaction_id', $ids)
                ->lockForUpdate()
                ->exists();
            if ($alreadySettled) {
                throw ValidationException::withMessages([
                    'payment_transaction_ids' => translate('One or more selected Bangla QR transactions are already settled.'),
                ]);
            }

            $gross = round((float) $transactions->sum('amount'), 8);
            $charge = round((float) ($data['charge_amount'] ?? 0), 8);
            if ($charge > $gross) {
                throw ValidationException::withMessages([
                    'charge_amount' => translate('Settlement charge can not be greater than gross amount.'),
                ]);
            }

            $net = round($gross - $charge, 8);
            $provider = $transactions->pluck('provider_name')->filter()->unique()->count() === 1
                ? $transactions->pluck('provider_name')->filter()->first()
                : 'mixed';

            $settlementNo = 'BQR-SET-' . now()->format('ymdHis') . '-' . random_int(1000, 9999);
            $settlementId = DB::table('bangla_qr_settlements')->insertGetId([
                'settlement_no' => $settlementNo,
                'provider_name' => $provider ?: 'bangla_qr',
                'settlement_date' => $data['settlement_date'],
                'gross_amount' => $gross,
                'charge_amount' => $charge,
                'net_amount' => $net,
                'bank_reference' => $data['bank_reference'] ?? null,
                'status' => 'posted',
                'created_by_admin_id' => $admin?->id,
                'created_at' => now(),
                'updated_at' => now(),
            ]);

            $allocatedCharge = 0.0;
            foreach ($transactions->values() as $index => $transaction) {
                $itemCharge = $index === $transactions->count() - 1
                    ? round($charge - $allocatedCharge, 8)
                    : round($gross > 0 ? ($charge * ((float) $transaction->amount / $gross)) : 0, 8);
                $allocatedCharge = round($allocatedCharge + $itemCharge, 8);

                DB::table('bangla_qr_settlement_items')->insert([
                    'bangla_qr_settlement_id' => $settlementId,
                    'payment_transaction_id' => $transaction->id,
                    'gross_amount' => $transaction->amount,
                    'charge_amount' => $itemCharge,
                    'net_amount' => round((float) $transaction->amount - $itemCharge, 8),
                    'created_at' => now(),
                    'updated_at' => now(),
                ]);
            }

            $settlement = DB::table('bangla_qr_settlements')->where('id', $settlementId)->first();
            $voucher = $this->accountingVoucherService->postBanglaQrSettlement($settlement, $admin?->id);
            DB::table('bangla_qr_settlements')->where('id', $settlementId)->update([
                'voucher_id' => $voucher->id,
                'updated_at' => now(),
            ]);

            return DB::table('bangla_qr_settlements')->where('id', $settlementId)->first();
        });

        return redirect()
            ->route('admin.showroom.bangla-qr.settlements')
            ->with('success', translate('Bangla QR settlement posted successfully') . ': ' . $settlement->settlement_no);
    }

    public function banglaQrSettlements(Request $request)
    {
        if (! Schema::hasTable('bangla_qr_settlements')) {
            return $this->missingTable('Bangla QR Settlement Report', 'admin.showroom.bangla-qr.settlements');
        }

        $query = DB::table('bangla_qr_settlements');
        $this->dateFilter($query, $request, 'bangla_qr_settlements.settlement_date');
        $this->searchFilter($query, $request, ['settlement_no', 'provider_name', 'bank_reference', 'status']);

        $summary = clone $query;
        $records = $query
            ->select(['settlement_no', 'provider_name', 'settlement_date', 'gross_amount', 'charge_amount', 'net_amount', 'bank_reference', 'status', 'voucher_id'])
            ->orderByDesc('settlement_date')
            ->orderByDesc('id');

        return $this->listView('Bangla QR Settlement Report', 'admin.showroom.bangla-qr.settlements', [
            ['key' => 'settlement_no', 'label' => 'Settlement No'],
            ['key' => 'date', 'label' => 'Date'],
            ['key' => 'provider', 'label' => 'Provider'],
            ['key' => 'bank_reference', 'label' => 'Bank Reference'],
            ['key' => 'status', 'label' => 'Status'],
            ['key' => 'voucher', 'label' => 'Voucher'],
            ['key' => 'gross', 'label' => 'Gross', 'class' => 'text-right'],
            ['key' => 'charge', 'label' => 'Charge', 'class' => 'text-right'],
            ['key' => 'net', 'label' => 'Net', 'class' => 'text-right'],
        ], $this->paginate($records, fn ($row) => [
            'settlement_no' => $row->settlement_no,
            'date' => $this->date($row->settlement_date),
            'provider' => $this->label($row->provider_name),
            'bank_reference' => $row->bank_reference ?: '-',
            'status' => $this->label($row->status),
            'voucher' => $row->voucher_id ? '#' . $row->voucher_id : '-',
            'gross' => $this->money($row->gross_amount),
            'charge' => $this->money($row->charge_amount),
            'net' => $this->money($row->net_amount),
        ]), [
            ['label' => 'Settlements', 'value' => (clone $summary)->count()],
            ['label' => 'Gross Amount', 'value' => $this->money((clone $summary)->sum('gross_amount'))],
            ['label' => 'Charges', 'value' => $this->money((clone $summary)->sum('charge_amount'))],
            ['label' => 'Net Amount', 'value' => $this->money((clone $summary)->sum('net_amount'))],
        ]);
    }

    public function failedCallbacks(Request $request)
    {
        if (! Schema::hasTable('payment_callback_logs')) {
            return $this->missingTable('Failed Callback Log', 'admin.showroom.bangla-qr.failed-callbacks');
        }

        $query = DB::table('payment_callback_logs')
            ->where(function ($query) {
                $query->whereNotNull('error_message')
                    ->orWhereIn('status', ['failed', 'error'])
                    ->orWhereIn('signature_status', ['failed', 'invalid']);
            });

        $this->dateFilter($query, $request, 'created_at');
        $this->searchFilter($query, $request, ['provider_name', 'event_type', 'status', 'signature_status', 'source_reference', 'error_message']);

        $summary = clone $query;
        $records = $query
            ->select(['id', 'provider_name', 'event_type', 'status', 'signature_status', 'source_reference', 'error_message', 'created_at'])
            ->orderByDesc('id');

        return $this->listView('Failed Payment Callback Log', 'admin.showroom.bangla-qr.failed-callbacks', [
            ['key' => 'log', 'label' => 'Log'],
            ['key' => 'date', 'label' => 'Date'],
            ['key' => 'provider', 'label' => 'Provider'],
            ['key' => 'event', 'label' => 'Event'],
            ['key' => 'reference', 'label' => 'Reference'],
            ['key' => 'status', 'label' => 'Status'],
            ['key' => 'signature', 'label' => 'Signature'],
            ['key' => 'error', 'label' => 'Error'],
        ], $this->paginate($records, fn ($row) => [
            'log' => '#' . $row->id,
            'date' => $this->dateTime($row->created_at),
            'provider' => $this->label($row->provider_name),
            'event' => $this->label($row->event_type),
            'reference' => $row->source_reference ?: '-',
            'status' => $this->label($row->status),
            'signature' => $this->label($row->signature_status),
            'error' => $row->error_message ? Str::limit($row->error_message, 80) : '-',
        ]), [
            ['label' => 'Failed Logs', 'value' => (clone $summary)->count()],
        ]);
    }

    public function manualVerifications(Request $request)
    {
        if (! Schema::hasTable('manual_payment_verifications')) {
            return $this->missingTable('Manual Verification Queue', 'admin.showroom.bangla-qr.manual-verifications');
        }

        $query = DB::table('manual_payment_verifications')
            ->leftJoin('payment_transactions', 'payment_transactions.id', '=', 'manual_payment_verifications.payment_transaction_id');

        $this->dateFilter($query, $request, 'manual_payment_verifications.created_at');
        $this->searchFilter($query, $request, ['manual_payment_verifications.submitted_transaction_id', 'manual_payment_verifications.status', 'payment_transactions.provider_name', 'payment_transactions.payment_method']);

        $summary = clone $query;
        $records = $query
            ->select([
                'manual_payment_verifications.id',
                'manual_payment_verifications.source_type',
                'manual_payment_verifications.source_id',
                'manual_payment_verifications.submitted_transaction_id',
                'manual_payment_verifications.amount',
                'manual_payment_verifications.status',
                'manual_payment_verifications.created_at',
                'payment_transactions.payment_method',
                'payment_transactions.provider_name',
                'payment_transactions.id as payment_transaction_id',
            ])
            ->orderByDesc('manual_payment_verifications.id');

        return $this->listView('Manual Verification Queue', 'admin.showroom.bangla-qr.manual-verifications', [
            ['key' => 'request', 'label' => 'Request'],
            ['key' => 'date', 'label' => 'Date'],
            ['key' => 'source', 'label' => 'Source'],
            ['key' => 'provider', 'label' => 'Provider'],
            ['key' => 'submitted_txn', 'label' => 'Submitted Txn'],
            ['key' => 'status', 'label' => 'Status'],
            ['key' => 'amount', 'label' => 'Amount', 'class' => 'text-right'],
            ['key' => 'actions', 'label' => 'Action', 'raw' => true],
        ], $this->paginate($records, fn ($row) => [
            'request' => '#' . $row->id,
            'date' => $this->dateTime($row->created_at),
            'source' => $this->label($row->source_type) . ' #' . ($row->source_id ?: '-'),
            'provider' => trim($this->label($row->payment_method) . ' / ' . $this->label($row->provider_name), ' /'),
            'submitted_txn' => $row->submitted_transaction_id ?: '-',
            'status' => $this->label($row->status),
            'amount' => $this->money($row->amount),
            'actions' => $this->manualVerificationActions($row),
        ]), [
            ['label' => 'Queue Items', 'value' => (clone $summary)->count()],
            ['label' => 'Pending', 'value' => (clone $summary)->where('manual_payment_verifications.status', 'pending')->count()],
            ['label' => 'Amount', 'value' => $this->money((clone $summary)->sum('manual_payment_verifications.amount'))],
        ]);
    }

    public function approveManualVerification(Request $request, ManualPaymentVerification $manualVerification, BanglaQrPaymentService $paymentService)
    {
        $request->validate([
            'note' => 'nullable|string|max:500',
        ]);

        $paymentService->approveManualVerification($manualVerification, auth('admin')->id(), $request->note);

        return back()->with('success', translate('Manual Bangla QR verification approved successfully.'));
    }

    public function rejectManualVerification(Request $request, ManualPaymentVerification $manualVerification, BanglaQrPaymentService $paymentService)
    {
        $request->validate([
            'note' => 'nullable|string|max:500',
        ]);

        $paymentService->rejectManualVerification($manualVerification, auth('admin')->id(), $request->note);

        return back()->with('success', translate('Manual Bangla QR verification rejected successfully.'));
    }

    public function customerPoints(Request $request)
    {
        if (! Schema::hasTable('customer_point_ledgers')) {
            return $this->missingTable('Customer Point Ledger', 'admin.showroom.points');
        }

        $query = DB::table('customer_point_ledgers')
            ->leftJoin('users as customers', 'customers.id', '=', 'customer_point_ledgers.customer_id');

        $this->dateFilter($query, $request, 'customer_point_ledgers.created_at');
        $this->searchFilter($query, $request, ['customer_point_ledgers.source_type', 'customer_point_ledgers.source_reference', 'customer_point_ledgers.point_type', 'customers.phone', 'customers.f_name', 'customers.l_name']);

        $summary = clone $query;
        $records = $query
            ->select([
                'customer_point_ledgers.id',
                'customer_point_ledgers.source_type',
                'customer_point_ledgers.source_id',
                'customer_point_ledgers.point_type',
                'customer_point_ledgers.points',
                'customer_point_ledgers.amount_value',
                'customer_point_ledgers.balance_after',
                'customer_point_ledgers.created_at',
                'customers.phone as customer_phone',
                DB::raw("TRIM(CONCAT(COALESCE(customers.f_name, ''), ' ', COALESCE(customers.l_name, ''))) as customer_name"),
            ])
            ->orderByDesc('customer_point_ledgers.id');

        return $this->listView('Customer Point Ledger', 'admin.showroom.points', [
            ['key' => 'ledger', 'label' => 'Ledger'],
            ['key' => 'date', 'label' => 'Date'],
            ['key' => 'customer', 'label' => 'Customer'],
            ['key' => 'source', 'label' => 'Source'],
            ['key' => 'type', 'label' => 'Type'],
            ['key' => 'points', 'label' => 'Points', 'class' => 'text-right'],
            ['key' => 'amount_value', 'label' => 'Amount Value', 'class' => 'text-right'],
            ['key' => 'balance_after', 'label' => 'Balance After', 'class' => 'text-right'],
        ], $this->paginate($records, fn ($row) => [
            'ledger' => '#' . $row->id,
            'date' => $this->dateTime($row->created_at),
            'customer' => $this->person($row->customer_name, $row->customer_phone),
            'source' => $this->label($row->source_type) . ' #' . ($row->source_id ?: '-'),
            'type' => $this->label($row->point_type),
            'points' => $row->points,
            'amount_value' => $this->money($row->amount_value),
            'balance_after' => $row->balance_after,
        ]), [
            ['label' => 'Earned Points', 'value' => (clone $summary)->where('customer_point_ledgers.point_type', 'earn')->sum('customer_point_ledgers.points')],
            ['label' => 'Redeemed Points', 'value' => abs((clone $summary)->where('customer_point_ledgers.point_type', 'redeem')->sum('customer_point_ledgers.points'))],
            ['label' => 'Ledger Rows', 'value' => (clone $summary)->count()],
        ]);
    }

    public function stockMovements(Request $request)
    {
        if (! Schema::hasTable('stock_movements')) {
            return $this->missingTable('Stock Movements', 'admin.showroom.stock-movements');
        }

        $query = DB::table('stock_movements')
            ->leftJoin('items', 'items.id', '=', 'stock_movements.item_id');

        $this->dateFilter($query, $request, 'stock_movements.created_at');
        $this->searchFilter($query, $request, ['items.name', 'stock_movements.movement_type', 'stock_movements.source_type', 'stock_movements.source_reference', 'stock_movements.sale_channel']);

        $summary = clone $query;
        $records = $query
            ->select([
                'stock_movements.id',
                'stock_movements.item_id',
                'stock_movements.variation_type',
                'stock_movements.quantity_delta',
                'stock_movements.stock_before',
                'stock_movements.stock_after',
                'stock_movements.movement_type',
                'stock_movements.source_type',
                'stock_movements.source_id',
                'stock_movements.source_reference',
                'stock_movements.sale_channel',
                'stock_movements.voucher_id',
                'stock_movements.created_at',
                'items.name as item_name',
            ])
            ->orderByDesc('stock_movements.id');

        return $this->listView('Stock Movements', 'admin.showroom.stock-movements', [
            ['key' => 'movement', 'label' => 'Movement'],
            ['key' => 'date', 'label' => 'Date'],
            ['key' => 'item', 'label' => 'Product'],
            ['key' => 'variation', 'label' => 'Variation'],
            ['key' => 'channel', 'label' => 'Channel'],
            ['key' => 'type', 'label' => 'Type'],
            ['key' => 'source', 'label' => 'Source'],
            ['key' => 'before', 'label' => 'Before', 'class' => 'text-right'],
            ['key' => 'delta', 'label' => 'Delta', 'class' => 'text-right'],
            ['key' => 'after', 'label' => 'After', 'class' => 'text-right'],
            ['key' => 'voucher', 'label' => 'Voucher'],
        ], $this->paginate($records, fn ($row) => [
            'movement' => '#' . $row->id,
            'date' => $this->dateTime($row->created_at),
            'item' => ($row->item_name ?: 'Item') . ' #' . ($row->item_id ?: '-'),
            'variation' => $row->variation_type ?: '-',
            'channel' => $this->label($row->sale_channel),
            'type' => $this->label($row->movement_type),
            'source' => $this->label($row->source_type) . ' #' . ($row->source_id ?: '-') . ($row->source_reference ? ' / ' . $row->source_reference : ''),
            'before' => $row->stock_before,
            'delta' => $row->quantity_delta,
            'after' => $row->stock_after,
            'voucher' => $row->voucher_id ? '#' . $row->voucher_id : '-',
        ]), [
            ['label' => 'Movements', 'value' => (clone $summary)->count()],
            ['label' => 'Stock In', 'value' => (clone $summary)->where('stock_movements.quantity_delta', '>', 0)->sum('stock_movements.quantity_delta')],
            ['label' => 'Stock Out', 'value' => abs((clone $summary)->where('stock_movements.quantity_delta', '<', 0)->sum('stock_movements.quantity_delta'))],
        ]);
    }

    public function devices(Request $request)
    {
        if (! Schema::hasTable('pos_devices')) {
            return $this->missingTable('POS Device Management', 'admin.showroom.devices');
        }

        $query = DB::table('pos_devices')
            ->leftJoin('admins as registrars', 'registrars.id', '=', 'pos_devices.registered_by_admin_id');

        $this->dateFilter($query, $request, 'pos_devices.created_at');
        $this->searchFilter($query, $request, ['pos_devices.device_uid', 'pos_devices.device_name', 'pos_devices.showroom_id', 'pos_devices.status']);

        $summary = clone $query;
        $records = $query
            ->select([
                'pos_devices.device_uid',
                'pos_devices.device_name',
                'pos_devices.showroom_id',
                'pos_devices.status',
                'pos_devices.last_seen_at',
                'pos_devices.created_at',
                DB::raw("TRIM(CONCAT(COALESCE(registrars.f_name, ''), ' ', COALESCE(registrars.l_name, ''))) as registered_by_name"),
            ])
            ->orderByDesc('pos_devices.id');

        return $this->listView('POS Device Management', 'admin.showroom.devices', [
            ['key' => 'uid', 'label' => 'Device UID'],
            ['key' => 'name', 'label' => 'Device Name'],
            ['key' => 'showroom', 'label' => 'Showroom'],
            ['key' => 'status', 'label' => 'Status'],
            ['key' => 'registered_by', 'label' => 'Registered By'],
            ['key' => 'last_seen', 'label' => 'Last Seen'],
            ['key' => 'created', 'label' => 'Created'],
        ], $this->paginate($records, fn ($row) => [
            'uid' => $row->device_uid,
            'name' => $row->device_name ?: '-',
            'showroom' => $row->showroom_id ?: '-',
            'status' => $this->label($row->status),
            'registered_by' => $row->registered_by_name ?: '-',
            'last_seen' => $this->dateTime($row->last_seen_at),
            'created' => $this->dateTime($row->created_at),
        ]), [
            ['label' => 'Devices', 'value' => (clone $summary)->count()],
            ['label' => 'Active Devices', 'value' => (clone $summary)->where('pos_devices.status', 'active')->count()],
        ]);
    }

    private function paymentTransactionList(Request $request, string $title, string $routeName, ?array $statuses = null)
    {
        if (! Schema::hasTable('payment_transactions')) {
            return $this->missingTable($title, $routeName);
        }

        $query = DB::table('payment_transactions')
            ->where('payment_transactions.payment_method', 'bangla_qr');

        if ($statuses) {
            $query->whereIn('payment_transactions.status', $statuses);
        }

        $this->dateFilter($query, $request, 'payment_transactions.created_at');
        $this->searchFilter($query, $request, ['transaction_id', 'provider_transaction_id', 'qr_reference', 'source_reference', 'status', 'provider_name']);

        $summary = clone $query;
        $records = $query
            ->select([
                'transaction_id',
                'provider_name',
                'provider_transaction_id',
                'qr_type',
                'qr_reference',
                'source_type',
                'source_id',
                'source_reference',
                'amount',
                'currency',
                'status',
                'verified_by_system',
                'paid_at',
                'created_at',
            ])
            ->orderByDesc('id');

        return $this->listView($title, $routeName, [
            ['key' => 'transaction', 'label' => 'Transaction'],
            ['key' => 'date', 'label' => 'Created'],
            ['key' => 'provider', 'label' => 'Provider'],
            ['key' => 'provider_txn', 'label' => 'Provider Txn'],
            ['key' => 'qr', 'label' => 'QR Reference'],
            ['key' => 'source', 'label' => 'Source'],
            ['key' => 'status', 'label' => 'Status'],
            ['key' => 'verified', 'label' => 'Verified'],
            ['key' => 'paid_at', 'label' => 'Paid At'],
            ['key' => 'amount', 'label' => 'Amount', 'class' => 'text-right'],
        ], $this->paginate($records, fn ($row) => [
            'transaction' => Str::limit($row->transaction_id, 18),
            'date' => $this->dateTime($row->created_at),
            'provider' => $this->label($row->provider_name),
            'provider_txn' => $row->provider_transaction_id ?: '-',
            'qr' => trim(($row->qr_type ?: '-') . ' / ' . ($row->qr_reference ?: '-'), ' /'),
            'source' => $this->label($row->source_type) . ' #' . ($row->source_id ?: '-') . ($row->source_reference ? ' / ' . $row->source_reference : ''),
            'status' => $this->label($row->status),
            'verified' => $row->verified_by_system ? translate('Yes') : translate('No'),
            'paid_at' => $this->dateTime($row->paid_at),
            'amount' => $this->money($row->amount) . ' ' . ($row->currency ?: 'BDT'),
        ]), [
            ['label' => 'Transactions', 'value' => (clone $summary)->count()],
            ['label' => 'Total Amount', 'value' => $this->money((clone $summary)->sum('payment_transactions.amount'))],
            ['label' => 'Successful', 'value' => (clone $summary)->where('payment_transactions.status', 'success')->count()],
            ['label' => 'Pending', 'value' => (clone $summary)->whereIn('payment_transactions.status', ['pending', 'initiated'])->count()],
        ]);
    }

    private function hasBanglaQrSettlementTables(): bool
    {
        return Schema::hasTable('payment_transactions')
            && Schema::hasTable('bangla_qr_settlements')
            && Schema::hasTable('bangla_qr_settlement_items');
    }

    private function unsettledBanglaQrTransactions(): Builder
    {
        $query = DB::table('payment_transactions')
            ->leftJoin('bangla_qr_settlement_items', 'bangla_qr_settlement_items.payment_transaction_id', '=', 'payment_transactions.id')
            ->whereNull('bangla_qr_settlement_items.id')
            ->where('payment_transactions.payment_method', 'bangla_qr')
            ->where('payment_transactions.status', 'paid');

        if (Schema::hasTable('manual_payment_verifications')) {
            $query->leftJoin('manual_payment_verifications', 'manual_payment_verifications.payment_transaction_id', '=', 'payment_transactions.id')
                ->where(function ($query) {
                    $query->where('payment_transactions.verified_by_system', true)
                        ->orWhere('manual_payment_verifications.status', 'approved');
                });
        } else {
            $query->where('payment_transactions.verified_by_system', true);
        }

        return $query;
    }

    private function manualVerificationActions(object $row): string
    {
        if ($row->status !== 'pending') {
            return '-';
        }

        $approveUrl = route('admin.showroom.bangla-qr.manual-verifications.approve', $row->id);
        $rejectUrl = route('admin.showroom.bangla-qr.manual-verifications.reject', $row->id);
        $csrf = csrf_field();

        return <<<HTML
            <div class="d-flex gap-1">
                <form method="post" action="{$approveUrl}" class="d-inline">
                    {$csrf}
                    <button type="submit" class="btn btn-sm btn--primary">Approve</button>
                </form>
                <form method="post" action="{$rejectUrl}" class="d-inline">
                    {$csrf}
                    <button type="submit" class="btn btn-sm btn-outline-danger">Reject</button>
                </form>
            </div>
        HTML;
    }

    private function listView(string $title, string $routeName, array $columns, LengthAwarePaginator $records, array $summaryCards = [])
    {
        $payload = [
            'title' => $title,
            'routeName' => $routeName,
            'columns' => $columns,
            'records' => $records,
            'summaryCards' => $summaryCards,
            'emptyText' => 'No data found',
        ];

        if (view()->exists('admin-views.showroom.reports.index')) {
            return view('admin-views.showroom.reports.index', $payload);
        }

        return response($this->fallbackListHtml($payload));
    }

    private function fallbackListHtml(array $payload): string
    {
        $title = e(translate($payload['title']));
        $action = e(route($payload['routeName']));
        $search = e(request('search'));
        $from = e(request('from'));
        $to = e(request('to'));
        $reset = e(route($payload['routeName']));
        $summary = collect($payload['summaryCards'])->map(function ($card) {
            return '<div style="border:1px solid #d8dee4;padding:12px;border-radius:6px;background:#fff;"><div style="color:#667085;font-size:13px;">'
                . e(translate($card['label'])) . '</div><strong>' . e($card['value']) . '</strong></div>';
        })->implode('');

        $headings = collect($payload['columns'])->map(fn ($column) => '<th>' . e(translate($column['label'])) . '</th>')->implode('');
        if ($headings === '') {
            $headings = '<th>' . e(translate('Status')) . '</th>';
        }

        $rows = collect($payload['records']->items())->map(function ($row) use ($payload) {
            $cells = collect($payload['columns'])->map(function ($column) use ($row) {
                $value = data_get($row, $column['key'], '-');
                return '<td>' . (! empty($column['raw']) ? $value : e($value)) . '</td>';
            })->implode('');

            return '<tr>' . $cells . '</tr>';
        })->implode('');

        if ($rows === '') {
            $rows = '<tr><td colspan="' . max(count($payload['columns']), 1) . '" style="text-align:center;padding:24px;">'
                . e(translate($payload['emptyText'])) . '</td></tr>';
        }

        $links = $payload['records']->links()->toHtml();

        return <<<HTML
            <!doctype html>
            <html lang="en">
            <head>
                <meta charset="utf-8">
                <meta name="viewport" content="width=device-width, initial-scale=1">
                <title>{$title}</title>
                <style>
                    body{font-family:Arial,sans-serif;background:#f5f6f8;color:#1f2937;margin:0;padding:24px}
                    .wrap{max-width:1180px;margin:0 auto}.panel{background:#fff;border:1px solid #d8dee4;border-radius:8px;padding:16px;margin-bottom:16px}
                    .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px}.form{display:flex;gap:8px;flex-wrap:wrap}
                    input{padding:10px;border:1px solid #cfd7df;border-radius:6px}.btn{padding:10px 14px;border-radius:6px;border:1px solid #0b7f7a;background:#0b7f7a;color:#fff;text-decoration:none}
                    .reset{background:#fff;color:#0b7f7a}table{width:100%;border-collapse:collapse;background:#fff}th,td{padding:12px;border-bottom:1px solid #e5e7eb;text-align:left}th{background:#f0f4f8}
                    nav{margin-top:16px}.pagination{display:flex;gap:6px;list-style:none;padding:0}.page-link{padding:8px 10px;border:1px solid #d8dee4;background:#fff;text-decoration:none}
                </style>
            </head>
            <body>
                <div class="wrap">
                    <h1>{$title}</h1>
                    <div class="panel">
                        <form class="form" action="{$action}" method="get">
                            <input type="search" name="search" value="{$search}" placeholder="Search">
                            <input type="date" name="from" value="{$from}">
                            <input type="date" name="to" value="{$to}">
                            <button class="btn" type="submit">Go</button>
                            <a class="btn reset" href="{$reset}">Reset</a>
                        </form>
                    </div>
                    <div class="grid" style="margin-bottom:16px;">{$summary}</div>
                    <div class="panel" style="padding:0;overflow:auto;">
                        <table><thead><tr>{$headings}</tr></thead><tbody>{$rows}</tbody></table>
                    </div>
                    {$links}
                </div>
            </body>
            </html>
        HTML;
    }

    private function missingTable(string $title, string $routeName)
    {
        return $this->listView($title, $routeName, [], new LengthAwarePaginator([], 0, $this->perPage, 1, [
            'path' => request()->url(),
            'query' => request()->query(),
        ]), [
            ['label' => 'Status', 'value' => translate('Required migration/table not found')],
        ]);
    }

    private function paginate(Builder $query, callable $mapper): LengthAwarePaginator
    {
        $records = $query->paginate($this->perPage)->withQueryString();
        $records->getCollection()->transform($mapper);

        return $records;
    }

    private function dateFilter(Builder $query, Request $request, string $column): void
    {
        if ($request->filled('from')) {
            $query->whereDate($column, '>=', $request->input('from'));
        }

        if ($request->filled('to')) {
            $query->whereDate($column, '<=', $request->input('to'));
        }
    }

    private function searchFilter(Builder $query, Request $request, array $columns): void
    {
        $search = trim((string) $request->input('search', ''));

        if ($search === '') {
            return;
        }

        $query->where(function ($query) use ($columns, $search) {
            foreach ($columns as $column) {
                $query->orWhere($column, 'like', '%' . $search . '%');
            }
        });
    }

    private function hasTableAndColumns(string $table, array $columns): bool
    {
        if (! Schema::hasTable($table)) {
            return false;
        }

        foreach ($columns as $column) {
            if (! Schema::hasColumn($table, $column)) {
                return false;
            }
        }

        return true;
    }

    private function firstExistingColumn($columns, array $candidates): ?string
    {
        foreach ($candidates as $candidate) {
            if ($columns->has($candidate)) {
                return $candidate;
            }
        }

        return null;
    }

    private function sumExpression(Builder $query, string $expression): float
    {
        return (float) $query->selectRaw("COALESCE(SUM({$expression}), 0) as aggregate")->value('aggregate');
    }

    private function dateTime($value): string
    {
        return $value ? Carbon::parse($value)->format('d M Y h:i A') : '-';
    }

    private function date($value): string
    {
        return $value ? Carbon::parse($value)->format('d M Y') : '-';
    }

    private function money($value): string
    {
        return Helpers::format_currency((float) $value);
    }

    private function label($value): string
    {
        return ($value === null || $value === '') ? '-' : ucwords(str_replace('_', ' ', (string) $value));
    }

    private function person(?string $name, ?string $phone): string
    {
        $text = trim(trim((string) $name) . ($phone ? ' / ' . trim((string) $phone) : ''));

        return $text !== '' ? $text : '-';
    }

    private function itemName($itemDetails): string
    {
        $decoded = is_string($itemDetails) ? json_decode($itemDetails, true) : null;

        return is_array($decoded) ? ($decoded['name'] ?? $decoded['item_name'] ?? '-') : '-';
    }
}
