<?php

namespace App\Services\Reports;

use App\Enums\AppointmentStatus;
use App\Enums\InvoiceStatus;
use App\Enums\PaymentStatus;
use App\Enums\ReminderStatus;
use App\Models\Appointment;
use App\Models\Business;
use App\Models\Customer;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\ReminderLog;
use App\Models\Service;
use App\Models\StaffProfile;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

class ReportService
{
    public function overview(Business $business, Request $request): array
    {
        $filters = $this->filters($request);
        $appointments = $this->appointmentQuery($business, $filters);
        $totalBookings = (clone $appointments)->count();
        $completedBookings = (clone $appointments)->where('status', AppointmentStatus::Completed->value)->count();
        $cancelledBookings = (clone $appointments)->where('status', AppointmentStatus::Cancelled->value)->count();
        $totalRevenue = (float) $this->paymentQuery($business, $filters)->sum('amount');
        $pendingInvoices = (float) $this->invoiceQuery($business, array_merge($filters, ['status' => 'all']))
            ->whereIn('status', [InvoiceStatus::Unpaid->value, InvoiceStatus::Partial->value, InvoiceStatus::Overdue->value])
            ->sum('balance_due');
        $repeatCustomers = Customer::query()
            ->forBusiness($business)
            ->whereHas('appointments', fn (Builder $query) => $this->applyAppointmentFilters($query, array_merge($filters, ['status' => 'all'])))
            ->withCount(['appointments' => fn (Builder $query) => $this->applyAppointmentFilters($query, array_merge($filters, ['status' => 'all']))])
            ->get()
            ->where('appointments_count', '>=', 2)
            ->count();
        $reminderRate = $this->reminderSuccessRate($business, $filters);

        return $this->withContext($business, $request, AppointmentStatus::options()) + [
            'summary' => [
                ['label' => 'Total Bookings', 'value' => number_format($totalBookings), 'note' => $this->rangeLabel($filters), 'icon' => 'calendar-days', 'class' => 'bg-blue-50 text-booklio-primary'],
                ['label' => 'Completed Bookings', 'value' => number_format($completedBookings), 'note' => 'Completed appointments', 'icon' => 'check', 'class' => 'bg-emerald-50 text-emerald-600'],
                ['label' => 'Cancelled Bookings', 'value' => number_format($cancelledBookings), 'note' => 'Cancelled appointments', 'icon' => 'x', 'class' => 'bg-red-50 text-red-600'],
                ['label' => 'Total Revenue', 'value' => $this->money($totalRevenue, $business), 'note' => 'Paid payments', 'icon' => 'dollar-sign', 'class' => 'bg-emerald-50 text-emerald-600'],
                ['label' => 'Pending Invoices', 'value' => $this->money($pendingInvoices, $business), 'note' => 'Uncollected balance', 'icon' => 'file-text', 'class' => 'bg-amber-50 text-amber-600'],
                ['label' => 'Repeat Customers', 'value' => number_format($repeatCustomers), 'note' => '2+ bookings in range', 'icon' => 'users', 'class' => 'bg-sky-50 text-sky-600'],
                ['label' => 'Reminder Success Rate', 'value' => $reminderRate.'%', 'note' => 'Sent vs failed logs', 'icon' => 'bell', 'class' => 'bg-purple-50 text-purple-600'],
            ],
            'trendBars' => $this->bookingsByDate($business, $filters),
            'statusMix' => $this->appointmentStatusMix($business, $filters),
            'topServices' => $this->serviceRows($business, $filters)->take(8),
        ];
    }

    public function bookings(Business $business, Request $request): array
    {
        $filters = $this->filters($request);

        return $this->withContext($business, $request, AppointmentStatus::options()) + [
            'bookingsByDate' => $this->bookingsByDate($business, $filters),
            'bookingsByStatus' => $this->appointmentStatusMix($business, $filters),
            'appointments' => $this->appointmentQuery($business, $filters)
                ->with(['customer', 'service', 'staffProfile'])
                ->latest('starts_at')
                ->paginate(15)
                ->withQueryString(),
        ];
    }

