<?php

namespace Modules\ClinicCore\Entities\Services;

use Modules\ClinicCore\Entities\ClcMedicalAlert;
use Modules\ClinicCore\Entities\ClcPatient;
use Modules\ClinicCore\Entities\ClcPatientAllergy;

/**
 * SIFA P3-T8 — medical alerts registry service.
 *
 * Central place to raise / resolve / query patient-safety alerts. Every method
 * is business_id-scoped. When a critical alert is raised and MedNotify is
 * installed, it also fans out through the shared notifications service
 * (fail-soft if absent).
 */
class MedicalAlertService
{
    /**
     * Raise (or refresh) an alert. When source_type/source_id are supplied an
     * existing active alert for the same source is updated rather than a
     * duplicate created — so repeated syncs are idempotent.
     *
     * @param array $data business_id, patient_id, alert_type, severity, title,
     *                     details?, source_type?, source_id?, created_by?
     */
    public function raise(array $data): ClcMedicalAlert
    {
        $attrs = [
            'business_id' => $data['business_id'],
            'patient_id'  => $data['patient_id'],
            'alert_type'  => $data['alert_type'] ?? 'other',
            'severity'    => $data['severity'] ?? 'warning',
            'title'       => $data['title'] ?? '',
            'details'     => $data['details'] ?? null,
            'source_type' => $data['source_type'] ?? null,
            'source_id'   => $data['source_id'] ?? null,
            'is_active'   => true,
            'created_by'  => $data['created_by'] ?? auth()->id(),
        ];

        $existing = null;
        if (!empty($attrs['source_type']) && !empty($attrs['source_id'])) {
            $existing = ClcMedicalAlert::where('business_id', $attrs['business_id'])
                ->where('patient_id', $attrs['patient_id'])
                ->where('alert_type', $attrs['alert_type'])
                ->where('source_type', $attrs['source_type'])
                ->where('source_id', $attrs['source_id'])
                ->first();
        }

        if ($existing) {
            $existing->fill([
                'severity'  => $attrs['severity'],
                'title'     => $attrs['title'],
                'details'   => $attrs['details'],
                'is_active' => true,
                // Re-raising clears a prior acknowledgement so the banner shows again.
                'acknowledged_by' => null,
                'acknowledged_at' => null,
            ])->save();
            $alert = $existing;
        } else {
            $alert = ClcMedicalAlert::create($attrs);
        }

        if ($alert->severity === 'critical') {
            $this->fanOutCritical($alert);
        }

        return $alert;
    }

    /** Resolve (deactivate) an alert by id, business-scoped. */
    public function resolve(int $alertId, int $businessId): void
    {
        ClcMedicalAlert::where('business_id', $businessId)
            ->where('id', $alertId)
            ->update(['is_active' => false]);
    }

    /**
     * Resolve any active alert tied to a given source (used when the source
     * row is deleted or downgraded so the allergy no longer warrants an alert).
     */
    public function resolveBySource(int $businessId, int $patientId, string $sourceType, int $sourceId): void
    {
        ClcMedicalAlert::where('business_id', $businessId)
            ->where('patient_id', $patientId)
            ->where('source_type', $sourceType)
            ->where('source_id', $sourceId)
            ->update(['is_active' => false]);
    }

    /** Acknowledge an alert (keeps it on record, hides the "unacknowledged" state). */
    public function acknowledge(int $alertId, int $businessId, ?int $userId = null): void
    {
        ClcMedicalAlert::where('business_id', $businessId)
            ->where('id', $alertId)
            ->update([
                'acknowledged_by' => $userId ?? auth()->id(),
                'acknowledged_at' => now(),
            ]);
    }

    /** Active alerts for a patient, critical first. */
    public function activeFor(int $patientId, ?int $businessId = null)
    {
        $q = ClcMedicalAlert::where('patient_id', $patientId)->where('is_active', true);
        if ($businessId) {
            $q->where('business_id', $businessId);
        }
        return $q->orderByRaw("FIELD(severity,'critical','warning','info')")
            ->orderByDesc('created_at')
            ->get();
    }

    /**
     * Auto-sync: keep exactly one allergy alert in step with an allergy row.
     * A severe/life_threatening ACTIVE allergy raises (or refreshes) an allergy
     * alert; anything else resolves the alert for that source. Called from the
     * allergy CRUD (store/update/destroy).
     */
    public function syncAllergyAlert(ClcPatientAllergy $allergy): void
    {
        $businessId = (int) $allergy->business_id;
        $source     = 'clc_patient_allergies';

        if ($allergy->isAlertWorthy()) {
            $this->raise([
                'business_id' => $businessId,
                'patient_id'  => (int) $allergy->patient_id,
                'alert_type'  => 'allergy',
                'severity'    => $allergy->severity === 'life_threatening' ? 'critical' : 'warning',
                'title'       => trim(__('clinicCore::lang.allergy') . ': ' . $allergy->allergen),
                'details'     => $allergy->reaction ?: null,
                'source_type' => $source,
                'source_id'   => (int) $allergy->id,
            ]);
        } else {
            $this->resolveBySource($businessId, (int) $allergy->patient_id, $source, (int) $allergy->id);
        }
    }

    /**
     * Fan a critical alert out through MedNotify when that module is installed.
     * Fail-soft: any absence/error is logged and swallowed.
     */
    protected function fanOutCritical(ClcMedicalAlert $alert): void
    {
        try {
            $mu = app(\App\Utils\ModuleUtil::class);
            if (!$mu->isModuleInstalled('MedNotify')) {
                return;
            }

            $patient = ClcPatient::find($alert->patient_id);
            $contactId = $patient ? $patient->contact_id : null;

            app(\Modules\MedNotify\Services\MedNotify::class)->notify('patient.critical_alert', [
                'business_id' => (int) $alert->business_id,
                'contacts'    => $contactId ? [$contactId] : [],
                'variables'   => [
                    'patient_name' => $patient ? $patient->patient_name : ('#' . $alert->patient_id),
                    'alert_title'  => $alert->title,
                    'alert_type'   => $alert->alert_type,
                    'link'         => $patient ? route('clinic.patients.show', $patient->id) : '',
                ],
            ]);
        } catch (\Throwable $e) {
            \Log::warning('ClinicCore MedicalAlertService MedNotify fan-out skipped: ' . $e->getMessage());
        }
    }
}
