<?php

namespace App\Http\Controllers\Care;

use App\Http\Controllers\Controller;
use App\Http\Controllers\Care\Concerns\ScopesToAccount;
use App\Models\Consultation;
use App\Models\Prescription;
use App\Models\Practitioner;
use App\Services\Care\BranchContext;
use App\Services\Care\CareQueueBridge;
use App\Services\Care\CareQueueContexts;
use App\Services\Care\ConsultationReturnContext;
use App\Services\Care\OrganizationResolver;
use App\Services\Care\PharmacyService;
use App\Services\Care\PrescriptionService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;

class PrescriptionController extends Controller
{
    use ScopesToAccount;

    public function __construct(
        protected PrescriptionService $prescriptions,
        protected PharmacyService $pharmacy,
        protected CareQueueBridge $queueBridge,
        protected BranchContext $branchContext,
    ) {}

    public function index(Request $request): View
    {
        $this->authorizeAbility($request, 'prescriptions.view');
        $organization = $this->organization($request);
        $member = $this->member($request);
        $branchId = $this->branchContext->resolve($request, $organization, $member);

        $prescriptions = $this->prescriptions->list(
            $this->ownerRef($request),
            $organization->id,
            $request->only(['status', 'patient_id']),
            $branchId > 0 ? $branchId : null,
        );

        return view('care.prescriptions.index', [
            'organization' => $organization,
            'prescriptions' => $prescriptions,
            'statuses' => config('care.prescription_statuses'),
        ]);
    }

    public function queue(Request $request): View
    {
        $this->authorizeAbility($request, 'prescriptions.view');
        $organization = $this->organization($request);
        $member = $this->member($request);
        $branches = $this->branchContext->branches($request, $organization, $member);
        $branchId = $this->branchContext->resolve($request, $organization, $member);

        $queue = $this->prescriptions->pharmacyQueue($this->ownerRef($request), $organization->id);
        if ($branchId > 0) {
            $queue = $queue->filter(fn (Prescription $rx) => (int) ($rx->visit?->branch_id) === $branchId)->values();
        }

        $queueIntegration = ['enabled' => false];
        if ($branchId && $this->queueBridge->isEnabled($organization)) {
            // Issue tickets in Care FIFO order so Queue Call Next matches this list.
            $this->queueBridge->syncMissingTickets($organization, CareQueueContexts::PHARMACY, $branchId, 50);
            $queueIntegration = $this->queueBridge->listMeta($organization, CareQueueContexts::PHARMACY, $branchId);
        }

        $memberPoint = $branchId
            ? app(\App\Services\Care\CareQueueProvisioner::class)
                ->pointForMember($organization, CareQueueContexts::PHARMACY, $branchId, $member)
            : null;
        $memberCounterUuid = $memberPoint['counter_uuid'] ?? null;

        if (! empty($queueIntegration['enabled'])) {
            $queue = $queue->sortBy(function (Prescription $rx) use ($memberCounterUuid) {
                $status = (string) ($rx->queue_ticket_status ?? '');
                $mine = $memberCounterUuid
                    && (string) ($rx->queue_service_point_uuid ?? '') === (string) $memberCounterUuid;
                $rank = match (true) {
                    $mine && $status === 'serving' => 0,
                    $mine && $status === 'called' => 1,
                    $status === 'waiting' || $status === '' => 2,
                    default => 3,
                };

                return sprintf('%d-%s', $rank, $rx->created_at?->format('Y-m-d H:i:s') ?? '');
            })->values();
        }

        $drugs = \App\Models\Drug::owned($this->ownerRef($request))
            ->where('organization_id', $organization->id)
            ->where('is_active', true)
            ->with(['batches' => fn ($q) => $q->where('quantity_on_hand', '>', 0)->orderBy('expiry_date')])
            ->orderBy('name')
            ->get();

        $canDispense = app(\App\Services\Care\CarePermissions::class)
            ->can($member, 'prescriptions.dispense');

        return view('care.prescriptions.queue', [
            'organization' => $organization,
            'queue' => $queue,
            'drugs' => $drugs,
            'canDispense' => $canDispense,
            'branches' => $branches,
            'branchId' => $branchId,
            'canSwitchBranch' => $this->branchContext->canSwitch($member),
            'queueIntegration' => $queueIntegration,
            'memberCounterUuid' => $memberCounterUuid,
        ]);
    }