    public function revenue(Business $business, Request $request): array
    {
        $filters = $this->filters($request);
        $paid = (float) $this->invoiceQuery($business, $filters)->sum('paid_total');
        $unpaid = (float) $this->invoiceQuery($business, $filters)->sum('balance_due');
        $total = max($paid + $unpaid, 1);

        return $this->withContext($business, $request, InvoiceStatus::options()) + [
            'revenueBars' => $this->invoiceTotalsByDate($business, $filters),
            'paidVsUnpaid' => [
                ['label' => 'Paid', 'value' => $this->money($paid, $business), 'percent' => round(($paid / $total) * 100, 1), 'class' => 'bg-emerald-500'],
                ['label' => 'Unpaid', 'value' => $this->money($unpaid, $business), 'percent' => round(($unpaid / $total) * 100, 1), 'class' => 'bg-amber-500'],
            ],
            'paymentMethods' => $this->paymentMethods($business, $filters),
            'rows' => $this->revenueRows($business, $filters),
        ];
    }

    public function staff(Business $business, Request $request): array
    {
        $filters = $this->filters($request);

        return $this->withContext($business, $request, AppointmentStatus::options()) + [
            'staff' => StaffProfile::query()
                ->forBusiness($business)
                ->when($filters['staff_id'] !== 'all', fn (Builder $query) => $query->whereKey($filters['staff_id']))
                ->orderBy('name')
                ->get()
                ->map(fn (StaffProfile $staff) => $this->staffRow($business, $staff, $filters)),
        ];
    }

    public function services(Business $business, Request $request): array
    {
        $filters = $this->filters($request);
        $services = $this->serviceRows($business, $filters);

        return $this->withContext($business, $request, AppointmentStatus::options()) + [
            'services' => $services,
            'mostBooked' => $services->sortByDesc('bookings')->take(3)->values(),
            'leastBooked' => $services->sortBy('bookings')->take(2)->values(),
        ];
    }

    public function customers(Business $business, Request $request): array
    {
        $filters = $this->filters($request);
        $customers = Customer::query()
            ->forBusiness($business)
            ->when($filters['status'] !== 'all', fn (Builder $query) => $query->where('status', $filters['status']));
        $totalCustomers = (clone $customers)->count();
        $newCustomers = (clone $customers)->whereBetween('created_at', [$filters['start'], $filters['end']])->count();
        $appointmentFilters = array_merge($filters, ['status' => 'all']);
        $returningCustomers = (clone $customers)
            ->whereHas('appointments', fn (Builder $query) => $this->applyAppointmentFilters($query, $appointmentFilters))
            ->withCount(['appointments' => fn (Builder $query) => $this->applyAppointmentFilters($query, $appointmentFilters)])
            ->get()
            ->where('appointments_count', '>=', 2)
            ->count();
        $totalSpent = (float) Payment::query()
            ->forBusiness($business)
            ->where('status', PaymentStatus::Paid->value)
            ->whereBetween('paid_at', [$filters['start'], $filters['end']])
            ->sum('amount');
        $appointmentCount = $this->appointmentQuery($business, $appointmentFilters)->count();

        return $this->withContext($business, $request, Customer::STATUSES) + [
            'kpis' => [
                ['label' => 'New Customers', 'value' => number_format($newCustomers), 'note' => $this->rangeLabel($filters), 'tone' => 'success'],
                ['label' => 'Returning Customers', 'value' => number_format($returningCustomers), 'note' => $totalCustomers ? round(($returningCustomers / $totalCustomers) * 100, 1).'% repeat rate' : 'No customers yet'],
                ['label' => 'Average Spend', 'value' => $this->money($totalCustomers ? $totalSpent / $totalCustomers : 0, $business), 'note' => 'Per customer in range'],
                ['label' => 'Appointments / Customer', 'value' => $totalCustomers ? number_format($appointmentCount / $totalCustomers, 1) : '0.0', 'note' => $this->rangeLabel($filters)],
            ],
            'growthBars' => $this->customerGrowth($business, $filters),
            'appointmentHistory' => $this->customerAppointmentHistory($business, $filters),
            'customers' => $this->topCustomers($business, $filters),
        ];
    }

