<?php

namespace App\Services\Care;

use App\Models\CareUnit;
use App\Models\Member;
use App\Models\Organization;
use App\Models\RosterEntry;
use App\Models\StaffAssignment;
use App\Models\Visit;
use Illuminate\Support\Collection;

/**
 * Bedside nurse workplace: units from today's duty + home placements, with placed patients.
 */
class NurseStationService
{
    /**
     * @return array{
     *     units: Collection<int, CareUnit>,
     *     patients_by_unit: array<int, Collection<int, Visit>>,
     *     duty_by_unit: array<int, Collection<int, RosterEntry>>,
     *     placement_by_unit: array<int, StaffAssignment>,
     *     selected_unit: ?CareUnit,
     * }
     */
    public function board(
        Organization $organization,
        Member $member,
        string $ownerRef,
        ?int $unitId = null,
    ): array {
        $dutyToday = RosterEntry::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->where('member_id', $member->id)
            ->whereDate('duty_date', now()->toDateString())
            ->where('status', '!=', RosterEntry::STATUS_CANCELLED)
            ->with(['shift', 'careUnit.department.branch'])
            ->orderBy('shift_id')
            ->get();

        $placements = StaffAssignment::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->where('member_id', $member->id)
            ->where('status', 'active')
            ->unitPlacements()
            ->effectiveOn(now())
            ->whereNotNull('care_unit_id')
            ->with(['careUnit.department.branch'])
            ->get();

        $unitIds = $dutyToday->pluck('care_unit_id')
            ->merge($placements->pluck('care_unit_id'))
            ->filter()
            ->map(fn ($id) => (int) $id)
            ->unique()
            ->values();

        $units = CareUnit::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->whereIn('id', $unitIds->all() ?: [0])
            ->where('is_active', true)
            ->with('department.branch')
            ->orderBy('name')
            ->get()
            ->keyBy('id');

        // Preserve duty-first ordering, then remaining placements.
        $ordered = collect();
        foreach ($dutyToday as $entry) {
            $id = (int) $entry->care_unit_id;
            if ($units->has($id) && ! $ordered->has($id)) {
                $ordered->put($id, $units->get($id));
            }
        }
        foreach ($placements as $assignment) {
            $id = (int) $assignment->care_unit_id;
            if ($units->has($id) && ! $ordered->has($id)) {
                $ordered->put($id, $units->get($id));
            }
        }

        $selected = null;
        if ($unitId && $ordered->has($unitId)) {
            $selected = $ordered->get($unitId);
        } elseif ($ordered->isNotEmpty()) {
            $selected = $ordered->first();
        }

        $patientsByUnit = [];
        foreach ($ordered as $id => $unit) {
            $patientsByUnit[$id] = Visit::owned($ownerRef)
                ->where('organization_id', $organization->id)
                ->where('care_unit_id', $id)
                ->whereIn('status', [Visit::STATUS_OPEN, Visit::STATUS_IN_PROGRESS])
                ->with(['patient', 'bed'])
                ->orderBy('placed_at')
                ->get();
        }

        $dutyByUnit = [];
        foreach ($dutyToday as $entry) {
            $id = (int) $entry->care_unit_id;
            $dutyByUnit[$id] ??= collect();
            $dutyByUnit[$id]->push($entry);
        }

        $placementByUnit = [];
        foreach ($placements as $assignment) {
            $placementByUnit[(int) $assignment->care_unit_id] = $assignment;
        }

        return [
            'units' => $ordered->values(),
            'patients_by_unit' => $patientsByUnit,
            'duty_by_unit' => $dutyByUnit,
            'placement_by_unit' => $placementByUnit,
            'selected_unit' => $selected,
        ];
    }
}
