<?php

namespace App\Services\Care\BloodBank;

use App\Models\BloodBankStock;
use App\Models\Organization;
use App\Models\SpecialtyClinicalRecord;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;

class BloodBankInventoryService
{
    /**
     * Canonical operational products for branch-level stock (thin register, not bag ledger).
     *
     * @return array<string, array{label: string, reorder_level: int}>
     */
    public function catalogProducts(): array
    {
        return [
            'whole_blood' => ['label' => 'Whole blood', 'reorder_level' => 2],
            'packed_rbc' => ['label' => 'Packed RBC', 'reorder_level' => 2],
            'platelets' => ['label' => 'Platelets', 'reorder_level' => 2],
            'ffp' => ['label' => 'FFP', 'reorder_level' => 2],
            'cryo' => ['label' => 'Cryoprecipitate', 'reorder_level' => 1],
        ];
    }

    /**
     * Ensure catalog rows exist for the branch, then return them ordered.
     *
     * @return Collection<int, BloodBankStock>
     */
    public function ensureStockRows(
        Organization $organization,
        string $ownerRef,
        ?int $branchId,
    ): Collection {
        foreach ($this->catalogProducts() as $code => $meta) {
            BloodBankStock::query()->firstOrCreate(
                [
                    'organization_id' => $organization->id,
                    'branch_id' => $branchId,
                    'product_code' => $code,
                ],
                [
                    'owner_ref' => $ownerRef,
                    'product_label' => $meta['label'],
                    'units_on_hand' => 0,
                    'reorder_level' => $meta['reorder_level'],
                ],
            );
        }

        return BloodBankStock::owned($ownerRef)
            ->where('organization_id', $organization->id)
            ->when(
                $branchId === null,
                fn ($q) => $q->whereNull('branch_id'),
                fn ($q) => $q->where('branch_id', $branchId),
            )
            ->orderBy('product_label')
            ->get();
    }

    /**
     * @param  list<array{product_code: string, units_on_hand: int, reorder_level: int, notes?: ?string}>  $rows
     * @return Collection<int, BloodBankStock>
     */
    public function updateStock(
        Organization $organization,
        string $ownerRef,
        ?int $branchId,
        array $rows,
        ?string $actorRef = null,
    ): Collection {
        $allowed = array_keys($this->catalogProducts());

        return DB::transaction(function () use ($organization, $ownerRef, $branchId, $rows, $actorRef, $allowed) {
            $this->ensureStockRows($organization, $ownerRef, $branchId);

            foreach ($rows as $row) {
                $code = (string) ($row['product_code'] ?? '');
                if (! in_array($code, $allowed, true)) {
                    continue;
                }

                $stock = BloodBankStock::owned($ownerRef)
                    ->where('organization_id', $organization->id)
                    ->where('product_code', $code)
                    ->when(
                        $branchId === null,
                        fn ($q) => $q->whereNull('branch_id'),
                        fn ($q) => $q->where('branch_id', $branchId),
                    )
                    ->first();

                if (! $stock) {
                    continue;
                }

                $stock->update([
                    'units_on_hand' => max(0, (int) ($row['units_on_hand'] ?? 0)),
                    'reorder_level' => max(0, (int) ($row['reorder_level'] ?? $stock->reorder_level)),
                    'notes' => isset($row['notes']) ? (string) $row['notes'] : $stock->notes,
                    'updated_by' => $actorRef,
                ]);
            }

            return $this->ensureStockRows($organization, $ownerRef, $branchId);
        });
    }

    /**
     * @return array{
     *     stock: Collection<int, BloodBankStock>,
     *     low_stock: Collection<int, BloodBankStock>,
     *     low_stock_count: int,
     *     total_units: int,
     *     recent_notes: Collection<int, SpecialtyClinicalRecord>
     * }
     */
    public function adminOverview(
        Organization $organization,
        string $ownerRef,
        ?int $branchId,
    ): array {
        $stock = $this->ensureStockRows($organization, $ownerRef, $branchId);
        $lowStock = $stock->filter(fn (BloodBankStock $row) => $row->isLowStock())->values();

        $recentNotes = 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))
            ->with(['visit.patient'])
            ->orderByDesc('recorded_at')
            ->limit(8)
            ->get();

        return [
            'stock' => $stock,
            'low_stock' => $lowStock,
            'low_stock_count' => $lowStock->count(),
            'total_units' => (int) $stock->sum('units_on_hand'),
            'recent_notes' => $recentNotes,
        ];
    }
}