    public function invoices(Business $business, Request $request): array
    {
        $filters = $this->filters($request);
        $invoices = $this->invoiceQuery($business, $filters);
        $total = (clone $invoices)->count();
        $paidCount = (clone $invoices)->where('status', InvoiceStatus::Paid->value)->count();
        $unpaidCount = (clone $invoices)->where('status', InvoiceStatus::Unpaid->value)->count();
        $overdueCount = (clone $invoices)->where('status', InvoiceStatus::Overdue->value)->count();
        $gross = (float) (clone $invoices)->sum('total');

        return $this->withContext($business, $request, InvoiceStatus::options()) + [
            'kpis' => [
                ['label' => 'Total Invoices', 'value' => number_format($total), 'note' => $this->rangeLabel($filters)],
                ['label' => 'Paid Invoices', 'value' => number_format($paidCount), 'note' => $this->money((float) $this->invoiceQuery($business, $filters)->where('status', InvoiceStatus::Paid->value)->sum('paid_total'), $business), 'tone' => 'success'],
                ['label' => 'Unpaid Invoices', 'value' => number_format($unpaidCount), 'note' => 'Awaiting payment', 'tone' => 'warning'],
                ['label' => 'Overdue Invoices', 'value' => number_format($overdueCount), 'note' => 'Needs follow-up', 'tone' => 'danger'],
                ['label' => 'Invoice Amount Summary', 'value' => $this->money($gross, $business), 'note' => 'Gross invoice amount'],
            ],
            'amountBars' => $this->invoiceTotalsByDate($business, $filters),
            'collectionHealth' => $this->invoiceCollectionHealth($business, $filters),
            'invoices' => $this->invoiceQuery($business, $filters)
                ->with('customer')
                ->latest('issue_date')
                ->paginate(15)
                ->withQueryString(),
        ];
    }

    public function reminders(Business $business, Request $request): array
    {
        $filters = $this->filters($request);
        $logs = $this->reminderQuery($business, $filters);
        $total = (clone $logs)->count();
        $sent = (clone $logs)->where('status', ReminderStatus::Sent->value)->count();
        $failed = (clone $logs)->where('status', ReminderStatus::Failed->value)->count();
        $whatsapp = (clone $logs)->where('channel', 'whatsapp')->count();
        $email = (clone $logs)->where('channel', 'email')->count();

        return $this->withContext($business, $request, $this->reminderStatusOptions()) + [
            'kpis' => [
                ['label' => 'Total Reminders', 'value' => number_format($total), 'note' => $this->rangeLabel($filters)],
                ['label' => 'Sent Reminders', 'value' => number_format($sent), 'note' => 'Delivered', 'tone' => 'success'],
                ['label' => 'Failed Reminders', 'value' => number_format($failed), 'note' => 'Needs review', 'tone' => 'danger'],
                ['label' => 'WhatsApp Reminders', 'value' => number_format($whatsapp), 'note' => $total ? round(($whatsapp / $total) * 100).'% share' : '0% share'],
                ['label' => 'Email Reminders', 'value' => number_format($email), 'note' => $total ? round(($email / $total) * 100).'% share' : '0% share'],
            ],
            'successBars' => $this->reminderSuccessBars($business, $filters),
            'channelSuccess' => $this->channelSuccess($business, $filters),
            'logs' => $this->reminderQuery($business, $filters)
                ->with('customer')
                ->latest()
                ->paginate(15)
                ->withQueryString(),
        ];
    }

    public function withContext(Business $business, Request $request, array $statusOptions): array
    {
        return [
            'filters' => $this->filters($request),
            'rangeOptions' => [
                'today' => 'Today',
                'this_week' => 'This Week',
                'this_month' => 'This Month',
                'last_30' => 'Last 30 Days',
                'custom' => 'Custom Date Range',
            ],
            'staffOptions' => ['all' => 'All Staff'] + StaffProfile::query()->forBusiness($business)->orderBy('name')->pluck('name', 'id')->all(),
            'serviceOptions' => ['all' => 'All Services'] + Service::query()->forBusiness($business)->orderBy('name')->pluck('name', 'id')->all(),
            'statusOptions' => ['all' => 'All Statuses'] + $statusOptions,
        ];
    }

