<?php

namespace Modules\ClinicCore\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Modules\ClinicCore\Entities\ClcPatientContactDetail;
use Modules\ClinicCore\Entities\ClcPatient;
use Modules\ClinicCore\Entities\ClcVaccination;
use Modules\ClinicCore\Entities\ClcAppointment;
use Modules\ClinicCore\Entities\ClcDoctor;
use Modules\ClinicCore\Entities\ClcPreferredDoctorHistory;
use App\Contact;
use Yajra\DataTables\Facades\DataTables;

class ClcPatientContactController extends Controller
{
    use \Modules\ClinicCore\Http\Controllers\Traits\LogsClcActivity;

    public function index()
    {
        if (!auth()->user()->can('clinic.manage_patient_contacts')) {
            abort(403, 'Unauthorized action.');
        }

        $businessId = request()->session()->get('user.business_id');
        $total = ClcPatientContactDetail::where('business_id', $businessId)->count();

        return view('clinicCore::patient-contacts.index', compact('total'));
    }

    public function list(Request $request)
    {
        if (!auth()->user()->can('clinic.manage_patient_contacts')) {
            abort(403, 'Unauthorized action.');
        }

        $businessId = request()->session()->get('user.business_id');
        $today      = now()->toDateString();
        $today30    = now()->addDays(30)->toDateString();

        $query = ClcPatientContactDetail::with('contact')
            ->where('business_id', $businessId);

        if ($request->filled('filter_search')) {
            $term = $request->filter_search;
            $query->whereHas('contact', function ($q) use ($term) {
                $q->where('name', 'like', '%' . $term . '%')
                  ->orWhere('mobile', 'like', '%' . $term . '%')
                  ->orWhere('email', 'like', '%' . $term . '%');
            });
        }

        // Per-row caches — populated as closures run for each row, shared via reference
        $patientIdsCache = [];
        $vaxCache    = [];
        $aptCache    = [];
        $finCache    = [];

        $getPetIds = function (int $cid) use ($businessId, &$patientIdsCache) {
            if (!array_key_exists($cid, $patientIdsCache)) {
                $patientIdsCache[$cid] = ClcPatient::where('contact_id', $cid)
                    ->where('business_id', $businessId)->pluck('id');
            }
            return $patientIdsCache[$cid];
        };

        $getVax = function (int $cid) use ($today, $today30, &$vaxCache, $getPetIds) {
            if (!array_key_exists($cid, $vaxCache)) {
                $ids = $getPetIds($cid);
                $overdue = $dueSoon = 0;
                if ($ids->isNotEmpty()) {
                    $overdue = ClcVaccination::whereIn('patient_id', $ids)
                        ->where('taken_status', 'not_taken')
                        ->where('administered_date', '<', $today)->count();
                    $dueSoon = ClcVaccination::whereIn('patient_id', $ids)
                        ->where('taken_status', 'not_taken')
                        ->whereBetween('administered_date', [$today, $today30])->count();
                }
                $vaxCache[$cid] = ['overdue' => $overdue, 'due_soon' => $dueSoon];
            }
            return $vaxCache[$cid];
        };

        $getMissed = function (int $cid) use ($businessId, &$aptCache) {
            if (!array_key_exists($cid, $aptCache)) {
                $aptCache[$cid] = ClcAppointment::where('contact_id', $cid)
                    ->where('business_id', $businessId)
                    ->whereIn('status', ['no_show', 'cancelled'])->count();
            }
            return $aptCache[$cid];
        };

        $getFin = function (int $cid) use ($businessId, &$finCache) {
            if (!array_key_exists($cid, $finCache)) {
                $row = \DB::table('transactions')
                    ->where('contact_id', $cid)->where('business_id', $businessId)
                    ->where('type', 'sell')->where('status', 'final')
                    ->selectRaw('COUNT(*) as cnt, COALESCE(SUM(final_total),0) as total_sell')
                    ->first();
                $totalPaid = \DB::table('transaction_payments')
                    ->join('transactions', 'transactions.id', '=', 'transaction_payments.transaction_id')
                    ->where('transactions.contact_id', $cid)
                    ->where('transactions.business_id', $businessId)
                    ->where('transactions.type', 'sell')->where('transactions.status', 'final')
                    ->where('transaction_payments.is_return', 0)
                    ->sum('transaction_payments.amount') ?: 0;
                $sell = $row->total_sell ?? 0;
                $finCache[$cid] = [
                    'count' => $row->cnt ?? 0,
                    'total' => $sell,
                    'paid'  => $totalPaid,
                    'due'   => max(0, $sell - $totalPaid),
                ];
            }
            return $finCache[$cid];
        };

        return DataTables::of($query)
            ->addColumn('name_col', function ($row) {
                $contact = $row->contact;
                if (!$contact) return '—';
                $name = '<a href="' . route('clinic.patient-contacts.show', $row->id) . '" style="font-weight:700;color:#1e293b;">' . e($contact->name ?? $contact->supplier_business_name ?? '—') . '</a>';
                // Item #9 - BLOCKED flag inline next to name so the list
                // surface immediately shows owners who can't book/be invoiced.
                $blocked = !empty($row->is_blocked)
                    ? ' <span title="' . e($row->blocked_reason ?? 'Blocked') . '" '
                       . 'style="font-size:9.5px;font-weight:800;padding:2px 7px;border-radius:9px;'
                       . 'background:#fef2f2;color:#dc2626;border:1px solid #fecaca;text-transform:uppercase;letter-spacing:.4px;">'
                       . '<i class="fa fa-ban"></i> Blocked</span>'
                    : '';
                // Item #2 - inline WA indicator next to mobile number.
                $waBadge = '';
                if ($contact->mobile) {
                    $waState = $contact->clc_has_whatsapp ?? null;
                    if ($waState === 1) {
                        $waBadge = ' <i class="fab fa-whatsapp" title="WhatsApp available" style="color:#16a34a;"></i>';
                    } elseif ($waState === 0) {
                        $waBadge = ' <i class="fas fa-ban" title="No WhatsApp" style="color:#dc2626;font-size:10px;"></i>';
                    }
                }
                $mobile = $contact->mobile
                    ? '<br><small class="text-muted"><i class="fa fa-phone"></i> ' . e($contact->mobile) . $waBadge . '</small>'
                    : '';
                return $name . $blocked . $mobile;
            })
            ->addColumn('email_col', function ($row) {
                $email = optional($row->contact)->email;
                if (!$email) return '—';
                $emailLink = '<a href="mailto:' . e($email) . '">' . e($email) . '</a>';
                if ($row->email_confirmed) {
                    $badge = ' <span class="label label-success" style="font-size:10px;" title="Email confirmed"><i class="fa fa-check"></i></span>';
                } elseif ($row->email_confirm_sent_at) {
                    $badge = ' <span class="label label-warning" style="font-size:10px;" title="Confirmation email sent — awaiting click"><i class="fa fa-clock"></i></span>';
                } else {
                    $badge = ' <span class="label label-default" style="font-size:10px;" title="No confirmation email sent yet"><i class="fa fa-envelope"></i></span>';
                }
                return $emailLink . $badge;
            })
            ->addColumn('pets_count_col', function ($row) use ($getPetIds) {
                $count = $getPetIds($row->contact_id)->count();
                if (!$count) return '<span class="text-muted">0</span>';
                return '<a href="' . route('clinic.patients.index') . '?contact_id=' . $row->contact_id . '" style="font-weight:700;color:#3b82f6;">' . $count . '</a>';
            })
            ->addColumn('alerts_col', function ($row) use ($getVax, $getMissed) {
                $cid    = $row->contact_id;
                $vax    = $getVax($cid);
                $missed = $getMissed($cid);
                $html = '';
                if ($vax['overdue']) {
                    $html .= '<span class="vc-lbadge vc-lbadge-danger"><i class="fa fa-exclamation-triangle"></i> ' . $vax['overdue'] . ' overdue vax</span>';
                }
                if ($vax['due_soon']) {
                    $html .= '<span class="vc-lbadge vc-lbadge-warning"><i class="fa fa-clock"></i> ' . $vax['due_soon'] . ' due soon</span>';
                }
                if ($missed) {
                    $html .= '<span class="vc-lbadge vc-lbadge-missed"><i class="fa fa-calendar-times"></i> ' . $missed . ' missed apt' . ($missed !== 1 ? 's' : '') . '</span>';
                }
                return $html ?: '<span class="text-muted" style="font-size:11px;">—</span>';
            })
            ->addColumn('finance_col', function ($row) use ($getFin) {
                $fin = $getFin($row->contact_id);
                if (!$fin['count']) return '<span class="text-muted" style="font-size:11px;">—</span>';
                $html = '<div style="font-size:11.5px;line-height:1.9;">';
                $html .= '<span style="color:#475569;font-weight:600;">' . $fin['count'] . ' inv.</span>';
                if ($fin['due'] > 0) {
                    $html .= '<br><span class="vc-lbadge vc-lbadge-danger"><i class="fa fa-exclamation-circle"></i> ' . number_format($fin['due'], 2) . ' due</span>';
                } else {
                    $html .= '<br><span style="color:#16a34a;font-size:10px;font-weight:600;"><i class="fa fa-check-circle"></i> Settled</span>';
                }
                $html .= '</div>';
                return $html;
            })
            ->addColumn('show_url', fn($row) => route('clinic.patient-contacts.show', $row->id))
            ->addColumn('name_plain', fn($row) => optional($row->contact)->name ?? '—')
            ->addColumn('mobile_plain', fn($row) => optional($row->contact)->mobile ?? null)
            ->addColumn('email_plain', fn($row) => optional($row->contact)->email ?? null)
            ->addColumn('pets_count', fn($row) => $getPetIds($row->contact_id)->count())
            ->addColumn('overdue_vax_raw', fn($row) => $getVax($row->contact_id)['overdue'])
            ->addColumn('due_soon_vax_raw', fn($row) => $getVax($row->contact_id)['due_soon'])
            ->addColumn('missed_apt_raw', fn($row) => $getMissed($row->contact_id))
            ->addColumn('due_amount_raw', fn($row) => $getFin($row->contact_id)['due'])
            ->addColumn('invoice_count_raw', fn($row) => $getFin($row->contact_id)['count'])
            ->addColumn('action', function ($row) {
                $show = '<a href="' . route('clinic.patient-contacts.show', $row->id) . '" class="btn btn-xs btn-info" title="View"><i class="fa fa-eye"></i></a> ';
                $edit = '<a href="' . route('clinic.patient-contacts.edit', $row->id) . '" class="btn btn-xs btn-warning" title="Edit"><i class="fa fa-edit"></i></a>';

                // Item #3 - Send Message button. Channel availability is
                // computed per-row so the modal only shows usable options
                // (no WhatsApp option if module isn't installed; no email
                // option if no email on file; etc). Wrapped in try/catch
                // because this runs ~25-100 times per pageload — a single
                // bad row must not 500 the whole datatable.
                $sendMsg = '';
                try {
                    if (auth()->user()->can('clinic.manage_patient_contacts') && $row->contact) {
                        $svc = app(\Modules\ClinicCore\Services\OwnerMessagingService::class);
                        $av  = $svc->availableChannels($row->contact);
                        if ($av['wa'] || $av['sms'] || $av['email']) {
                            $sendMsg = ' <button type="button" class="btn btn-xs btn-primary vc-send-msg-btn" title="Send message"'
                                . ' data-owner-id="' . $row->id . '"'
                                . ' data-owner-name="' . e(optional($row->contact)->name ?? '') . '"'
                                . ' data-has-wa="'    . ($av['wa']    ? '1' : '0') . '"'
                                . ' data-has-sms="'   . ($av['sms']   ? '1' : '0') . '"'
                                . ' data-has-email="' . ($av['email'] ? '1' : '0') . '"'
                                . ' data-send-url="'  . route('clinic.patient-contacts.send-message', $row->id) . '">'
                                . '<i class="fa fa-paper-plane"></i></button>';
                        }
                    }
                } catch (\Throwable $e) {
                    \Log::warning('ClinicCore owner-list send-message render failed: ' . $e->getMessage());
                }

                // Item #3b - inline unblock action when the row is blocked AND
                // the viewer has the manage permission. Fires the same
                // .vc-unblock-btn class as the banner button, so the
                // pet-owner list page just needs the shared modal+script
                // (included via the partial below).
                $unblock = '';
                if (!empty($row->is_blocked) && auth()->user()->can('clinic.manage_owner_blocking')) {
                    $unblock = ' <button type="button" class="btn btn-xs btn-success vc-unblock-btn" title="Unblock owner"'
                        . ' data-toggle="modal" data-target="#drmUnblockModal"'
                        . ' data-form-action="' . route('clinic.owner-blocking.unblock', $row->id) . '"'
                        . ' data-owner-name="' . e(optional($row->contact)->name ?? '') . '"'
                        . ' data-has-backup="' . ($row->credit_limit_backup !== null ? '1' : '0') . '"'
                        . ' data-backup-value="' . e((string) ($row->credit_limit_backup ?? '')) . '">'
                        . '<i class="fa fa-lock-open"></i></button>';
                }
                return $show . $edit . $sendMsg . $unblock;
            })
            ->rawColumns(['action', 'name_col', 'email_col', 'pets_count_col', 'alerts_col', 'finance_col'])
            ->make(true);
    }