    public function callNext(Request $request): RedirectResponse
    {
        $this->authorizeAbility($request, 'prescriptions.dispense');
        $organization = $this->organization($request);
        $member = $this->member($request);
        $branchId = $this->branchContext->resolve($request, $organization, $member);
        abort_unless($branchId > 0, 422);
        abort_unless($this->queueBridge->isEnabled($organization), 404);

        $result = $this->queueBridge->callNext($organization, CareQueueContexts::PHARMACY, $branchId, $member);
        $ticket = $result['ticket'] ?? null;
        if (! $ticket) {
            // Distinguish empty queue vs mid-dispense block via a follow-up check is hard over HTTP;
            // prefer a clear action message for pharmacists.
            return back()->with('info', 'No waiting patients — or finish the patient you are already serving before calling next.');
        }

        return back()->with('success', 'Called '.$ticket['ticket_number'].'.');
    }

    public function serve(Request $request, Prescription $prescription): RedirectResponse
    {
        $this->authorizeAbility($request, 'prescriptions.dispense');
        $this->authorizePrescription($request, $prescription);
        $organization = $this->organization($request);
        abort_unless($this->queueBridge->isEnabled($organization), 404);

        $ticket = $this->queueBridge->serve($organization, $prescription);
        if (! $ticket) {
            return back()->with('error', 'Could not serve pharmacy ticket.');
        }

        return back()->with('success', 'Now serving '.$ticket['ticket_number'].'.');
    }

    public function create(Request $request, Consultation $consultation): View
    {
        $this->authorizeAbility($request, 'prescriptions.manage');
        $this->authorizeConsultation($request, $consultation);

        $consultation->load(['patient', 'practitioner']);

        app(ConsultationReturnContext::class)->remember($consultation);

        $practitioners = Practitioner::owned($this->ownerRef($request))
            ->where('organization_id', $this->organization($request)->id)
            ->where('is_active', true)
            ->orderBy('name')
            ->get();

        return view('care.prescriptions.create', [
            'consultation' => $consultation,
            'practitioners' => $practitioners,
            'routes' => config('care.medication_routes'),
        ]);
    }

    public function store(Request $request, Consultation $consultation): RedirectResponse
    {
        $this->authorizeAbility($request, 'prescriptions.manage');
        $this->authorizeConsultation($request, $consultation);

        $prescription = $this->prescriptions->createFromConsultation(
            $consultation,
            $this->ownerRef($request),
            $this->validatedPrescriptionData($request),
            $this->ownerRef($request),
        );

        $return = app(ConsultationReturnContext::class);
        $return->remember($consultation);

        if ($request->input('return_to') === 'consultation') {
            return redirect()
                ->route('care.consultations.show', $consultation)
                ->with('success', 'Prescription created.');
        }

        if ($request->input('return_to') === 'specialty') {
            $module = (string) $request->input('specialty_module', '');
            $visitId = (int) $request->input('specialty_visit_id', 0);
            $modules = app(\App\Services\Care\SpecialtyModuleService::class);
            $visit = $consultation->visit_id
                ? \App\Models\Visit::query()->find($consultation->visit_id)
                : null;

            if (
                $module !== ''
                && $modules->definition($module)
                && $visit
                && (int) $visit->id === $visitId
                && (int) $visit->id === (int) $consultation->visit_id
            ) {
                return redirect()
                    ->route('care.specialty.workspace', [
                        'module' => $module,
                        'visit' => $visit,
                    ])
                    ->with('success', 'Prescription created.');
            }
        }

        return redirect()->route('care.prescriptions.show', array_filter([
            'prescription' => $prescription,
            ...$return->query($consultation),
        ]))->with('success', 'Prescription created.');
    }

