<?php

namespace Modules\ClinicCore\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Modules\ClinicCore\Entities\ClcAppointment;
use Modules\ClinicCore\Entities\ClcDoctor;
use Modules\ClinicCore\Entities\ClcPatient;
use Modules\ClinicCore\Entities\ClcReferral;
use Yajra\DataTables\Facades\DataTables;

/**
 * SIFA P4-T6 — referrals CRUD + status workflow + referral-letter print.
 *
 * Internal (to another doctor here), external outgoing (to an outside
 * facility/provider) and external incoming (intake of a referral we received).
 * The letter print pulls the patient's active Rx meds + structured allergies at
 * render time. All actions are business-scoped + gated on clinic.referral.manage.
 */
class ClcReferralController 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.referral.manage')) {
            abort(403, 'Unauthorized action.');
        }
    }

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

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

    public function list(Request $request)
    {
        $this->guard();
        $businessId = $this->biz();

        $query = ClcReferral::with(['patient:id,patient_name,species', 'fromDoctor:id,name', 'toDoctor:id,name'])
            ->where('business_id', $businessId);

        if ($request->filled('filter_direction')) {
            $query->where('direction', $request->filter_direction);
        }
        if ($request->filled('filter_status')) {
            $query->where('status', $request->filter_status);
        }
        if ($request->filled('filter_search')) {
            $term = $request->filter_search;
            $query->where(function ($q) use ($term) {
                $q->where('referral_no', 'like', '%' . $term . '%')
                  ->orWhere('to_facility_name', 'like', '%' . $term . '%')
                  ->orWhere('to_provider_name', 'like', '%' . $term . '%')
                  ->orWhere('from_provider_name', 'like', '%' . $term . '%')
                  ->orWhereHas('patient', fn ($sq) => $sq->where('patient_name', 'like', '%' . $term . '%'));
            });
        }

        return DataTables::of($query)
            ->addColumn('referral_no_col', fn ($row) => '<span style="font-weight:700;font-family:monospace;color:#475569;">' . e($row->referral_no) . '</span>')
            ->addColumn('patient_name', fn ($row) => optional($row->patient)->patient_name ?? '—')
            ->addColumn('direction_badge', function ($row) {
                $map = ['internal' => 'info', 'external_outgoing' => 'primary', 'external_incoming' => 'default'];
                return '<span class="label label-' . ($map[$row->direction] ?? 'default') . '">' . __('clinicCore::lang.ref_dir_' . $row->direction) . '</span>';
            })
            ->addColumn('target', function ($row) {
                if ($row->direction === 'internal') return e(optional($row->toDoctor)->name ?? '—');
                if ($row->direction === 'external_outgoing') return e(trim(($row->to_facility_name ?? '') . ' ' . ($row->to_provider_name ? '· ' . $row->to_provider_name : '')) ?: '—');
                return e($row->from_provider_name ?? '—');
            })
            ->addColumn('urgency_badge', function ($row) {
                $map = ['routine' => 'default', 'urgent' => 'warning', 'emergency' => 'danger'];
                return '<span class="label label-' . ($map[$row->urgency] ?? 'default') . '">' . ucfirst($row->urgency) . '</span>';
            })
            ->addColumn('status_badge', function ($row) {
                $map = ['draft' => 'default', 'sent' => 'info', 'received' => 'info', 'accepted' => 'primary',
                        'in_progress' => 'warning', 'completed' => 'success', 'rejected' => 'danger', 'cancelled' => 'default'];
                return '<span class="label label-' . ($map[$row->status] ?? 'default') . '">' . ucfirst(str_replace('_', ' ', $row->status)) . '</span>';
            })
            ->addColumn('action', function ($row) {
                $view  = '<button class="btn btn-xs btn-info ref-view" data-id="' . $row->id . '" title="View"><i class="fa fa-eye"></i></button> ';
                $print = '<a href="' . route('clinic.referrals.print', $row->id) . '" target="_blank" class="btn btn-xs btn-default" title="Letter"><i class="fa fa-print"></i></a> ';
                $del   = '<button class="btn btn-xs btn-danger ref-del" data-id="' . $row->id . '" title="Delete"><i class="fa fa-trash"></i></button>';
                return $view . $print . $del;
            })
            ->rawColumns(['referral_no_col', 'direction_badge', 'urgency_badge', 'status_badge', 'action'])
            ->make(true);
    }

    /**
     * Prefill payload for the "Refer" button on the encounter panel: patient +
     * appointment + from_doctor + clinical summary pulled from the signed SOAP
     * assessment/plan (when present).
     */
    public function prefill(Request $request)
    {
        $this->guard();
        $businessId = $this->biz();

        $appointmentId = (int) $request->input('appointment_id');
        $appt = ClcAppointment::with('pet:id,patient_name')->where('business_id', $businessId)->find($appointmentId);
        if (!$appt) {
            return response()->json(['success' => false, 'msg' => 'Appointment not found.'], 404);
        }

        $summary = $this->summaryFromEncounter($appt);

        return response()->json([
            'success'        => true,
            'patient_id'     => $appt->patient_id,
            'patient_name'   => optional($appt->pet)->patient_name,
            'appointment_id' => $appt->id,
            'from_doctor_id' => $appt->doctor_id,
            'clinical_summary' => $summary,
        ]);
    }

    /** Assemble a clinical summary from the encounter's SOAP assessment + plan. */
    private function summaryFromEncounter(ClcAppointment $appt): string
    {
        $parts = [];
        foreach (['soap_assessment' => 'Assessment', 'soap_plan' => 'Plan'] as $col => $label) {
            $val = trim((string) ($appt->{$col} ?? ''));
            if ($val !== '') {
                $parts[] = $label . ': ' . $val;
            }
        }
        return implode("\n", $parts);
    }

    public function store(Request $request)
    {
        $this->guard();
        $businessId = $this->biz();

        $request->validate([
            'patient_id'         => 'required|integer',
            'direction'          => 'required|in:internal,external_outgoing,external_incoming',
            'urgency'            => 'nullable|in:routine,urgent,emergency',
            'reason'             => 'required|string|max:2000',
            'clinical_summary'   => 'nullable|string',
            'appointment_id'     => 'nullable|integer',
            'from_doctor_id'     => 'nullable|integer',
            'to_doctor_id'       => 'nullable|integer',
            'to_facility_name'   => 'nullable|string|max:191',
            'to_provider_name'   => 'nullable|string|max:191',
            'from_provider_name' => 'nullable|string|max:191',
            'requested_services' => 'nullable|array',
        ]);

        $patient = ClcPatient::where('business_id', $businessId)->findOrFail($request->patient_id);

        $referral = ClcReferral::create([
            'business_id'        => $businessId,
            'referral_no'        => ClcReferral::nextReferralNo($businessId),
            'patient_id'         => $patient->id,
            'appointment_id'     => $request->appointment_id,
            'direction'          => $request->direction,
            'urgency'            => $request->urgency ?: 'routine',
            'from_doctor_id'     => $request->direction === 'external_incoming' ? null : $request->from_doctor_id,
            'from_provider_name' => $request->direction === 'external_incoming' ? $request->from_provider_name : null,
            'to_doctor_id'       => $request->direction === 'internal' ? $request->to_doctor_id : null,
            'to_facility_name'   => $request->direction === 'external_outgoing' ? $request->to_facility_name : null,
            'to_provider_name'   => $request->direction === 'external_outgoing' ? $request->to_provider_name : null,
            'reason'             => $request->reason,
            'clinical_summary'   => $request->clinical_summary,
            'requested_services' => $this->cleanServices($request->input('requested_services', [])),
            // Incoming referrals start 'received'; ours start 'draft'.
            'status'             => $request->direction === 'external_incoming' ? 'received' : 'draft',
            'created_by'         => auth()->id(),
        ]);

        $this->drmLog('create', $referral, ['note' => 'Referral ' . $referral->direction]);

        return response()->json([
            'success'     => true,
            'msg'         => __('clinicCore::lang.saved_successfully'),
            'referral_id' => $referral->id,
        ]);
    }

    /** JSON detail for the view modal (incl. allowed next transitions). */
    public function show($id)
    {
        $this->guard();
        $businessId = $this->biz();

        $r = ClcReferral::with(['patient:id,patient_name,species', 'fromDoctor:id,name', 'toDoctor:id,name', 'appointment:id'])
            ->where('business_id', $businessId)->findOrFail($id);

        $bookUrl = null;
        if ($r->status === 'completed' && $r->direction === 'internal' && $r->to_doctor_id) {
            $bookUrl = route('clinic.appointments.create', [
                'patient_id' => $r->patient_id,
                'doctor_id'  => $r->to_doctor_id,
                'source'     => 'referral',
            ]);
        }

        return response()->json([
            'success'  => true,
            'referral' => [
                'id'                 => $r->id,
                'referral_no'        => $r->referral_no,
                'patient_name'       => optional($r->patient)->patient_name,
                'direction'          => $r->direction,
                'urgency'            => $r->urgency,
                'from_doctor'        => optional($r->fromDoctor)->name,
                'from_provider_name' => $r->from_provider_name,
                'to_doctor'          => optional($r->toDoctor)->name,
                'to_facility_name'   => $r->to_facility_name,
                'to_provider_name'   => $r->to_provider_name,
                'reason'             => $r->reason,
                'clinical_summary'   => $r->clinical_summary,
                'requested_services' => $r->requested_services ?: [],
                'response_notes'     => $r->response_notes,
                'status'             => $r->status,
                'next_statuses'      => ClcReferral::TRANSITIONS[$r->status] ?? [],
                'book_url'           => $bookUrl,
                'print_url'          => route('clinic.referrals.print', $r->id),
            ],
        ]);
    }

    /** Move a referral to the next status (guarded by the transition map). */
    public function transition(Request $request, $id)
    {
        $this->guard();
        $businessId = $this->biz();

        $request->validate([
            'status'         => 'required|in:draft,sent,received,accepted,in_progress,completed,rejected,cancelled',
            'response_notes' => 'nullable|string',
        ]);

        $r = ClcReferral::where('business_id', $businessId)->findOrFail($id);
        if (!$r->canTransitionTo($request->status)) {
            return response()->json([
                'success' => false,
                'msg'     => __('clinicCore::lang.ref_invalid_transition'),
            ], 422);
        }

        $before = $r->status;
        $r->status = $request->status;
        if ($request->filled('response_notes')) {
            $r->response_notes = $request->response_notes;
        }
        $r->save();

        $this->drmLog('update', $r, ['action' => 'status', 'from' => $before, 'to' => $r->status]);

        return response()->json(['success' => true, 'msg' => __('clinicCore::lang.updated_successfully'), 'status' => $r->status]);
    }

    public function destroy($id)
    {
        $this->guard();
        $businessId = $this->biz();

        $r = ClcReferral::where('business_id', $businessId)->findOrFail($id);
        $this->drmLog('delete', $r, ['before' => $this->_drmFilterAttrs($r->getAttributes())]);
        $r->delete();

        return response()->json(['success' => true, 'msg' => __('clinicCore::lang.deleted_successfully')]);
    }

    /** Referral-letter print view (letterhead pattern like orders/print). */
    public function print($id)
    {
        $this->guard();
        $businessId = $this->biz();

        $referral = ClcReferral::with([
            'patient.owner', 'fromDoctor', 'toDoctor', 'appointment',
        ])->where('business_id', $businessId)->findOrFail($id);

        $business = \App\Business::findOrFail($businessId);

        // Active meds from live prescriptions, and structured allergies.
        $meds = \DB::table('clc_prescription_items as i')
            ->join('clc_prescriptions as p', 'p.id', '=', 'i.prescription_id')
            ->where('p.business_id', $businessId)
            ->where('p.patient_id', $referral->patient_id)
            ->where('p.status', 'active')
            ->orderByDesc('p.prescribed_date')
            ->limit(30)
            ->get(['i.medicine_name', 'i.dosage', 'i.frequency']);

        $allergies = \DB::table('clc_patient_allergies')
            ->where('business_id', $businessId)
            ->where('patient_id', $referral->patient_id)
            ->where('status', 'active')
            ->orderByDesc('severity')
            ->get(['allergen', 'severity', 'type']);

        return view('clinicCore::referrals.print', compact('referral', 'business', 'meds', 'allergies'));
    }

    /** Strip empty rows from the requested-services repeater. */
    private function cleanServices($services): array
    {
        if (!is_array($services)) {
            return [];
        }
        $out = [];
        foreach ($services as $s) {
            $service = trim((string) ($s['service'] ?? ''));
            if ($service === '') {
                continue;
            }
            $out[] = [
                'service'  => mb_substr($service, 0, 191),
                'icd_code' => mb_substr(trim((string) ($s['icd_code'] ?? '')), 0, 30),
                'notes'    => mb_substr(trim((string) ($s['notes'] ?? '')), 0, 500),
            ];
        }
        return $out;
    }
}