    public function create()
    {
        if (!auth()->user()->can('clinic.manage_patient_contacts')) {
            abort(403, 'Unauthorized action.');
        }

        $businessId = request()->session()->get('user.business_id');
        $doctors = ClcDoctor::where('business_id', $businessId)->active()->orderBy('name')->get();

        return view('clinicCore::patient-contacts.create', compact('doctors'));
    }

    public function store(Request $request)
    {
        if (!auth()->user()->can('clinic.manage_patient_contacts')) {
            abort(403, 'Unauthorized action.');
        }

        $request->validate([
            'name'   => 'required|string|max:150',
            'mobile' => 'nullable|string|max:30',
            'email'  => 'nullable|email|max:150',
        ]);

        $businessId = request()->session()->get('user.business_id');

        $contact = null;

        if ($request->contact_id) {
            $contact = Contact::where('business_id', $businessId)->find($request->contact_id);
        }

        if (!$contact && $request->email) {
            $contact = Contact::where('business_id', $businessId)->where('email', $request->email)->first();
        }

        // Item #2 - WA availability radio: 'yes'->1, 'no'->0, 'unknown'->NULL.
        // Only update the column when the input was sent (avoid clobbering
        // an existing value when editing without explicitly choosing).
        $waInput = $request->input('clc_has_whatsapp');
        $waValue = $waInput === 'yes' ? 1 : ($waInput === 'no' ? 0 : ($waInput === 'unknown' ? null : '__skip__'));

        if ($contact) {
            $contactUpdate = array_filter([
                'name'   => $request->name,
                'mobile' => $request->mobile ?: $contact->mobile,
                'email'  => $request->email  ?: $contact->email,
            ]);
            if ($waValue !== '__skip__' && \Schema::hasColumn('contacts', 'clc_has_whatsapp')) {
                $contactUpdate['clc_has_whatsapp'] = $waValue;
            }
            $contact->update($contactUpdate);
        } else {
            $newContactData = [
                'business_id'    => $businessId,
                'name'           => $request->name,
                'mobile'         => $request->mobile,
                'email'          => $request->email,
                'type'           => 'customer',
                'contact_status' => 'active',
                'created_by'     => auth()->id(),
            ];
            if ($waValue !== '__skip__' && \Schema::hasColumn('contacts', 'clc_has_whatsapp')) {
                $newContactData['clc_has_whatsapp'] = $waValue;
            }
            $contact = Contact::create($newContactData);
        }

        $oldPreferredDoctorId = optional(ClcPatientContactDetail::where('contact_id', $contact->id)->first())->preferred_doctor_id;
        $newPreferredDoctorId = $request->preferred_doctor_id ?: null;

        $ownerDetail = ClcPatientContactDetail::updateOrCreate(
            ['contact_id' => $contact->id],
            [
                'business_id'                  => $businessId,
                'emergency_contact_name'       => $request->emergency_contact_name,
                'emergency_contact_phone'      => $request->emergency_contact_phone,
                'emergency_contact_relation'   => $request->emergency_contact_relation,
                'occupation'                   => $request->occupation,
                'referred_by'                  => $request->referred_by,
                'preferred_doctor_id'          => $newPreferredDoctorId,
                'notes'                        => $request->notes,
            ]
        );

        ClcPreferredDoctorHistory::record([
            'business_id'   => $businessId,
            'contact_id'    => $contact->id,
            'patient_id'        => null,
            'old_doctor_id' => $oldPreferredDoctorId,
            'new_doctor_id' => $newPreferredDoctorId,
            'source'        => 'manual',
        ]);

        // Auto-send confirmation email if email is present and not yet confirmed
        if ($contact->email && !$ownerDetail->email_confirmed) {
            $this->sendEmailConfirmation($ownerDetail, $contact->email, $contact->name, $businessId);
        }

        $this->drmLog('create', $ownerDetail);

        return redirect()->route('clinic.patient-contacts.index')
            ->with('status', ['success' => 1, 'msg' => __('clinicCore::lang.saved_successfully')]);
    }

