<?php

namespace App\Filament\Support;

use Carbon\Carbon;
use Carbon\CarbonInterface;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\Select;
use Filament\Schemas\Components\Utilities\Get;
use Filament\Tables\Filters\Filter;
use Illuminate\Database\Eloquent\Builder;

/**
 * The single "Date Range Preset" control shared by every report.
 *
 * Replaces the old pair of standalone From/To date pickers with one dropdown
 * (Today — default — / This Week / Last Week / This Month / Last Month /
 * This Year / Custom); the From/To pickers only appear when `custom` is
 * chosen. Both the report pages and the simplified report Resources
 * (CashMovementResource) build their date filter through {@see Filter()}, so
 * the preset semantics, the applied window and the human-readable
 * description all stay in lockstep.
 */
final class ReportDateRange
{
    /** @return array<string, string> */
    public static function options(): array
    {
        return [
            'today' => 'Today',
            'this_week' => 'This Week',
            'last_week' => 'Last Week',
            'this_month' => 'This Month',
            'last_month' => 'Last Month',
            'this_year' => 'This Year',
            'custom' => 'Custom',
        ];
    }

    public static function label(string $range): string
    {
        return self::options()[$range] ?? $range;
    }

    public static function filter(string $column = 'created_at'): Filter
    {
        return Filter::make('date')
            ->label('Date range')
            ->schema([
                Select::make('range')
                    ->label('Range')
                    ->options(self::options())
                    ->default('today')
                    ->live(),
                DatePicker::make('from')
                    ->label('From date')
                    ->visible(fn (Get $get): bool => $get('range') === 'custom'),
                DatePicker::make('to')
                    ->label('To date')
                    ->visible(fn (Get $get): bool => $get('range') === 'custom'),
            ])
            ->indicateUsing(fn (array $data): ?string => filled($data['range'] ?? null)
                ? 'Range: '.self::label($data['range'])
                : null)
            ->query(fn (Builder $query, array $data): Builder => self::apply($query, $data, $column));
    }

    /**
     * The closed [from, to] window implied by the filter's state. `custom`
     * uses the user's own dates; every preset is computed against now.
     *
     * @param  array<string, mixed>  $data
     * @return array{CarbonInterface, CarbonInterface} | null
     */
    public static function bounds(array $data): ?array
    {
        $range = $data['range'] ?? 'today';

        if ($range === 'custom') {
            $from = $data['from'] ?? null;
            $to = $data['to'] ?? null;

            if ($from === null && $to === null) {
                return null;
            }

            return [
                $from !== null ? Carbon::parse($from)->startOfDay() : Carbon::create(1, 1, 1),
                $to !== null ? Carbon::parse($to)->endOfDay() : Carbon::create(9999, 12, 31),
            ];
        }

        $from = match ($range) {
            'this_week' => now()->startOfWeek(),
            'last_week' => now()->subWeek()->startOfWeek(),
            'this_month' => now()->startOfMonth(),
            'last_month' => now()->subMonthNoOverflow()->startOfMonth(),
            'this_year' => now()->startOfYear(),
            default => today(),
        };
        $to = match ($range) {
            'this_week' => now()->endOfWeek(),
            'last_week' => now()->subWeek()->endOfWeek(),
            'this_month' => now()->endOfMonth(),
            'last_month' => now()->subMonthNoOverflow()->endOfMonth(),
            'this_year' => now()->endOfYear(),
            default => today(),
        };

        return [$from->startOfDay(), $to->endOfDay()];
    }

    /**
     * @param  array<string, mixed>  $data
     */
    public static function apply(Builder $query, array $data, string $column = 'created_at'): Builder
    {
        $bounds = self::bounds($data);

        if ($bounds === null) {
            return $query;
        }

        [$from, $to] = $bounds;

        return $query
            ->when($from->year > 1, fn (Builder $q) => $q->where($column, '>=', $from))
            ->when($to->year < 9999, fn (Builder $q) => $q->where($column, '<=', $to));
    }

    /**
     * "Period" line used in PDF headers and receipt printouts so the exported
     * document names the window it actually covers.
     *
     * @param  array<string, mixed>  $data
     */
    public static function describe(array $data): string
    {
        $range = $data['range'] ?? 'today';

        if ($range === 'custom') {
            $from = $data['from'] ?? null;
            $to = $data['to'] ?? null;

            return match (true) {
                $from !== null && $to !== null => Carbon::parse($from)->toDateString().' – '.Carbon::parse($to)->toDateString(),
                $from !== null => 'From '.Carbon::parse($from)->toDateString(),
                $to !== null => 'Until '.Carbon::parse($to)->toDateString(),
                default => self::label('today'),
            };
        }

        return self::label($range);
    }
}
