<?php

namespace App\Support;

use App\Models\Category;
use App\Models\Expense;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;

/**
 * The filtering, sorting and summarising the list shares with print, CSV and PDF.
 *
 * The Vue app reads it through the JSON API, the server-rendered print/PDF views
 * read it directly — one source of truth for "what is on the sheet right now".
 */
class ExpenseReport
{
    /** Only these keys are ever treated as filters. */
    public const KEYS = ['q', 'category_id', 'payment_method', 'from', 'to'];

    public static function filters(Request $request): array
    {
        return [
            'q' => trim((string) $request->query('q')),
            'category_id' => $request->query('category_id'),
            'payment_method' => $request->query('payment_method'),
            'from' => $request->query('from'),
            'to' => $request->query('to'),
        ];
    }

    public static function isFiltered(array $filters): bool
    {
        return collect($filters)->filter(fn ($v) => $v !== null && $v !== '')->isNotEmpty();
    }

    public static function query(array $filters): Builder
    {
        return Expense::with('category')
            ->when($filters['q'] !== '', function ($q) use ($filters) {
                $term = '%'.$filters['q'].'%';
                $q->where(function ($w) use ($term) {
                    $w->where('description', 'like', $term)
                        ->orWhere('remark', 'like', $term)
                        ->orWhereHas('category', fn ($c) => $c->where('name', 'like', $term));
                });
            })
            ->when($filters['category_id'], fn ($q, $v) => $q->where('category_id', $v))
            ->when($filters['payment_method'], fn ($q, $v) => $q->where('payment_method', $v))
            ->when($filters['from'], fn ($q, $v) => $q->whereDate('expense_date', '>=', $v))
            ->when($filters['to'], fn ($q, $v) => $q->whereDate('expense_date', '<=', $v))
            ->orderByRaw('expense_date is null')
            ->orderBy('expense_date')
            ->orderBy('sort_order')
            ->orderBy('id');
    }

    /** The rows matching the request's filters. */
    public static function rows(Request $request): Collection
    {
        return static::query(static::filters($request))->get();
    }

    public static function summary(Collection $expenses): array
    {
        $byCategory = $expenses
            ->groupBy(fn ($e) => $e->categoryName())
            ->map(fn ($group) => (float) $group->sum('amount'))
            ->sortDesc();

        $byMethod = $expenses
            ->groupBy('payment_method')
            ->map(fn ($group) => (float) $group->sum('amount'))
            ->sortDesc();

        return [
            'total' => (float) $expenses->sum('amount'),
            'count' => $expenses->count(),
            'average' => $expenses->count() ? (float) $expenses->sum('amount') / $expenses->count() : 0.0,
            'largest' => (float) ($expenses->max('amount') ?? 0),
            'by_category' => $byCategory,
            'by_method' => $byMethod,
        ];
    }

    /** Human-readable description of the filters, for the report header. */
    public static function filterSummary(array $filters): ?string
    {
        $parts = [];

        if ($filters['q'] !== '') {
            $parts[] = 'খোঁজ "'.$filters['q'].'"';
        }

        if ($filters['category_id'] && $name = Category::find($filters['category_id'])?->name) {
            $parts[] = config('tracker.labels.category').': '.$name;
        }

        if ($filters['payment_method']) {
            $parts[] = config('tracker.labels.payment_method').': '.$filters['payment_method'];
        }

        if ($filters['from']) {
            $parts[] = 'তারিখ থেকে '.$filters['from'];
        }

        if ($filters['to']) {
            $parts[] = 'তারিখ পর্যন্ত '.$filters['to'];
        }

        return $parts ? implode(', ', $parts) : null;
    }
}