    public function show($id)
    {
        if (!auth()->user()->can('clinic.manage_patient_contacts')) {
            abort(403, 'Unauthorized action.');
        }

        $businessId = request()->session()->get('user.business_id');
        $owner = ClcPatientContactDetail::with('contact')->where('business_id', $businessId)->findOrFail($id);
        $patients  = ClcPatient::where('contact_id', $owner->contact_id)
            ->where('business_id', $businessId)
            ->withCount(['vaccinations as overdue_vax_count' => function ($q) {
                $q->where('taken_status', 'not_taken')->where('administered_date', '<', now()->toDateString());
            }, 'vaccinations as due_soon_vax_count' => function ($q) {
                $q->where('taken_status', 'not_taken')
                  ->whereBetween('administered_date', [now()->toDateString(), now()->addDays(30)->toDateString()]);
            }, 'appointments as missed_apt_count' => function ($q) {
                $q->whereIn('status', ['no_show', 'cancelled']);
            }])
            ->get();

        $patientIds = $patients->pluck('id');
        $today  = \Carbon\Carbon::today();

        $overdueVaccinations = \Modules\ClinicCore\Entities\ClcVaccination::whereIn('patient_id', $patientIds)
            ->where('taken_status', 'not_taken')
            ->where('administered_date', '<', $today)
            ->with(['pet', 'vaccineType'])
            ->orderBy('administered_date')
            ->get();

        $dueSoonVaccinations = \Modules\ClinicCore\Entities\ClcVaccination::whereIn('patient_id', $patientIds)
            ->where('taken_status', 'not_taken')
            ->whereBetween('administered_date', [$today, $today->copy()->addDays(30)])
            ->with(['pet', 'vaccineType'])
            ->orderBy('administered_date')
            ->get();

        // ERP financial data
        $contactId    = $owner->contact_id;
        $totalSell    = \DB::table('transactions')
            ->where('contact_id', $contactId)->where('business_id', $businessId)
            ->where('type', 'sell')->where('status', 'final')->sum('final_total') ?: 0;
        $totalPaid    = \DB::table('transaction_payments')
            ->join('transactions', 'transactions.id', '=', 'transaction_payments.transaction_id')
            ->where('transactions.contact_id', $contactId)
            ->where('transactions.business_id', $businessId)
            ->where('transactions.type', 'sell')->where('transactions.status', 'final')
            ->where('transaction_payments.is_return', 0)
            ->sum('transaction_payments.amount') ?: 0;
        $dueAmount    = max(0, $totalSell - $totalPaid);
        $invoiceCount = \DB::table('transactions')
            ->where('contact_id', $contactId)->where('business_id', $businessId)
            ->where('type', 'sell')->where('status', 'final')->count();

        // ── Item 2.1: cumulative rating + rating history for this owner ──
        // Pulls every pet_owner evaluation row linked to this contact, plus
        // the joined appointment metadata so the widget can show context.
        $ratings = \Modules\ClinicCore\Entities\ClcEvaluation::where('business_id', $businessId)
            ->where('contact_id', $contactId)
            ->where('category', 'pet_owner')
            ->whereNotNull('avg_rating')
            ->with(['appointment:id,ref_no,appointment_date,doctor_id,patient_id',
                    'appointment.doctor:id,name',
                    'appointment.pet:id,patient_name',
                    'evaluator:id,first_name,last_name,surname'])
            ->orderByDesc('created_at')
            ->get();

        $ratingCount   = $ratings->count();
        $ratingAverage = $ratingCount ? round($ratings->avg('avg_rating'), 2) : null;
        $ratingFlagged = $ratings->where('flagged_for_review', true)->count();
        // 5-point scale → percentage for the threshold check used later by
        // the auto-block policy (item 3.1). Stored here for the widget too.
        $ratingPercent = $ratingAverage !== null ? round(($ratingAverage / 5) * 100) : null;

        // Item #2 - preferred doctor display + change. Load active doctors
        // for the change dropdown; eager-load the preferred doctor itself.
        $owner->loadMissing('preferredDoctor');
        $doctorsList = \Modules\ClinicCore\Entities\ClcDoctor::where('business_id', $businessId)
            ->active()->orderBy('name')->get(['id', 'name', 'specialization']);

        $this->drmLog('view', $owner);
        return view('clinicCore::patient-contacts.show', compact(
            'owner', 'patients', 'overdueVaccinations', 'dueSoonVaccinations',
            'totalSell', 'dueAmount', 'invoiceCount',
            'ratings', 'ratingCount', 'ratingAverage', 'ratingFlagged', 'ratingPercent',
            'doctorsList'
        ));
    }

