<?php

namespace Modules\ClinicCore\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Modules\ClinicCore\Entities\ClcDoctor;
use Modules\ClinicCore\Entities\ClcMedicine;
use Modules\ClinicCore\Entities\ClcPrescription;
use Modules\ClinicCore\Entities\ClcPrescriptionItem;
use Modules\ClinicCore\Entities\Services\ClcMedicineDispenseService;
use Modules\ClinicCore\Entities\Services\ClinicCapabilities;
use Modules\ClinicCore\Entities\Services\PharmacyDispenseService;
use Modules\ClinicCore\Entities\Services\RxSafetyService;
use Yajra\DataTables\Facades\DataTables;

/**
 * SIFA P4-T3 — pharmacist dispense queue + per-item dispensing.
 *
 * The queue lists ACTIVE prescriptions that still have undispensed items
 * (dispensed_at null), filterable by date and doctor. Each item can be dispensed
 * (permission clinic.rx.dispense) with FEFO batch guidance, a controlled-drug
 * witness, and a re-run of the deterministic RxSafetyService.
 *
 * Dispensing is a CLINICAL act only — no ERP stock is decremented here (that
 * rides MedBilling's capture→bill sell). See ClcMedicineDispenseService.
 */
class ClcPharmacyController extends Controller
{
    use \Modules\ClinicCore\Http\Controllers\Traits\LogsClcActivity;

    private function biz(): int
    {
        return (int) request()->session()->get('user.business_id');
    }

    private function guard(): void
    {
        if (!auth()->user()->can('clinic.rx.dispense') && !auth()->user()->can('clinic.manage_prescriptions')) {
            abort(403, 'Unauthorized action.');
        }
    }

    /** The dispense-queue screen. */
    public function index()
    {
        $this->guard();

        $businessId = $this->biz();
        $doctors = ClcDoctor::where('business_id', $businessId)->active()->orderBy('name')->get();

        return view('clinicCore::pharmacy.index', compact('doctors'));
    }

    /**
     * DataTable feed: active prescriptions with at least one undispensed item,
     * filterable by date range + doctor. Row-detail (items) is loaded via
     * queueItems() on expand.
     */
    public function list(Request $request)
    {
        $this->guard();

        $businessId = $this->biz();

        $query = ClcPrescription::with(['pet:id,patient_name,species', 'owner:id,name', 'doctor:id,name'])
            ->where('business_id', $businessId)
            ->where('status', 'active')
            ->whereHas('items', fn ($q) => $q->whereNull('dispensed_at'))
            ->withCount([
                'items as items_total',
                'items as items_dispensed' => fn ($q) => $q->whereNotNull('dispensed_at'),
            ]);

        if ($request->filled('filter_doctor')) {
            $query->where('doctor_id', $request->filter_doctor);
        }
        $dateFormat = session('business.date_format', 'd M Y');
        foreach (['filter_date_from' => '>=', 'filter_date_to' => '<='] as $field => $op) {
            if ($request->filled($field)) {
                try {
                    $d = \Carbon\Carbon::createFromFormat($dateFormat, $request->{$field})->toDateString();
                    $query->whereDate('prescribed_date', $op, $d);
                } catch (\Exception $e) {}
            }
        }

        return DataTables::of($query)
            ->addColumn('ref_no_col', fn ($row) => '<a href="' . route('clinic.prescriptions.show', $row->id) . '" style="font-weight:700;font-family:monospace;color:#475569;">' . e($row->ref_no) . '</a>')
            ->addColumn('pet_name', fn ($row) => $row->pet ? e($row->pet->patient_name) : '—')
            ->addColumn('owner_name', fn ($row) => $row->owner ? e($row->owner->name) : '—')
            ->addColumn('doctor_name', fn ($row) => optional($row->doctor)->name ?? '—')
            ->addColumn('prescribed_date_col', fn ($row) => $row->prescribed_date
                ? \Carbon\Carbon::parse($row->prescribed_date)->format($dateFormat) : '—')
            ->addColumn('progress_col', function ($row) {
                $done = (int) $row->items_dispensed;
                $tot  = (int) $row->items_total;
                return '<span class="label label-' . ($done > 0 ? 'warning' : 'default') . '">' . $done . ' / ' . $tot . '</span>';
            })
            ->addColumn('action', fn ($row) => '<button class="btn btn-xs btn-primary btn-open-rx" data-id="' . $row->id . '"><i class="fa fa-pills"></i> ' . e(__('clinicCore::lang.dispense')) . '</button>')
            ->rawColumns(['ref_no_col', 'progress_col', 'action'])
            ->make(true);
    }

