<?php

namespace App\Services\Care\BloodBank;

use App\Models\Appointment;
use App\Models\Bill;
use App\Models\BillLineItem;
use App\Models\Organization;
use App\Models\SpecialtyClinicalRecord;
use App\Models\Visit;
use App\Services\Care\SpecialtyShellService;
use Carbon\Carbon;
use Illuminate\Support\Collection;

class BloodBankAnalyticsService
{
    public function __construct(
        protected SpecialtyShellService $shell,
    ) {}

    /**
     * @return array{
     *     requests_today: int,
     *     completed_today: int,
     *     high_urgency_open: int,
     *     urgency_mix: Collection,
     *     products_issued: Collection,
     *     low_stock_flags: int,
     *     revenue_by_service: Collection,
     *     from: string,
     *     to: string
     * }
     */
    public function report(
        Organization $organization,
        string $ownerRef,
        ?int $branchId = null,
        ?string $from = null,
        ?string $to = null,
    ): array {
        $todayStart = now()->startOfDay();
        $rangeFrom = $from ? Carbon::parse($from)->startOfDay() : now()->startOfMonth();
        $rangeTo = $to ? Carbon::parse($to)->endOfDay() : now()->endOfDay();

        $departmentIds = $this->shell->departmentIds($organization, 'blood_bank', $ownerRef, $branchId);

        $appointmentBase = Appointment::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->when($departmentIds !== [], fn ($q) => $q->whereIn('department_id', $departmentIds))
            ->when($departmentIds === [], fn ($q) => $q->whereRaw('1 = 0'))
            ->when($branchId, fn ($q) => $q->where('branch_id', $branchId));

        $visitIds = (clone $appointmentBase)->whereNotNull('visit_id')->pluck('visit_id');

        $requestsToday = SpecialtyClinicalRecord::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->where('module_key', 'blood_bank')
            ->where('record_type', 'blood_request')
            ->when($branchId, fn ($q) => $q->where('branch_id', $branchId))
            ->where('recorded_at', '>=', $todayStart)
            ->count();

        $completedToday = (clone $appointmentBase)
            ->where('status', Appointment::STATUS_COMPLETED)
            ->where('completed_at', '>=', $todayStart)
            ->count();

        $requestRecords = SpecialtyClinicalRecord::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->where('module_key', 'blood_bank')
            ->where('record_type', 'blood_request')
            ->when($branchId, fn ($q) => $q->where('branch_id', $branchId))
            ->whereBetween('recorded_at', [$rangeFrom, $rangeTo])
            ->get();

        $urgencyMix = $requestRecords
            ->groupBy(fn (SpecialtyClinicalRecord $r) => (string) ($r->payload['urgency'] ?? 'Unknown'))
            ->map(fn (Collection $rows, string $urgency) => [
                'urgency' => $urgency,
                'total' => $rows->count(),
            ])
            ->values()
            ->sortByDesc('total')
            ->values();

        $openVisitIds = Visit::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->whereIn('id', $visitIds->isEmpty() ? [0] : $visitIds)
            ->whereIn('status', [Visit::STATUS_OPEN, Visit::STATUS_IN_PROGRESS])
            ->pluck('id');

        $highUrgencyOpen = SpecialtyClinicalRecord::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->where('module_key', 'blood_bank')
            ->where('record_type', 'blood_request')
            ->whereIn('visit_id', $openVisitIds->isEmpty() ? [0] : $openVisitIds)
            ->get()
            ->filter(function (SpecialtyClinicalRecord $r) {
                $urgency = strtolower((string) ($r->payload['urgency'] ?? ''));

                return str_contains($urgency, 'urgent')
                    || str_contains($urgency, 'emergency')
                    || str_contains($urgency, 'massive');
            })
            ->count();

        $issueRecords = SpecialtyClinicalRecord::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->where('module_key', 'blood_bank')
            ->where('record_type', 'issue_note')
            ->when($branchId, fn ($q) => $q->where('branch_id', $branchId))
            ->whereBetween('recorded_at', [$rangeFrom, $rangeTo])
            ->get();

        $productsIssued = $issueRecords
            ->groupBy(fn (SpecialtyClinicalRecord $r) => (string) ($r->payload['product'] ?? 'Unknown'))
            ->map(fn (Collection $rows, string $product) => [
                'product' => $product,
                'total' => $rows->sum(fn (SpecialtyClinicalRecord $r) => (int) ($r->payload['units_issued'] ?? 0)),
                'issues' => $rows->count(),
            ])
            ->values()
            ->sortByDesc('total')
            ->values();

        // Fall back to request issued_units when no dedicated issue notes exist.
        if ($productsIssued->isEmpty()) {
            $productsIssued = $requestRecords
                ->filter(fn (SpecialtyClinicalRecord $r) => (int) ($r->payload['issued_units'] ?? 0) > 0)
                ->groupBy(fn (SpecialtyClinicalRecord $r) => (string) ($r->payload['product'] ?? 'Unknown'))
                ->map(fn (Collection $rows, string $product) => [
                    'product' => $product,
                    'total' => $rows->sum(fn (SpecialtyClinicalRecord $r) => (int) ($r->payload['issued_units'] ?? 0)),
                    'issues' => $rows->count(),
                ])
                ->values()
                ->sortByDesc('total')
                ->values();
        }

        $lowStockFlags = SpecialtyClinicalRecord::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->where('module_key', 'blood_bank')
            ->where('record_type', 'inventory_note')
            ->when($branchId, fn ($q) => $q->where('branch_id', $branchId))
            ->whereBetween('recorded_at', [$rangeFrom, $rangeTo])
            ->get()
            ->filter(function (SpecialtyClinicalRecord $r) {
                if (! empty($r->payload['low_stock_alert'])) {
                    return true;
                }
                foreach (['whole_blood_units', 'packed_rbc_units', 'platelet_units', 'ffp_units'] as $key) {
                    if (isset($r->payload[$key]) && (int) $r->payload[$key] <= 2) {
                        return true;
                    }
                }

                return false;
            })
            ->count();

        $serviceLabels = collect($this->shell->provisionedServices($organization, 'blood_bank'))
            ->pluck('label')
            ->filter()
            ->values()
            ->all();

        $revenueByService = BillLineItem::query()
            ->where('owner_ref', $ownerRef)
            ->where('source_type', 'specialty_service')
            ->whereIn('description', $serviceLabels ?: ['__none__'])
            ->whereBetween('created_at', [$rangeFrom, $rangeTo])
            ->whereHas('bill', function ($q) use ($organization, $visitIds) {
                $q->where('organization_id', $organization->id)
                    ->whereIn('visit_id', $visitIds->isEmpty() ? [0] : $visitIds)
                    ->whereNotIn('status', [Bill::STATUS_VOID]);
            })
            ->selectRaw('description, count(*) as total, sum(total_minor) as amount_minor')
            ->groupBy('description')
            ->orderByDesc('amount_minor')
            ->get();

        return [
            'requests_today' => $requestsToday,
            'completed_today' => $completedToday,
            'high_urgency_open' => $highUrgencyOpen,
            'urgency_mix' => $urgencyMix,
            'products_issued' => $productsIssued,
            'low_stock_flags' => $lowStockFlags,
            'revenue_by_service' => $revenueByService,
            'from' => $rangeFrom->toDateString(),
            'to' => $rangeTo->toDateString(),
        ];
    }
}