    /**
     * Item #3 - POST /clinic/patient-contacts/{id}/send-message
     * Send a message to the pet owner via the chosen channel
     * (whatsapp | sms | email). Channel availability is enforced
     * server-side via OwnerMessagingService::availableChannels(), so
     * even a forged request can't route through a disabled channel.
     */
    public function sendMessage(Request $request, $id)
    {
        if (!auth()->user()->can('clinic.manage_patient_contacts')) {
            abort(403, 'Not authorised.');
        }
        $request->validate([
            'channel' => 'required|in:whatsapp,sms,email',
            'message' => 'required|string|max:4000',
            'subject' => 'nullable|string|max:200',
        ]);
        $businessId = $request->session()->get('user.business_id');
        $owner = ClcPatientContactDetail::where('business_id', $businessId)->findOrFail($id);
        $contact = \App\Contact::find($owner->contact_id);
        if (!$contact) abort(404, 'Owner contact missing.');

        $svc = app(\Modules\ClinicCore\Services\OwnerMessagingService::class);
        $avail = $svc->availableChannels($contact);

        $ch = $request->channel;
        $chKey = $ch === 'whatsapp' ? 'wa' : $ch;
        if (empty($avail[$chKey])) {
            return response()->json([
                'success' => false,
                'msg'     => 'The ' . strtoupper($ch) . ' channel is not available for this owner.',
            ], 422);
        }

        switch ($ch) {
            case 'whatsapp':
                $result = $svc->sendWhatsapp($contact, (int) $businessId, $request->message, (int) auth()->id());
                break;
            case 'sms':
                $result = $svc->sendSms($contact, (int) $businessId, $request->message);
                break;
            case 'email':
            default:
                $result = $svc->sendEmail($contact, (int) $businessId, $request->message, $request->subject);
                break;
        }
        return response()->json($result);
    }

