<?php

namespace App\Services\Care;

use App\Models\Assessment;
use App\Models\CareUnit;
use App\Models\MarAdministration;
use App\Models\NursingNote;
use App\Models\StaffAssignment;
use App\Models\User;
use App\Models\Visit;
use App\Models\VisitVital;
use App\Models\WardHandover;
use Carbon\Carbon;
use Carbon\CarbonInterface;
use Illuminate\Support\Collection;

class NursePerformanceService
{
    /**
     * Aggregate nursing KPIs for a care unit over a date range.
     *
     * @return array<string, mixed>
     */
    public function unitReport(
        CareUnit $unit,
        string $ownerRef,
        ?CarbonInterface $from = null,
        ?CarbonInterface $to = null,
    ): array {
        $from = Carbon::parse($from ?? now()->subDays(7))->startOfDay();
        $to = Carbon::parse($to ?? now())->endOfDay();

        $visitIds = Visit::owned($ownerRef)
            ->where('care_unit_id', $unit->id)
            ->pluck('id');

        $mar = MarAdministration::owned($ownerRef)
            ->where('care_unit_id', $unit->id)
            ->whereBetween('recorded_at', [$from, $to])
            ->get();

        $marGiven = $mar->where('status', MarAdministration::STATUS_GIVEN)->count();
        $marHeld = $mar->where('status', MarAdministration::STATUS_HELD)->count();
        $marRefused = $mar->where('status', MarAdministration::STATUS_REFUSED)->count();
        $marMissed = $mar->where('status', MarAdministration::STATUS_MISSED)->count();
        $marTotal = $mar->count();
        $marOnTime = $mar->filter(function (MarAdministration $a) {
            if ($a->status !== MarAdministration::STATUS_GIVEN || ! $a->scheduled_for) {
                return false;
            }

            return $a->recorded_at->between(
                $a->scheduled_for->copy()->subMinutes(30),
                $a->scheduled_for->copy()->addMinutes(60),
            );
        })->count();

        $notes = NursingNote::owned($ownerRef)
            ->where('care_unit_id', $unit->id)
            ->whereBetween('recorded_at', [$from, $to])
            ->get();

        $vitals = VisitVital::owned($ownerRef)
            ->where('care_unit_id', $unit->id)
            ->whereBetween('recorded_at', [$from, $to])
            ->count();

        $nursingCodes = NursingAssessmentService::NURSING_PACK_CODES;
        $assessments = Assessment::owned($ownerRef)
            ->whereIn('visit_id', $visitIds->isEmpty() ? [0] : $visitIds->all())
            ->whereBetween('created_at', [$from, $to])
            ->whereHas('template', fn ($q) => $q->whereIn('code', $nursingCodes))
            ->with('template')
            ->get();

        $assessmentsCompleted = $assessments->where('status', Assessment::STATUS_COMPLETED)->count();
        $assessmentsDraft = $assessments->where('status', Assessment::STATUS_DRAFT)->count();

        $handovers = WardHandover::owned($ownerRef)
            ->where('care_unit_id', $unit->id)
            ->whereBetween('created_at', [$from, $to])
            ->get();

        $assignedStaff = StaffAssignment::owned($ownerRef)
            ->where('care_unit_id', $unit->id)
            ->where('status', 'active')
            ->unitPlacements()
            ->effectiveOn($to)
            ->with('member')
            ->get();

        $byActor = $this->byActor(
            $mar,
            $notes,
            $assessments->where('status', Assessment::STATUS_COMPLETED),
            $handovers->where('status', WardHandover::STATUS_COMPLETED),
        );

        return [
            'from' => $from,
            'to' => $to,
            'placed_patients' => Visit::owned($ownerRef)
                ->where('care_unit_id', $unit->id)
                ->whereIn('status', [Visit::STATUS_OPEN, Visit::STATUS_IN_PROGRESS])
                ->count(),
            'mar' => [
                'total' => $marTotal,
                'given' => $marGiven,
                'held' => $marHeld,
                'refused' => $marRefused,
                'missed' => $marMissed,
                'on_time_given' => $marOnTime,
                'on_time_rate' => $marGiven > 0 ? round(($marOnTime / $marGiven) * 100, 1) : null,
                'compliance_rate' => $marTotal > 0
                    ? round((($marGiven) / $marTotal) * 100, 1)
                    : null,
            ],
            'notes' => [
                'total' => $notes->count(),
                'by_type' => $notes->groupBy('note_type')->map->count()->all(),
            ],
            'vitals' => [
                'total' => $vitals,
            ],
            'assessments' => [
                'started' => $assessments->count(),
                'completed' => $assessmentsCompleted,
                'draft' => $assessmentsDraft,
                'completion_rate' => $assessments->count() > 0
                    ? round(($assessmentsCompleted / $assessments->count()) * 100, 1)
                    : null,
                'by_template' => $assessments
                    ->groupBy(fn (Assessment $a) => $a->template?->code ?? 'unknown')
                    ->map(fn (Collection $group, $code) => [
                        'code' => $code,
                        'name' => $group->first()->template?->name ?? $code,
                        'completed' => $group->where('status', Assessment::STATUS_COMPLETED)->count(),
                        'total' => $group->count(),
                    ])
                    ->values()
                    ->all(),
            ],
            'handovers' => [
                'total' => $handovers->count(),
                'completed' => $handovers->where('status', WardHandover::STATUS_COMPLETED)->count(),
            ],
            'assigned_staff' => $assignedStaff->count(),
            'by_actor' => $byActor,
        ];
    }

    /**
     * @param  Collection<int, MarAdministration>  $mar
     * @param  Collection<int, NursingNote>  $notes
     * @param  Collection<int, Assessment>  $assessments
     * @param  Collection<int, WardHandover>  $handovers
     * @return list<array<string, mixed>>
     */
    protected function byActor(
        Collection $mar,
        Collection $notes,
        Collection $assessments,
        Collection $handovers,
    ): array {
        $refs = collect()
            ->merge($mar->pluck('administered_by'))
            ->merge($notes->pluck('recorded_by'))
            ->merge($assessments->pluck('completed_by'))
            ->merge($handovers->pluck('handed_over_by'))
            ->filter()
            ->unique()
            ->values();

        $emails = User::query()->whereIn('public_id', $refs)->pluck('email', 'public_id');
        $names = User::query()->whereIn('public_id', $refs)->pluck('name', 'public_id');

        $rows = [];
        foreach ($refs as $ref) {
            $rows[] = [
                'actor_ref' => $ref,
                'label' => $names[$ref] ?? $emails[$ref] ?? $ref,
                'mar_given' => $mar->where('administered_by', $ref)->where('status', MarAdministration::STATUS_GIVEN)->count(),
                'mar_total' => $mar->where('administered_by', $ref)->count(),
                'notes' => $notes->where('recorded_by', $ref)->count(),
                'assessments_completed' => $assessments->where('completed_by', $ref)->count(),
                'handovers' => $handovers->where('handed_over_by', $ref)->count(),
            ];
        }

        usort($rows, fn ($a, $b) => ($b['mar_total'] + $b['notes'] + $b['assessments_completed'])
            <=> ($a['mar_total'] + $a['notes'] + $a['assessments_completed']));

        return $rows;
    }
}