    public function show(Request $request, Prescription $prescription): View
    {
        $this->authorizeAbility($request, 'prescriptions.view');
        $this->authorizePrescription($request, $prescription);

        $prescription->load(['patient', 'practitioner', 'items', 'consultation', 'visit']);

        $canManage = app(\App\Services\Care\CarePermissions::class)
            ->can($this->member($request), 'prescriptions.manage');
        $canDispense = app(\App\Services\Care\CarePermissions::class)
            ->can($this->member($request), 'prescriptions.dispense');

        $return = app(ConsultationReturnContext::class);

        return view('care.prescriptions.show', [
            'prescription' => $prescription,
            'statuses' => config('care.prescription_statuses'),
            'routes' => config('care.medication_routes'),
            'canManage' => $canManage,
            'canDispense' => $canDispense,
            'returnConsultation' => $return->resolve($request, (int) $prescription->patient_id)
                ?? $return->resolveLinked($request, $prescription->consultation),
        ]);
    }

    public function activate(Request $request, Prescription $prescription): RedirectResponse
    {
        $this->authorizeAbility($request, 'prescriptions.manage');
        $this->authorizePrescription($request, $prescription);

        $this->prescriptions->activate($prescription, $this->ownerRef($request), $this->ownerRef($request));

        return back()->with('success', 'Prescription activated.');
    }

    public function dispense(Request $request, Prescription $prescription): RedirectResponse
    {
        $this->authorizeAbility($request, 'prescriptions.dispense');
        $this->authorizePrescription($request, $prescription);

        $allocations = $request->input('allocations', []);

        if (! empty($allocations)) {
            $this->pharmacy->dispensePrescription(
                $prescription,
                $this->ownerRef($request),
                $allocations,
                $this->ownerRef($request),
            );
        } else {
            $this->prescriptions->dispense($prescription, $this->ownerRef($request), $this->ownerRef($request));
        }

        return redirect()->route('care.prescriptions.queue')
            ->with('success', 'Prescription dispensed.');
    }

    public function cancel(Request $request, Prescription $prescription): RedirectResponse
    {
        $this->authorizeAbility($request, 'prescriptions.manage');
        $this->authorizePrescription($request, $prescription);

        $this->prescriptions->cancel($prescription, $this->ownerRef($request), $this->ownerRef($request));

        return back()->with('success', 'Prescription cancelled.');
    }

    protected function authorizeConsultation(Request $request, Consultation $consultation): void
    {
        $this->authorizeOwner($request, $consultation);
        $consultation->loadMissing('visit');
        abort_unless($consultation->visit->organization_id === $this->organization($request)->id, 404);
    }

    protected function authorizePrescription(Request $request, Prescription $prescription): void
    {
        $this->authorizeOwner($request, $prescription);
        abort_unless($prescription->organization_id === $this->organization($request)->id, 404);

        $branchId = app(OrganizationResolver::class)->branchScope($this->member($request));
        if ($branchId !== null) {
            $prescription->loadMissing('visit');
            abort_unless($prescription->visit->branch_id === $branchId, 404);
        }
    }

    /**
     * @return array<string, mixed>
     */
    protected function validatedPrescriptionData(Request $request): array
    {
        return $request->validate([
            'practitioner_id' => ['nullable', 'integer', 'exists:care_practitioners,id'],
            'notes' => ['nullable', 'string', 'max:5000'],
            'activate' => ['nullable', 'boolean'],
            'items' => ['required', 'array', 'min:1'],
            'items.*.is_procedure' => ['nullable', 'boolean'],
            'items.*.name' => ['required', 'string', 'max:255'],
            'items.*.dosage' => ['nullable', 'string', 'max:100'],
            'items.*.frequency' => ['nullable', 'string', 'max:100'],
            'items.*.duration' => ['nullable', 'string', 'max:100'],
            'items.*.route' => ['nullable', 'string', 'max:50'],
            'items.*.quantity' => ['nullable', 'string', 'max:50'],
            'items.*.instructions' => ['nullable', 'string', 'max:2000'],
        ]);
    }
}