    /**
     * JSON: the items for one prescription (row expand / dispense panel), with a
     * re-run of the deterministic safety screen (non-blocking display).
     */
    public function queueItems(Request $request, $prescriptionId)
    {
        $this->guard();

        $businessId = $this->biz();
        $rx = ClcPrescription::with(['items.medicine', 'pet:id,patient_name', 'doctor:id,name'])
            ->where('business_id', $businessId)->findOrFail($prescriptionId);

        $items = $rx->items->map(function ($it) {
            $med = $it->medicine;
            return [
                'id'            => $it->id,
                'medicine_name' => $it->medicine_name,
                'dosage'        => $it->dosage,
                'frequency'     => $it->frequency,
                'quantity'      => $it->quantity,
                'prn'           => (bool) $it->prn,
                'prn_reason'    => $it->prn_reason,
                'controlled'    => $med ? (bool) $med->is_controlled : false,
                'schedule_label' => $med ? $med->schedule_label : null,
                'medicine_id'   => $it->medicine_id,
                'dispensed_at'  => $it->dispensed_at ? $it->dispensed_at->format('d M Y H:i') : null,
                'dispensed_qty' => $it->dispensed_qty,
            ];
        })->values();

        $warnings = app(PharmacyDispenseService::class)->reCheckSafety($rx);

        return response()->json([
            'ref_no'   => $rx->ref_no,
            'patient'  => optional($rx->pet)->patient_name,
            'doctor'   => optional($rx->doctor)->name,
            'items'    => $items,
            'warnings' => $warnings,
        ]);
    }

    /**
     * JSON: FEFO batch guidance for one item + the witness user list (for
     * controlled medicines). Display-only — nothing is written.
     */
    public function dispenseOptions(Request $request, $itemId)
    {
        $this->guard();

        $businessId = $this->biz();
        $item = ClcPrescriptionItem::with('prescription')->findOrFail($itemId);
        if (!$item->prescription || $item->prescription->business_id != $businessId) {
            abort(404);
        }

        $medicine = $item->medicine_id ? ClcMedicine::find($item->medicine_id) : null;
        $isControlled = $medicine && $medicine->is_controlled;

        // Controlled interlock: only clinics enabled for controlled substances.
        if ($isControlled && !ClinicCapabilities::businessHas($businessId, 'controlled_drugs')) {
            return response()->json([
                'linked'  => false,
                'msg'     => __('clinicCore::lang.dispense_controlled_disabled', ['medicine' => $item->medicine_name]),
                'batches' => [],
            ]);
        }

        $batches = collect();
        $linked = false;
        if ($medicine && !empty($medicine->product_id)) {
            $linked = true;
            $batches = app(ClcMedicineDispenseService::class)->fefoSuggestions($medicine);
        }

        // Witness candidates for controlled dispensing (all business users except self).
        $witnesses = [];
        if ($isControlled) {
            $witnesses = \App\User::where('business_id', $businessId)
                ->where('id', '!=', auth()->id())
                ->orderBy('first_name')
                ->get(['id', 'surname', 'first_name', 'last_name'])
                ->map(fn ($u) => ['id' => $u->id, 'name' => trim($u->surname . ' ' . $u->first_name . ' ' . $u->last_name)])
                ->values();
        }

        return response()->json([
            'linked'          => $linked,
            'controlled'      => $isControlled,
            'schedule_label'  => $medicine ? $medicine->schedule_label : null,
            'unit'            => $medicine ? $medicine->unit : null,
            'prescribed_qty'  => $item->quantity,
            'batches'         => $batches->values(),
            'witnesses'       => $witnesses,
            'already'         => !empty($item->dispensed_at),
        ]);
    }