    /**
     * Item #2 (WhatsApp) - POST /clinic/patient-contacts/{id}/whatsapp
     * Toggle the WhatsApp availability flag on the underlying contact.
     * Accepts: state = 'yes' | 'no' | 'unknown'.
     */
    public function setWhatsappState(Request $request, $id)
    {
        if (!auth()->user()->can('clinic.manage_patient_contacts')) {
            abort(403, 'Unauthorized action.');
        }
        $request->validate(['state' => 'required|in:yes,no,unknown']);
        $businessId = $request->session()->get('user.business_id');
        $owner = ClcPatientContactDetail::where('business_id', $businessId)->findOrFail($id);
        $contact = \App\Contact::find($owner->contact_id);
        if (!$contact) abort(404, 'Contact missing.');
        $contact->clc_has_whatsapp = $request->state === 'unknown' ? null
                                  : ($request->state === 'yes' ? 1 : 0);
        $contact->save();
        return response()->json([
            'success' => true,
            'state'   => $request->state,
        ]);
    }

    /**
     * Item #2 - POST /clinic/patient-contacts/{id}/preferred-doctor
     * Set/clear the owner-level preferred doctor. Snapshots the change
     * into clc_preferred_doctor_history when the table exists, so the
     * existing Preferred-Doctor Changes report (vc.reports.preferred-doctor-changes)
     * still picks it up.
     */
    public function setPreferredDoctor(Request $request, $id)
    {
        if (!auth()->user()->can('clinic.manage_patient_contacts')) {
            abort(403, 'Unauthorized action.');
        }
        $request->validate([
            'preferred_doctor_id' => 'nullable|integer|exists:clc_doctors,id',
        ]);
        $businessId = $request->session()->get('user.business_id');
        $owner = ClcPatientContactDetail::where('business_id', $businessId)->findOrFail($id);

        $previousId = $owner->preferred_doctor_id;
        $newId      = $request->preferred_doctor_id ?: null;
        if ($previousId == $newId) {
            return response()->json(['success' => true, 'changed' => false]);
        }
        $owner->preferred_doctor_id = $newId;
        $owner->save();

        // Snapshot for the audit trail used by the existing Preferred-Doctor
        // Changes report. Fail-soft if the table isn't present.
        try {
            if (\Schema::hasTable('clc_preferred_doctor_history')) {
                \DB::table('clc_preferred_doctor_history')->insert([
                    'business_id'      => $businessId,
                    'scope'            => 'owner',
                    'scope_id'         => $owner->id,
                    'contact_id'       => $owner->contact_id,
                    'patient_id'           => null,
                    'previous_doctor_id' => $previousId,
                    'new_doctor_id'    => $newId,
                    'changed_by'       => auth()->id(),
                    'changed_at'       => now(),
                    'created_at'       => now(),
                    'updated_at'       => now(),
                ]);
            }
        } catch (\Throwable $e) {
            \Log::warning('Preferred-doctor history snapshot failed: ' . $e->getMessage());
        }
        return response()->json([
            'success'         => true,
            'changed'         => true,
            'preferred_doctor'=> $newId
                ? optional(\Modules\ClinicCore\Entities\ClcDoctor::find($newId))->name
                : null,
        ]);
    }