    public function filters(Request $request): array
    {
        $range = $request->query('range', 'this_month');
        $now = now();

        [$start, $end] = match ($range) {
            'today' => [$now->copy()->startOfDay(), $now->copy()->endOfDay()],
            'this_week' => [$now->copy()->startOfWeek(), $now->copy()->endOfWeek()],
            'last_30' => [$now->copy()->subDays(29)->startOfDay(), $now->copy()->endOfDay()],
            'custom' => $this->customDateRange($request),
            default => [$now->copy()->startOfMonth(), $now->copy()->endOfMonth()],
        };

        return [
            'range' => $range,
            'start' => $start,
            'end' => $end,
            'start_date' => $start->toDateString(),
            'end_date' => $end->toDateString(),
            'staff_id' => $request->query('staff_id', 'all') ?: 'all',
            'service_id' => $request->query('service_id', 'all') ?: 'all',
            'status' => $request->query('status', 'all') ?: 'all',
        ];
    }

    public function appointmentQuery(Business $business, array $filters): Builder
    {
        return $this->applyAppointmentFilters(Appointment::query()->forBusiness($business), $filters);
    }

    private function invoiceQuery(Business $business, array $filters): Builder
    {
        return Invoice::query()
            ->forBusiness($business)
            ->whereBetween('issue_date', [$filters['start']->toDateString(), $filters['end']->toDateString()])
            ->when($filters['status'] !== 'all', fn (Builder $query) => $query->where('status', $filters['status']))
            ->when($filters['staff_id'] !== 'all' || $filters['service_id'] !== 'all', function (Builder $query) use ($filters): void {
                $query->whereHas('appointment', fn (Builder $appointment) => $this->applyAppointmentFilters($appointment, array_merge($filters, ['status' => 'all']), false));
            });
    }

    private function paymentQuery(Business $business, array $filters): Builder
    {
        return Payment::query()
            ->forBusiness($business)
            ->where('status', PaymentStatus::Paid->value)
            ->whereBetween('paid_at', [$filters['start'], $filters['end']])
            ->when($filters['staff_id'] !== 'all' || $filters['service_id'] !== 'all', function (Builder $query) use ($filters): void {
                $query->whereHas('invoice.appointment', fn (Builder $appointment) => $this->applyAppointmentFilters($appointment, array_merge($filters, ['status' => 'all']), false));
            });
    }

    private function reminderQuery(Business $business, array $filters): Builder
    {
        return ReminderLog::query()
            ->forBusiness($business)
            ->where(function (Builder $query) use ($filters): void {
                $query->whereBetween('sent_at', [$filters['start'], $filters['end']])
                    ->orWhere(function (Builder $query) use ($filters): void {
                        $query->whereNull('sent_at')->whereBetween('created_at', [$filters['start'], $filters['end']]);
                    });
            })
            ->when($filters['status'] !== 'all', fn (Builder $query) => $query->where('status', $filters['status']));
    }

    private function applyAppointmentFilters(Builder $query, array $filters, bool $includeDate = true): Builder
    {
        return $query
            ->when($includeDate, fn (Builder $query) => $query->whereBetween('starts_at', [$filters['start'], $filters['end']]))
            ->when($filters['staff_id'] !== 'all', fn (Builder $query) => $query->where('staff_profile_id', $filters['staff_id']))
            ->when($filters['service_id'] !== 'all', fn (Builder $query) => $query->where('service_id', $filters['service_id']))
            ->when($filters['status'] !== 'all', fn (Builder $query) => $query->where('status', $filters['status']));
    }

    private function bookingsByDate(Business $business, array $filters): Collection
    {
        $rows = $this->appointmentQuery($business, $filters)
            ->selectRaw('date(starts_at) as label, count(*) as total')
            ->groupBy('label')
            ->orderBy('label')
            ->get();

        return $this->withChartHeights($rows, 'total', fn ($row) => [
            'label' => Carbon::parse($row->label)->format('M d'),
            'value' => (int) $row->total,
        ]);
    }

