<?php

namespace App\Domain\Billing;

use App\Enums\InvoiceStatus;
use App\Models\InvoiceItem;
use App\Models\Payment;
use App\Support\ClinicTime;
use Carbon\CarbonImmutable;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;

/**
 * Two lenses, stated explicitly in the UI:
 *  - COLLECTED: actual cash received (payments) — by day/month/method.
 *  - BILLED:    issued invoice items — by procedure/category (revenue
 *               attribution per procedure follows billing, since payments
 *               are invoice-level; noted in docs/M4-NOTES.md).
 */
class RevenueService
{
    public function collectedBetween(CarbonImmutable $from, CarbonImmutable $to): int
    {
        return (int) Payment::whereBetween('paid_at', [$from, $to])->sum('amount');
    }

    public function collectedByMethod(CarbonImmutable $from, CarbonImmutable $to): Collection
    {
        return Payment::whereBetween('paid_at', [$from, $to])
            ->select('method', DB::raw('SUM(amount) as total'), DB::raw('COUNT(*) as count'))
            ->groupBy('method')->orderByDesc('total')->get();
    }

    /** Daily collected series for the chart (clinic-local days). */
    public function collectedDaily(int $days = 30): Collection
    {
        $tz = ClinicTime::timezone();

        return collect(range($days - 1, 0))->map(function ($ago) use ($tz) {
            $day = CarbonImmutable::today($tz)->subDays($ago);

            return [
                'label' => $day->format('d M'),
                'total' => $this->collectedBetween($day->utc(), $day->addDay()->utc()),
            ];
        });
    }

    /** BILLED revenue grouped by procedure category. */
    public function billedByCategory(CarbonImmutable $from, CarbonImmutable $to): Collection
    {
        return $this->billedItems($from, $to)
            ->select(
                DB::raw("COALESCE(procedures.category, 'other') as category"),
                DB::raw('SUM(invoice_items.amount) as total'),
                DB::raw('COUNT(*) as count'),
            )
            ->groupBy('category')->orderByDesc('total')->get();
    }

    /** BILLED revenue per procedure → top performing services. */
    public function billedByProcedure(CarbonImmutable $from, CarbonImmutable $to, int $limit = 15): Collection
    {
        return $this->billedItems($from, $to)
            ->select(
                'invoice_items.procedure_id',
                DB::raw('MAX(procedures.name_en) as name_en'),
                DB::raw('MAX(procedures.name_ar) as name_ar'),
                DB::raw("COALESCE(MAX(procedures.category), 'other') as category"),
                DB::raw('SUM(invoice_items.amount) as total'),
                DB::raw('SUM(invoice_items.qty) as qty'),
            )
            ->whereNotNull('invoice_items.procedure_id')
            ->groupBy('invoice_items.procedure_id')
            ->orderByDesc('total')->limit($limit)->get();
    }

    protected function billedItems(CarbonImmutable $from, CarbonImmutable $to)
    {
        return InvoiceItem::query()
            ->join('invoices', 'invoices.id', '=', 'invoice_items.invoice_id')
            ->leftJoin('procedures', 'procedures.id', '=', 'invoice_items.procedure_id')
            ->whereBetween('invoices.issued_at', [$from, $to])
            ->whereIn('invoices.status', [
                InvoiceStatus::Issued->value,
                InvoiceStatus::PartiallyPaid->value,
                InvoiceStatus::Paid->value,
            ]);
    }
}