    public function edit($id)
    {
        if (!auth()->user()->can('clinic.manage_patient_contacts')) {
            abort(403, 'Unauthorized action.');
        }

        $businessId = request()->session()->get('user.business_id');
        $owner = ClcPatientContactDetail::with('contact')->where('business_id', $businessId)->findOrFail($id);
        $doctors = ClcDoctor::where('business_id', $businessId)->active()->orderBy('name')->get();

        return view('clinicCore::patient-contacts.create', compact('owner', 'doctors'));
    }

    public function update(Request $request, $id)
    {
        if (!auth()->user()->can('clinic.manage_patient_contacts')) {
            abort(403, 'Unauthorized action.');
        }

        $request->validate(['name' => 'required|string|max:150']);
        $businessId = request()->session()->get('user.business_id');
        $owner   = ClcPatientContactDetail::with('contact')->where('business_id', $businessId)->findOrFail($id);
        $contact = $owner->contact;
        $oldEmail = optional($contact)->email;

        optional($contact)->update([
            'name'   => $request->name,
            'mobile' => $request->mobile,
            'email'  => $request->email,
        ]);

        $original = $owner->getOriginal();
        $oldPreferredDoctorId = $owner->preferred_doctor_id;
        $newPreferredDoctorId = $request->preferred_doctor_id ?: null;

        $owner->update(array_merge(
            $request->only([
                'emergency_contact_name', 'emergency_contact_phone',
                'emergency_contact_relation', 'occupation', 'referred_by', 'notes',
            ]),
            ['preferred_doctor_id' => $newPreferredDoctorId]
        ));
        $dirty = array_keys($owner->getChanges());

        ClcPreferredDoctorHistory::record([
            'business_id'   => $businessId,
            'contact_id'    => $owner->contact_id,
            'patient_id'        => null,
            'old_doctor_id' => $oldPreferredDoctorId,
            'new_doctor_id' => $newPreferredDoctorId,
            'source'        => 'manual',
        ]);
        if ($dirty) {
            $this->drmLog('update', $owner, [
                'before' => array_intersect_key($original, array_flip($dirty)),
                'after'  => array_intersect_key($owner->getAttributes(), array_flip($dirty)),
                'fields' => $dirty,
            ]);
        }

        // If email changed, reset confirmation and send new one
        $newEmail = $request->email;
        if ($newEmail && $newEmail !== $oldEmail) {
            $this->sendEmailConfirmation($owner, $newEmail, $request->name ?? optional($contact)->name, $businessId);
        }

        return redirect()->route('clinic.patient-contacts.show', $id)
            ->with('status', ['success' => 1, 'msg' => __('clinicCore::lang.updated_successfully')]);
    }