    private function appointmentStatusMix(Business $business, array $filters): Collection
    {
        $rows = $this->appointmentQuery($business, $filters)
            ->select('status', DB::raw('count(*) as total'))
            ->groupBy('status')
            ->get();
        $total = max((int) $rows->sum('total'), 1);

        return $rows->map(function ($row) use ($total) {
            $status = $row->status instanceof AppointmentStatus ? $row->status->value : (string) $row->status;

            return [
                'label' => AppointmentStatus::tryFrom($status)?->label() ?? Str::headline($status),
                'count' => (int) $row->total,
                'percent' => round(((int) $row->total / $total) * 100, 1),
                'class' => $this->appointmentStatusColor($status),
                'variant' => $this->appointmentStatusVariant($status),
            ];
        })->values();
    }

    private function invoiceTotalsByDate(Business $business, array $filters): Collection
    {
        $rows = $this->invoiceQuery($business, $filters)
            ->selectRaw('date(issue_date) as label, sum(total) as total')
            ->groupBy('label')
            ->orderBy('label')
            ->get();

        return $this->withChartHeights($rows, 'total', fn ($row) => [
            'label' => Carbon::parse($row->label)->format('M d'),
            'value' => (float) $row->total,
            'display' => $this->money((float) $row->total, $business),
        ]);
    }

    private function paymentMethods(Business $business, array $filters): Collection
    {
        $rows = $this->paymentQuery($business, $filters)
            ->select('method', DB::raw('sum(amount) as total'))
            ->groupBy('method')
            ->orderByDesc('total')
            ->get();
        $grandTotal = max((float) $rows->sum('total'), 1);

        return $rows->map(fn ($row) => [
            'label' => Str::headline(str_replace('_', ' ', (string) $row->method)),
            'amount' => $this->money((float) $row->total, $business),
            'percent' => round(((float) $row->total / $grandTotal) * 100, 1).'%',
        ]);
    }

    private function revenueRows(Business $business, array $filters): Collection
    {
        return $this->invoiceQuery($business, $filters)
            ->selectRaw('date(issue_date) as date_label, count(*) as invoices_count, sum(paid_total) as paid, sum(balance_due) as unpaid, sum(total) as total')
            ->groupBy('date_label')
            ->orderByDesc('date_label')
            ->get()
            ->map(fn ($row) => [
                'date' => Carbon::parse($row->date_label)->format('M d, Y'),
                'bookings' => (int) $row->invoices_count,
                'paid' => $this->money((float) $row->paid, $business),
                'unpaid' => $this->money((float) $row->unpaid, $business),
                'total' => $this->money((float) $row->total, $business),
            ]);
    }

    private function staffRow(Business $business, StaffProfile $staff, array $filters): array
    {
        $appointments = $this->appointmentQuery($business, $filters)->where('staff_profile_id', $staff->id);
        $total = (clone $appointments)->count();
        $completed = (clone $appointments)->where('status', AppointmentStatus::Completed->value)->count();
        $cancelled = (clone $appointments)->where('status', AppointmentStatus::Cancelled->value)->count();
        $revenue = (float) (clone $appointments)->whereIn('status', [AppointmentStatus::Completed->value, AppointmentStatus::Confirmed->value])->sum('price');
        $completionRate = $total ? ($completed / $total) * 100 : 0;
        $badge = $completionRate >= 80 ? 'Top Performer' : ($completionRate >= 55 ? 'Strong' : 'Needs Focus');

        return [
            'name' => $staff->name,
            'appointments' => $total,
            'completed' => $completed,
            'cancelled' => $cancelled,
            'revenue' => $this->money($revenue, $business),
            'rating' => $staff->rating_display,
            'badge' => $badge,
            'variant' => $badge === 'Needs Focus' ? 'warning' : 'success',
        ];
    }