    /**
     * Dispense one item. Stamps clinical dispense fields + (for controlled)
     * writes a witnessed register row. NO ERP stock is decremented here.
     * On full dispense of the Rx, fires prescription.ready + completes the Rx.
     */
    public function dispenseItem(Request $request, $itemId)
    {
        if (!auth()->user()->can('clinic.rx.dispense')) {
            return response()->json(['success' => false, 'msg' => 'Unauthorized'], 403);
        }

        $request->validate([
            'purchase_line_id' => 'nullable|integer',
            'qty'              => 'required|numeric|gt:0',
            'location_id'      => 'required|integer',
            'witness_user_id'  => 'nullable|integer',
            'safety_confirmed' => 'nullable|boolean',
        ]);

        $businessId = $this->biz();
        $item = ClcPrescriptionItem::with('prescription')->findOrFail($itemId);
        if (!$item->prescription || $item->prescription->business_id != $businessId) {
            abort(404);
        }
        if (!empty($item->dispensed_at)) {
            return response()->json(['success' => false, 'msg' => __('clinicCore::lang.dispense_already_done')], 422);
        }

        $medicine = $item->medicine_id ? ClcMedicine::find($item->medicine_id) : null;
        $isControlled = $medicine && $medicine->is_controlled;

        if ($isControlled && !ClinicCapabilities::businessHas($businessId, 'controlled_drugs')) {
            return response()->json([
                'success' => false,
                'msg'     => __('clinicCore::lang.dispense_controlled_disabled', ['medicine' => $item->medicine_name]),
            ], 422);
        }

        // Dispense-time safety re-check: a life-threatening allergy blocks unless
        // explicitly confirmed (mirrors Wave C create-flow semantics). Only the
        // catalogue-linked item's medicine is screened here.
        if ($item->medicine_id && !$request->boolean('safety_confirmed')) {
            try {
                $warnings = app(RxSafetyService::class)->check(
                    (int) $item->prescription->patient_id,
                    [(int) $item->medicine_id],
                    ['business_id' => $businessId, 'raise_alerts' => false]
                );
                if (RxSafetyService::hasBlocking($warnings)) {
                    return response()->json([
                        'success'  => false,
                        'blocking' => true,
                        'warnings' => $warnings,
                        'msg'      => __('clinicCore::lang.dispense_safety_block'),
                    ], 422);
                }
            } catch (\Throwable $e) {
                \Log::warning('ClcPharmacy dispense safety re-check skipped: ' . $e->getMessage());
            }
        }

        try {
            $updated = app(ClcMedicineDispenseService::class)->dispense(
                $item,
                $request->filled('purchase_line_id') ? (int) $request->purchase_line_id : null,
                (float) $request->qty,
                (int) $request->location_id,
                auth()->id(),
                ['witness_user_id' => $request->filled('witness_user_id') ? (int) $request->witness_user_id : null]
            );
        } catch (\InvalidArgumentException $e) {
            return response()->json(['success' => false, 'msg' => $e->getMessage()], 422);
        }

        $this->drmLog('update', $updated, [
            'action'          => 'dispensed',
            'dispensed_qty'   => $updated->dispensed_qty,
            'controlled'      => $isControlled,
            'witness_user_id' => $request->witness_user_id,
        ]);

        // Cross-item: complete the Rx + notify when everything is dispensed.
        $completion = app(PharmacyDispenseService::class)->onItemDispensed($updated);

        return response()->json([
            'success'      => true,
            'msg'          => __('clinicCore::lang.dispense_recorded'),
            'dispensed_at' => optional($updated->dispensed_at)->format('d M Y H:i'),
            'rx_completed' => $completion['rx_completed'],
            'notified'     => $completion['notified'],
        ]);
    }
}