    public function resendConfirmation(Request $request, $id)
    {
        if (!auth()->user()->can('clinic.manage_patient_contacts')) {
            return response()->json(['success' => false, 'msg' => 'Unauthorized'], 403);
        }

        $businessId = request()->session()->get('user.business_id');
        $owner      = ClcPatientContactDetail::with('contact')->where('business_id', $businessId)->findOrFail($id);
        $email      = optional($owner->contact)->email;

        if (!$email) {
            return response()->json(['success' => false, 'msg' => 'No email address on file.']);
        }

        $sent = $this->sendEmailConfirmation($owner, $email, optional($owner->contact)->name ?? 'Contact', $businessId);

        return response()->json([
            'success' => $sent,
            'msg'     => $sent ? 'Confirmation email sent to ' . $email : 'Failed to send email.',
        ]);
    }

    public function confirmEmail($token)
    {
        $owner = ClcPatientContactDetail::where('email_confirm_token', $token)->first();

        if (!$owner) {
            return response(
                '<html><head><meta charset="utf-8"><title>Invalid Link</title></head>'
                . '<body style="font-family:sans-serif;text-align:center;padding:80px 20px;color:#333;">'
                . '<h2 style="color:#e74c3c;">&#10007; Invalid or Expired Link</h2>'
                . '<p>This confirmation link may have already been used or is no longer valid.</p>'
                . '</body></html>'
            );
        }

        $ownerName = optional($owner->contact)->name ?? 'Contact';
        $owner->update([
            'email_confirmed'     => true,
            'email_confirm_token' => null,
        ]);

        return response(
            '<html><head><meta charset="utf-8"><title>Email Confirmed</title></head>'
            . '<body style="font-family:sans-serif;text-align:center;padding:80px 20px;color:#333;">'
            . '<h2 style="color:#27ae60;">&#10003; Email Confirmed!</h2>'
            . '<p>Thank you, <strong>' . htmlspecialchars($ownerName) . '</strong>!</p>'
            . '<p>Your email has been confirmed. You will now receive vaccination reminders.</p>'
            . '</body></html>'
        );
    }