    private function serviceRows(Business $business, array $filters): Collection
    {
        return Service::query()
            ->forBusiness($business)
            ->when($filters['service_id'] !== 'all', fn (Builder $query) => $query->whereKey($filters['service_id']))
            ->orderBy('name')
            ->get()
            ->map(function (Service $service) use ($business, $filters) {
                $appointments = $this->appointmentQuery($business, $filters)->where('service_id', $service->id);
                $bookings = (clone $appointments)->count();
                $revenue = (float) (clone $appointments)->whereIn('status', [AppointmentStatus::Completed->value, AppointmentStatus::Confirmed->value])->sum('price');

                return [
                    'name' => $service->name,
                    'bookings' => $bookings,
                    'revenue' => $this->money($revenue, $business),
                    'duration' => $service->duration_minutes.' min',
                    'rank' => $bookings === 0 ? 'Least Booked' : ($revenue >= 100 ? 'High Revenue' : 'Booked'),
                    'variant' => $bookings === 0 ? 'warning' : 'success',
                    'percent' => min(100, max(8, $bookings * 20)),
                ];
            })->values();
    }

    private function customerGrowth(Business $business, array $filters): Collection
    {
        $rows = Customer::query()
            ->forBusiness($business)
            ->whereBetween('created_at', [$filters['start'], $filters['end']])
            ->selectRaw('date(created_at) as label, count(*) as total')
            ->groupBy('label')
            ->orderBy('label')
            ->get();

        return $this->withChartHeights($rows, 'total', fn ($row) => [
            'label' => Carbon::parse($row->label)->format('M d'),
            'value' => (int) $row->total,
        ]);
    }

    private function customerAppointmentHistory(Business $business, array $filters): array
    {
        $counts = Customer::query()
            ->forBusiness($business)
            ->withCount(['appointments' => fn (Builder $query) => $this->applyAppointmentFilters($query, array_merge($filters, ['status' => 'all']))])
            ->get()
            ->pluck('appointments_count');
        $total = max($counts->count(), 1);

        return [
            ['label' => '1 appointment', 'percent' => round(($counts->filter(fn ($count) => $count === 1)->count() / $total) * 100, 1), 'class' => 'bg-slate-400'],
            ['label' => '2-5 appointments', 'percent' => round(($counts->filter(fn ($count) => $count >= 2 && $count <= 5)->count() / $total) * 100, 1), 'class' => 'bg-booklio-primary'],
            ['label' => '6+ appointments', 'percent' => round(($counts->filter(fn ($count) => $count >= 6)->count() / $total) * 100, 1), 'class' => 'bg-emerald-500'],
        ];
    }

    private function topCustomers(Business $business, array $filters): Collection
    {
        return Customer::query()
            ->forBusiness($business)
            ->withCount(['appointments' => fn (Builder $query) => $this->applyAppointmentFilters($query, array_merge($filters, ['status' => 'all']))])
            ->withSum(['payments' => fn (Builder $query) => $query->where('status', PaymentStatus::Paid->value)->whereBetween('paid_at', [$filters['start'], $filters['end']])], 'amount')
            ->orderByDesc('payments_sum_amount')
            ->limit(10)
            ->get()
            ->map(fn (Customer $customer) => [
                'name' => $customer->name,
                'spent' => $this->money((float) ($customer->payments_sum_amount ?? 0), $business),
                'appointments' => $customer->appointments_count,
                'last' => $customer->last_visit_at?->format('M d, Y') ?? '-',
            ]);
    }

    private function invoiceCollectionHealth(Business $business, array $filters): array
    {
        $query = $this->invoiceQuery($business, $filters);
        $total = max((clone $query)->count(), 1);

        return collect([
            ['label' => 'Paid', 'count' => (clone $query)->where('status', InvoiceStatus::Paid->value)->count(), 'class' => 'bg-emerald-500'],
            ['label' => 'Unpaid', 'count' => (clone $query)->where('status', InvoiceStatus::Unpaid->value)->count(), 'class' => 'bg-amber-500'],
            ['label' => 'Overdue', 'count' => (clone $query)->where('status', InvoiceStatus::Overdue->value)->count(), 'class' => 'bg-red-500'],
        ])->map(fn ($item) => $item + ['percent' => round(($item['count'] / $total) * 100, 1)])->all();
    }

