<?php

namespace Modules\Reporting\Filament\Pages;

use BackedEnum;
use Filament\Actions\Action;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\Select;
use Filament\Pages\Page;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Columns\Summarizers\Sum;
use Filament\Tables\Columns\Summarizers\Summarizer;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Concerns\InteractsWithTable;
use Filament\Tables\Contracts\HasTable;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Query\Builder as QueryBuilder;
use Maatwebsite\Excel\Excel as ExcelFormat;
use Maatwebsite\Excel\Facades\Excel;
use Modules\Booking\Enums\BookingChannel;
use Modules\Booking\Enums\BookingStatus;
use Modules\Booking\Models\Booking;
use Modules\Payment\Enums\PaymentStatus;
use Modules\Payment\Models\Payment;
use Modules\Reporting\Exports\BookingsRevenueExport;
use Modules\Routing\Models\EvRoute;
use UnitEnum;

class BookingsRevenueReport extends Page implements HasTable
{
    use InteractsWithTable;

    protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedDocumentChartBar;

    protected static string|UnitEnum|null $navigationGroup = 'Reports';

    protected static ?string $navigationLabel = 'Bookings & Revenue';

    protected static ?string $title = 'Bookings & Revenue Report';

    protected string $view = 'reporting::filament.pages.bookings-revenue-report';

    /**
     * @var array<string, mixed>|null
     */
    public ?array $filters = [];

    public static function canAccess(): bool
    {
        return auth()->user()?->can('view_reports') ?? false;
    }

    public function mount(): void
    {
        $this->filtersForm->fill();
    }

    public function filtersForm(Schema $schema): Schema
    {
        return $schema
            ->components([
                DatePicker::make('travel_date_from')
                    ->label('Travel date from')
                    ->live(),
                DatePicker::make('travel_date_to')
                    ->label('Travel date to')
                    ->afterOrEqual('travel_date_from')
                    ->live(),
                Select::make('status')
                    ->label('Status')
                    ->options(BookingStatus::class)
                    ->native(false)
                    ->placeholder('All statuses')
                    ->live(),
                Select::make('ev_route_id')
                    ->label('Route')
                    ->options(fn () => EvRoute::with(['fromDestination', 'toDestination'])->get()
                        ->mapWithKeys(fn (EvRoute $route) => [$route->id => $route->name]))
                    ->searchable()
                    ->placeholder('All routes')
                    ->live(),
                Select::make('created_by_channel')
                    ->label('Channel')
                    ->options(BookingChannel::class)
                    ->native(false)
                    ->placeholder('All channels')
                    ->live(),
            ])
            ->columns(3)
            ->statePath('filters');
    }

    public function table(Table $table): Table
    {
        return $table
            ->query(fn (): Builder => $this->reportQuery())
            ->columns([
                TextColumn::make('booking_ref')
                    ->label('Ref')
                    ->sortable(),
                TextColumn::make('travel_date')
                    ->date()
                    ->sortable(),
                TextColumn::make('route.name')
                    ->label('Route'),
                TextColumn::make('created_by_channel')
                    ->badge(),
                TextColumn::make('status')
                    ->badge(),
                TextColumn::make('passenger_name')
                    ->label('Passenger'),
                TextColumn::make('passenger_count')
                    ->label('Pax')
                    ->state(fn (Booking $record) => $record->vehicleOptions->sum('passenger_count'))
                    ->summarize(Summarizer::make()
                        ->label('Total')
                        ->using(fn (QueryBuilder $query) => Booking::query()
                            ->with('vehicleOptions')
                            ->whereIn('id', (clone $query)->pluck('id'))
                            ->get()
                            ->sum(fn (Booking $b) => $b->vehicleOptions->sum('passenger_count')))),
                TextColumn::make('price')
                    ->numeric(2)
                    ->sortable()
                    ->summarize(Sum::make()->label('Total')),
                TextColumn::make('payment_status')
                    ->label('Payment')
                    ->state(fn (Booking $record) => $this->bestPayment($record)?->status?->value ?? '—'),
                TextColumn::make('payment_amount')
                    ->label('Paid')
                    ->state(fn (Booking $record) => $this->bestPayment($record)?->amount)
                    ->summarize(Summarizer::make()
                        ->label('Total')
                        ->using(fn (QueryBuilder $query) => Booking::query()
                            ->with('payments')
                            ->whereIn('id', (clone $query)->pluck('id'))
                            ->get()
                            ->sum(fn (Booking $b) => $this->bestPayment($b)?->amount ?? 0))),
                TextColumn::make('driver_name')
                    ->label('Driver')
                    ->placeholder('—'),
            ])
            ->defaultSort('travel_date', 'desc')
            ->paginated([25, 50, 100]);
    }

    public function reportQuery(): Builder
    {
        $data = $this->filters ?? [];

        return Booking::query()
            ->with(['route.fromDestination', 'route.toDestination', 'payments', 'vehicleOptions'])
            ->when($data['travel_date_from'] ?? null, fn (Builder $q, $d) => $q->whereDate('travel_date', '>=', $d))
            ->when($data['travel_date_to'] ?? null, fn (Builder $q, $d) => $q->whereDate('travel_date', '<=', $d))
            ->when($data['status'] ?? null, fn (Builder $q, $s) => $q->where('status', $s))
            ->when($data['ev_route_id'] ?? null, fn (Builder $q, $id) => $q->where('ev_route_id', $id))
            ->when($data['created_by_channel'] ?? null, fn (Builder $q, $c) => $q->where('created_by_channel', $c))
            // A unique tie-breaker after travel_date — required for FromQuery's
            // chunked export to paginate deterministically (see its docblock);
            // the table's own defaultSort() applies on top of this for display.
            ->orderBy('travel_date', 'desc')
            ->orderBy('id');
    }

    protected function bestPayment(Booking $record): ?Payment
    {
        return $record->payments->sortByDesc(
            fn (Payment $p) => $p->status === PaymentStatus::Completed ? 1 : 0
        )->first();
    }

    protected function getHeaderActions(): array
    {
        return [
            Action::make('exportCsv')
                ->label('Export CSV')
                ->icon(Heroicon::OutlinedArrowDownTray)
                ->action(fn () => Excel::download(
                    new BookingsRevenueExport($this->reportQuery()),
                    'bookings-revenue-'.now()->format('Y-m-d').'.csv',
                    ExcelFormat::CSV,
                )),
            Action::make('exportXlsx')
                ->label('Export Excel')
                ->icon(Heroicon::OutlinedArrowDownTray)
                ->action(fn () => Excel::download(
                    new BookingsRevenueExport($this->reportQuery()),
                    'bookings-revenue-'.now()->format('Y-m-d').'.xlsx',
                    ExcelFormat::XLSX,
                )),
        ];
    }
}