    public function search(Request $request)
    {
        $businessId = request()->session()->get('user.business_id');

        $term = $request->input('q', '');

        $contacts = Contact::where('business_id', $businessId)
            ->whereIn('type', ['customer', 'both'])
            ->where('contact_status', 'active')
            ->whereIn('id', ClcPatient::where('business_id', $businessId)
                ->where('status', 'active')
                ->where('deceased', false)
                ->pluck('contact_id'))
            ->where(function ($q) use ($term) {
                $q->where('name', 'LIKE', '%' . $term . '%')
                  ->orWhere('mobile', 'LIKE', '%' . $term . '%')
                  ->orWhere('email', 'LIKE', '%' . $term . '%')
                  ->orWhere('supplier_business_name', 'LIKE', '%' . $term . '%');
            })
            ->limit(20)
            ->get(['id', 'name', 'supplier_business_name', 'mobile', 'email']);

        return response()->json($contacts->map(function ($c) {
            $displayName = trim($c->name ?? '') ?: trim($c->supplier_business_name ?? '');
            return [
                'id'     => $c->id,
                'text'   => $displayName . ($c->mobile ? ' (' . $c->mobile . ')' : ''),
                'name'   => $displayName,
                'mobile' => $c->mobile,
                'email'  => $c->email,
            ];
        }));
    }

    private function sendEmailConfirmation(ClcPatientContactDetail $ownerDetail, string $email, string $ownerName, int $businessId): bool
    {
        $token = bin2hex(random_bytes(32));
        $ownerDetail->update([
            'email_confirmed'       => false,
            'email_confirm_token'   => $token,
            'email_confirm_sent_at' => now(),
        ]);

        $confirmUrl   = route('clinic.patient-contacts.confirm-email', ['token' => $token]);
        $businessName = optional(\App\Business::find($businessId))->name ?? 'Clinic';

        try {
            \Mail::raw(
                "Dear {$ownerName},\n\n"
                . "Please confirm your email address to receive vaccination reminders from {$businessName}.\n\n"
                . "Click the link below to confirm:\n{$confirmUrl}\n\n"
                . "If you did not request this, you can safely ignore this email.\n\n"
                . "Best regards,\n{$businessName}",
                function ($message) use ($email, $ownerName) {
                    $message->to($email, $ownerName)
                            ->subject('Confirm your email — Vaccination Reminders');
                }
            );
            return true;
        } catch (\Exception $e) {
            return false;
        }
    }
}
