<?php

namespace Modules\ClinicCore\Entities\Services;

use Modules\ClinicCore\Entities\ClcPrescription;
use Modules\ClinicCore\Entities\ClcPrescriptionItem;

/**
 * SIFA P4-T3 — pharmacy dispensing orchestration around the existing
 * ClcMedicineDispenseService (which does the item-level clinical stamping +
 * controlled-register write, NEVER stock). This service adds the cross-item
 * concerns:
 *
 *   - onItemDispensed(): after a single item is dispensed, if every item on its
 *     prescription is now dispensed, fire MedNotify 'prescription.ready' to the
 *     patient contact (fail-soft) and stamp the Rx status 'completed' — but only
 *     when the Rx was 'active' (so we never resurrect a cancelled Rx).
 *   - reCheckSafety(): re-run the deterministic RxSafetyService at dispense time
 *     so allergy / duplicate-therapy warnings surface again in the pharmacy
 *     (non-blocking except a life-threatening allergy, mirroring Wave C).
 *
 * No ERP stock is touched here — see ClcMedicineDispenseService's docblock for
 * the no-double-deduction division (MedBilling's capture→bill sell decrements).
 */
class PharmacyDispenseService
{
    /**
     * React to a single item being dispensed. Returns
     * ['rx_completed' => bool, 'notified' => bool].
     */
    public function onItemDispensed(ClcPrescriptionItem $item): array
    {
        $out = ['rx_completed' => false, 'notified' => false];

        $rx = $item->prescription()->with('items')->first();
        if (!$rx) {
            return $out;
        }

        $allDispensed = $rx->items->every(fn ($i) => !empty($i->dispensed_at));
        if (!$allDispensed) {
            return $out;
        }

        // Full dispense — notify the patient contact the Rx is ready (fail-soft).
        $out['notified'] = $this->notifyReady($rx);

        // Stamp completed only when the Rx status matches the existing semantics:
        // the enum is (active, completed, cancelled) — completing an active Rx is
        // correct; a cancelled Rx is left untouched.
        if ($rx->status === 'active') {
            $rx->update(['status' => 'completed']);
            $out['rx_completed'] = true;
        }

        return $out;
    }

    /** Fire MedNotify 'prescription.ready' to the owning contact. Fail-soft. */
    public function notifyReady(ClcPrescription $rx): bool
    {
        try {
            $class = '\Modules\MedNotify\Services\MedNotify';
            if (!class_exists($class)) {
                return false;
            }
            if (empty($rx->contact_id)) {
                return false;
            }

            $patientName = optional($rx->pet)->patient_name ?: optional($rx->owner)->name ?: 'Patient';
            $business = \App\Business::find($rx->business_id);

            app($class)->notify('prescription.ready', [
                'business_id' => (int) $rx->business_id,
                'contacts'    => [(int) $rx->contact_id],
                'variables'   => [
                    'patient_name'  => $patientName,
                    'pharmacy_name' => $business->name ?? '',
                    'rx_no'         => $rx->ref_no ?: ('RX-' . $rx->id),
                    'link'          => route('clinic.prescriptions.show', $rx->id),
                ],
            ]);

            return true;
        } catch (\Throwable $e) {
            \Log::warning('PharmacyDispenseService prescription.ready notify skipped: ' . $e->getMessage());
            return false;
        }
    }

    /**
     * Re-run the deterministic safety screen for the catalogue-linked medicines
     * on a prescription at dispense time. Returns the RxSafetyService warnings[]
     * (empty on any failure). Non-blocking here — the pharmacy surfaces them.
     *
     * @return array<int, array<string, mixed>>
     */
    public function reCheckSafety(ClcPrescription $rx): array
    {
        try {
            $rx->loadMissing('items');
            $medicineIds = $rx->items
                ->pluck('medicine_id')
                ->map(fn ($v) => (int) $v)
                ->filter()
                ->unique()
                ->values()
                ->all();

            if (empty($medicineIds) || empty($rx->patient_id)) {
                return [];
            }

            return app(RxSafetyService::class)->check(
                (int) $rx->patient_id,
                $medicineIds,
                ['business_id' => (int) $rx->business_id, 'raise_alerts' => false]
            );
        } catch (\Throwable $e) {
            \Log::warning('PharmacyDispenseService reCheckSafety skipped: ' . $e->getMessage());
            return [];
        }
    }
}