    private function reminderSuccessBars(Business $business, array $filters): Collection
    {
        $rows = $this->reminderQuery($business, $filters)
            ->selectRaw('date(coalesce(sent_at, created_at)) as label, count(*) as total, sum(case when status = ? then 1 else 0 end) as sent_count', [ReminderStatus::Sent->value])
            ->groupBy('label')
            ->orderBy('label')
            ->get();

        return $rows->map(fn ($row) => [
            'label' => Carbon::parse($row->label)->format('M d'),
            'rate' => (int) $row->total ? round(((int) $row->sent_count / (int) $row->total) * 100, 1) : 0,
        ]);
    }

    private function channelSuccess(Business $business, array $filters): Collection
    {
        return collect(['whatsapp' => 'WhatsApp', 'email' => 'Email'])
            ->map(function (string $label, string $channel) use ($business, $filters) {
                $query = $this->reminderQuery($business, $filters)->where('channel', $channel);
                $total = (clone $query)->count();
                $sent = (clone $query)->where('status', ReminderStatus::Sent->value)->count();

                return [
                    'label' => $label,
                    'percent' => $total ? round(($sent / $total) * 100, 1) : 0,
                    'class' => $channel === 'whatsapp' ? 'bg-emerald-500' : 'bg-booklio-primary',
                ];
            })->values();
    }

    private function reminderSuccessRate(Business $business, array $filters): float
    {
        $query = $this->reminderQuery($business, $filters);
        $total = (clone $query)->whereIn('status', [ReminderStatus::Sent->value, ReminderStatus::Failed->value])->count();

        if (! $total) {
            return 0;
        }

        return round(((clone $query)->where('status', ReminderStatus::Sent->value)->count() / $total) * 100, 1);
    }

    private function withChartHeights(Collection $rows, string $valueKey, callable $mapper): Collection
    {
        $max = max((float) $rows->max($valueKey), 1);

        return $rows->map(function ($row) use ($max, $valueKey, $mapper) {
            return $mapper($row) + ['height' => max(18, round(((float) $row->{$valueKey} / $max) * 150))];
        })->values();
    }

    private function customDateRange(Request $request): array
    {
        try {
            $start = $request->query('start_date') ? Carbon::parse($request->query('start_date'))->startOfDay() : now()->startOfMonth();
            $end = $request->query('end_date') ? Carbon::parse($request->query('end_date'))->endOfDay() : now()->endOfMonth();
        } catch (\Throwable) {
            return [now()->startOfMonth(), now()->endOfMonth()];
        }

        return $start->greaterThan($end) ? [$end, $start] : [$start, $end];
    }

    private function rangeLabel(array $filters): string
    {
        return Carbon::parse($filters['start'])->format('M d').' - '.Carbon::parse($filters['end'])->format('M d, Y');
    }

    private function money(float $amount, Business $business): string
    {
        $currency = strtoupper($business->currency ?? 'USD');

        return match ($currency) {
            'USD' => '$'.number_format($amount, 2),
            default => $currency.' '.number_format($amount, 2),
        };
    }

    private function appointmentStatusColor(string $status): string
    {
        return match ($status) {
            AppointmentStatus::Completed->value => 'bg-emerald-500',
            AppointmentStatus::Confirmed->value, AppointmentStatus::CheckedIn->value => 'bg-blue-500',
            AppointmentStatus::Cancelled->value => 'bg-red-500',
            AppointmentStatus::NoShow->value => 'bg-slate-400',
            default => 'bg-amber-500',
        };
    }

    private function appointmentStatusVariant(string $status): string
    {
        return match ($status) {
            AppointmentStatus::Completed->value => 'success',
            AppointmentStatus::Confirmed->value, AppointmentStatus::CheckedIn->value => 'primary',
            AppointmentStatus::Cancelled->value => 'danger',
            AppointmentStatus::NoShow->value => 'muted',
            default => 'warning',
        };
    }

    private function reminderStatusOptions(): array
    {
        return collect(ReminderStatus::cases())
            ->mapWithKeys(fn (ReminderStatus $status) => [$status->value => Str::headline($status->value)])
            ->all();
    }
}
