<?php

namespace App\Controllers;

use App\Core\Controller;
use App\Helpers\Auth;
use App\Helpers\Mailer;
use App\Helpers\Url;
use App\Models\Contribution;
use App\Models\ContributionType;
use App\Models\Receipt;
use App\Models\Setting;
use App\Models\AuditLog;
use App\Models\User;

class ContributionController extends Controller
{
    public function __construct()
    {
        parent::__construct();
        Auth::requireLogin();
    }

    public function index()
    {
        $isAdmin = Auth::user()->role === 'admin';
        $model = new Contribution();
        $typeModel = new ContributionType();
        $types = $typeModel->getActive();

        if ($isAdmin) {
            $page = max(1, (int)($_GET['page'] ?? 1));
            $userId = !empty($_GET['user_id']) ? (int)$_GET['user_id'] : null;
            $result = $model->getAllWithDetails($page, 20, $userId);
            $contributions = $result->data;
            $pagination = $result;
            $userModel = new User();
            $members = $userModel->where('approval_status', 'approved');
        } else {
            $contributions = $model->getByUserWithReceipt(Auth::id());
            $pagination = null;
            $members = [];
        }

        $this->view('contributions/index', [
            'contributions' => $contributions ?? [],
            'types' => $types,
            'pagination' => $pagination,
            'members' => $members,
            'isAdmin' => $isAdmin,
        ]);
    }

    public function create()
    {
        if (Auth::user()->role !== 'admin') {
            $_SESSION['error'] = 'Only admins can record contributions';
            $this->redirect('/contributions');
        }
        $typeModel = new ContributionType();
        $types = $typeModel->getActive();
        $userModel = new User();
        $members = $userModel->where('approval_status', 'approved');

        $existingUserId = $_GET['user_id'] ?? '';
        $existingTypeId = $_GET['type_id'] ?? '';
        $existingMonth = $_GET['month'] ?? '';

        $this->view('contributions/form', [
            'types' => $types,
            'members' => $members,
            'contribution' => null,
            'existingUserId' => $existingUserId,
            'existingTypeId' => $existingTypeId,
            'existingMonth' => $existingMonth,
        ]);
    }

    public function searchMembers()
    {
        $query = isset($_GET['q']) ? trim($_GET['q']) : '';
        if (strlen($query) < 1) {
            $this->json([]);
        }
        $stmt = $this->db->prepare("SELECT id, first_name, other_names, last_name, name, email, mobile_phone, alternative_phone FROM users WHERE approval_status = 'approved' AND (first_name LIKE ? OR last_name LIKE ? OR other_names LIKE ? OR name LIKE ? OR email LIKE ? OR mobile_phone LIKE ? OR alternative_phone LIKE ?) ORDER BY first_name ASC LIMIT 15");
        $like = "%{$query}%";
        $stmt->execute([$like, $like, $like, $like, $like, $like, $like]);
        $users = $stmt->fetchAll(\PDO::FETCH_OBJ);

        $results = array_map(function ($u) {
            $fullName = $u->first_name ? trim($u->first_name . ' ' . $u->other_names . ' ' . $u->last_name) : $u->name;
            $phone = $u->mobile_phone ?? $u->alternative_phone ?? '';
            return [
                'id' => $u->id,
                'name' => $fullName,
                'email' => $u->email,
                'phone' => $phone,
                'label' => "{$fullName} ({$u->email})",
            ];
        }, $users);

        $this->json($results);
    }

    public function store()
    {
        if (Auth::user()->role !== 'admin') {
            $_SESSION['error'] = 'Only admins can record contributions';
            $this->redirect('/contributions');
        }

        $userId = (int)($_POST['user_id'] ?? Auth::id());
        $contributionTypeId = (int)($_POST['contribution_type_id'] ?? 0);
        $paidMonth = $_POST['paid_month'] ?? date('Y-m');

        $amount = (float)($_POST['amount'] ?? 0);
        $totalAmount = !empty($_POST['total_amount']) ? (float)$_POST['total_amount'] : $amount;
        $status = $_POST['status'] ?? 'completed';
        $paymentDate = $_POST['payment_date'] ?? date('Y-m-d');

        $model = new Contribution();

        $existingBal = $model->getObligationBalance($userId, $contributionTypeId, $paidMonth);
        $hasExistingPartial = ($existingBal->balance > 0 || $existingBal->paid > 0);

        if ($hasExistingPartial) {
            $totalAmount = $totalAmount ?: $existingBal->total;
        }
        if ($amount > $totalAmount) {
            $amount = $totalAmount;
        }

        $data = [
            'user_id' => $userId,
            'contribution_type_id' => $contributionTypeId,
            'amount' => $amount,
            'total_amount' => $totalAmount,
            'payment_method' => $_POST['payment_method'] ?? '',
            'transaction_ref' => $_POST['transaction_ref'] ?? '',
            'payment_date' => $paymentDate,
            'paid_month' => $paidMonth,
            'notes' => $_POST['notes'] ?? '',
            'processed_by' => Auth::id(),
        ];

        if ($hasExistingPartial) {
            $newPaid = $existingBal->paid + $amount;
            $newBalance = $totalAmount - $newPaid;
            $data['balance'] = max(0, $newBalance);
            if ($newBalance <= 0) {
                $data['status'] = 'completed';
            } else {
                $data['status'] = 'partial';
            }
            $contributionId = $model->create($data);
            $model->updateBalanceForObligation($userId, $contributionTypeId, $paidMonth);
            $status = $data['status'];
        } else {
            $balance = $totalAmount - $amount;
            if ($balance <= 0) {
                $data['status'] = 'completed';
                $data['balance'] = 0;
            } else {
                $data['status'] = 'partial';
                $data['balance'] = $balance;
            }
            $contributionId = $model->create($data);
            $status = $data['status'];
        }

        $receiptId = null;
        if ($status === 'completed') {
            $receiptModel = new Receipt();
            $receiptId = $receiptModel->create([
                'contribution_id' => $contributionId,
                'receipt_number' => $receiptModel->generateNumber(),
                'issued_date' => date('Y-m-d'),
                'issued_by' => Auth::id(),
            ]);
        }

        $audit = new AuditLog();
        $audit->log(Auth::id(), 'contribution.create', 'contributions', $contributionId, "Recorded contribution GHS {$amount} for user #{$userId}");

        $this->notifyMember($userId, $data, $receiptId);

        $_SESSION['success'] = $status === 'partial' ? 'Partial payment recorded. Balance remains GHS ' . number_format(($totalAmount - ($existingBal->paid + $amount)), 2) : 'Contribution recorded successfully';
        $this->redirect('/contributions');
    }

    public function edit($params)
    {
        if (Auth::user()->role !== 'admin') {
            $_SESSION['error'] = 'Unauthorized';
            $this->redirect('/contributions');
        }
        $id = $params['id'] ?? null;
        $model = new Contribution();
        $contribution = $model->find($id);
        if (!$contribution) {
            $_SESSION['error'] = 'Contribution not found';
            $this->redirect('/contributions');
        }
        $typeModel = new ContributionType();
        $types = $typeModel->getActive();
        $userModel = new User();
        $members = $userModel->where('approval_status', 'approved');
        $receiptModel = new Receipt();
        $receipt = $receiptModel->getByContribution($id);

        $relatedPayments = [];
        if ($contribution->paid_month && $contribution->contribution_type_id) {
            $relatedPayments = $model->getObligationPayments($contribution->user_id, $contribution->contribution_type_id, $contribution->paid_month);
        }

        $this->view('contributions/form', [
            'types' => $types,
            'members' => $members,
            'contribution' => $contribution,
            'receipt' => $receipt[0] ?? null,
            'relatedPayments' => $relatedPayments,
        ]);
    }

    public function update($params)
    {
        if (Auth::user()->role !== 'admin') {
            $_SESSION['error'] = 'Unauthorized';
            $this->redirect('/contributions');
        }
        $id = $params['id'] ?? null;
        $model = new Contribution();
        $contribution = $model->find($id);
        if (!$contribution) {
            $_SESSION['error'] = 'Contribution not found';
            $this->redirect('/contributions');
        }

        $status = $_POST['status'] ?? $contribution->status;
        $amount = (float)($_POST['amount'] ?? $contribution->amount);
        $totalAmount = !empty($_POST['total_amount']) ? (float)$_POST['total_amount'] : ($contribution->total_amount ?: $amount);

        $model->update($id, [
            'contribution_type_id' => (int)($_POST['contribution_type_id'] ?? $contribution->contribution_type_id),
            'amount' => $amount,
            'total_amount' => $totalAmount,
            'payment_method' => $_POST['payment_method'] ?? $contribution->payment_method,
            'transaction_ref' => $_POST['transaction_ref'] ?? $contribution->transaction_ref,
            'payment_date' => $_POST['payment_date'] ?? $contribution->payment_date,
            'paid_month' => $_POST['paid_month'] ?? $contribution->paid_month,
            'status' => $status,
            'notes' => $_POST['notes'] ?? $contribution->notes,
            'updated_by' => Auth::id(),
        ]);

        if ($contribution->paid_month && $contribution->contribution_type_id) {
            $model->updateBalanceForObligation($contribution->user_id, $contribution->contribution_type_id, $contribution->paid_month);
        }

        if (in_array($status, ['completed', 'partial']) && $contribution->status !== $status) {
            $receiptModel = new Receipt();
            $existing = $receiptModel->getByContribution($id);
            if (empty($existing) && $status === 'completed') {
                $receiptModel->create([
                    'contribution_id' => $id,
                    'receipt_number' => $receiptModel->generateNumber(),
                    'issued_date' => date('Y-m-d'),
                    'issued_by' => Auth::id(),
                ]);
            }
        }

        $audit = new AuditLog();
        $audit->log(Auth::id(), 'contribution.update', 'contributions', $id, "Updated contribution #{$id}");

        if ($status === 'completed' && $contribution->status !== 'completed') {
            $this->notifyMember($contribution->user_id, (array)$contribution, null);
        }

        $_SESSION['success'] = 'Contribution updated successfully';
        $this->redirect('/contributions');
    }

    public function addPayment()
    {
        if (Auth::user()->role !== 'admin') {
            $_SESSION['error'] = 'Unauthorized';
            $this->redirect('/contributions');
        }

        $userId = (int)($_GET['user_id'] ?? 0);
        $typeId = (int)($_GET['type_id'] ?? 0);
        $month = $_GET['month'] ?? '';

        if (!$userId || !$typeId || !$month) {
            $_SESSION['error'] = 'Missing obligation parameters';
            $this->redirect('/contributions');
        }

        $model = new Contribution();
        $typeModel = new ContributionType();
        $types = $typeModel->getActive();
        $userModel = new User();
        $members = $userModel->where('approval_status', 'approved');
        $obligation = $model->getObligationBalance($userId, $typeId, $month);
        $payments = $model->getObligationPayments($userId, $typeId, $month);

        $contribution = (object)[
            'user_id' => $userId,
            'contribution_type_id' => $typeId,
            'paid_month' => $month,
            'total_amount' => $obligation->total,
            'amount' => 0,
            'balance' => $obligation->balance,
        ];

        $existingContribution = new \stdClass();
        $existingContribution->id = null;
        $existingContribution->user_id = $userId;
        $existingContribution->contribution_type_id = $typeId;
        $existingContribution->paid_month = $month;
        $existingContribution->amount = 0;
        $existingContribution->total_amount = $obligation->total;
        $existingContribution->balance = $obligation->balance;
        $existingContribution->payment_method = '';
        $existingContribution->transaction_ref = '';
        $existingContribution->payment_date = date('Y-m-d');
        $existingContribution->status = 'partial';
        $existingContribution->notes = '';

        $this->view('contributions/form', [
            'types' => $types,
            'members' => $members,
            'contribution' => $existingContribution,
            'isAddPayment' => true,
            'relatedPayments' => $payments,
        ]);
    }

    public function approve($params)
    {
        if (Auth::user()->role !== 'admin') {
            $_SESSION['error'] = 'Unauthorized';
            $this->redirect('/contributions');
        }

        $id = $params['id'] ?? null;
        $model = new Contribution();
        $contribution = $model->find($id);

        if ($contribution && $contribution->status === 'pending') {
            $model->update($id, ['status' => 'completed', 'processed_by' => Auth::id()]);

            if ($contribution->paid_month && $contribution->contribution_type_id) {
                $model->updateBalanceForObligation($contribution->user_id, $contribution->contribution_type_id, $contribution->paid_month);
            }

            $receiptId = null;
            $receiptModel = new Receipt();
            $existing = $receiptModel->getByContribution($id);
            if (empty($existing)) {
                $receiptId = $receiptModel->create([
                    'contribution_id' => $id,
                    'receipt_number' => $receiptModel->generateNumber(),
                    'issued_date' => date('Y-m-d'),
                    'issued_by' => Auth::id(),
                ]);
            }

            $audit = new AuditLog();
            $audit->log(Auth::id(), 'contribution.approve', 'contributions', $id, "Approved contribution GHS {$contribution->amount}");

            $this->notifyMember($contribution->user_id, (array)$contribution, $receiptId);

            $_SESSION['success'] = 'Contribution approved and receipt generated';
        } else {
            $_SESSION['error'] = 'Contribution not found or already completed';
        }

        $this->redirect('/contributions');
    }

    public function receipts()
    {
        $isAdmin = Auth::user()->role === 'admin';
        $receiptModel = new Receipt();

        $from = $_GET['from'] ?? date('Y-01-01');
        $to = $_GET['to'] ?? date('Y-m-t');

        if ($isAdmin) {
            if (!empty($_GET['user_id'])) {
                $receipts = $receiptModel->getByPeriod((int)$_GET['user_id'], $from, $to);
            } else {
                $allReceipts = $receiptModel->all();
                $receipts = $allReceipts;
            }
        } else {
            $receipts = $receiptModel->getByPeriod(Auth::id(), $from, $to);
        }

        $userModel = new User();
        $members = $isAdmin ? $userModel->where('approval_status', 'approved') : [];

        $this->view('contributions/receipts', [
            'receipts' => $receipts ?? [],
            'members' => $members,
            'isAdmin' => $isAdmin,
            'from' => $from,
            'to' => $to,
        ]);
    }

    public function downloadReceipt($params)
    {
        $id = $params['id'] ?? null;
        $receiptModel = new Receipt();
        $receipt = $receiptModel->getWithDetails($id);

        if (!$receipt) {
            $_SESSION['error'] = 'Receipt not found';
            $this->redirect('/receipts');
        }

        if (Auth::user()->role !== 'admin' && Auth::id() != $receipt->user_id) {
            $_SESSION['error'] = 'Unauthorized';
            $this->redirect('/receipts');
        }

        $settingModel = new Setting();
        $companyName = $settingModel->getValue('receipt_company_name', 'Ghana Anaesthesiologists Society');
        $companyDetails = $settingModel->getValue('receipt_company_details', '');

        $receipt->company_name = $companyName;
        $receipt->company_details = $companyDetails;

        $this->view('contributions/receipt_download', ['receipt' => $receipt]);
    }

    public function member()
    {
        $userId = Auth::id();
        $model = new Contribution();

        $from = $_GET['from'] ?? date('Y-01-01');
        $to = $_GET['to'] ?? date('Y-m-t');
        $status = $_GET['status'] ?? '';

        $contributions = $model->getByPeriod($userId, $from, $to, $status ?: null);
        $summary = $model->getMemberSummary($userId);
        $timeline = $model->getTimeline($userId);
        $obligations = $model->getObligations($userId);

        $typeModel = new ContributionType();
        $types = $typeModel->getActive();

        $this->view('contributions/member', [
            'contributions' => $contributions,
            'summary' => $summary,
            'timeline' => $timeline,
            'obligations' => $obligations,
            'types' => $types,
            'from' => $from,
            'to' => $to,
            'status' => $status,
        ]);
    }

    public function arrears()
    {
        if (Auth::user()->role !== 'admin') {
            $_SESSION['error'] = 'Unauthorized';
            $this->redirect('/contributions');
        }

        $search = $_GET['search'] ?? '';
        $model = new Contribution();
        $members = $model->getAllMembersWithArrears($search);

        $totalArrearsAll = array_sum(array_column($members, 'total_arrears'));
        $totalMembers = count($members);

        // If a member ID is specified, show breakdown
        $detailMemberId = $_GET['member_id'] ?? null;
        $detail = null;
        if ($detailMemberId) {
            $detail = $model->getMemberArrearsBreakdown((int)$detailMemberId);
            $userModel = new User();
            $detailMember = $userModel->find((int)$detailMemberId);
        }

        $this->view('contributions/arrears', [
            'members' => $members,
            'search' => $search,
            'totalArrearsAll' => $totalArrearsAll,
            'totalMembers' => $totalMembers,
            'detail' => $detail,
            'detailMember' => $detailMember ?? null,
        ]);
    }

    public function reminderCron()
    {
        $settingModel = new Setting();
        $enabled = $settingModel->getValue('payment_reminder_enabled', 'true');
        if ($enabled !== 'true') {
            echo "Payment reminders disabled\n";
            return;
        }

        $frequency = $settingModel->getValue('payment_reminder_frequency', 'weekly');
        $graceDays = (int)$settingModel->getValue('payment_reminder_grace_days', 5);

        $today = new \DateTime();
        $currentDay = (int)$today->format('j');
        $reminderDay = (int)$settingModel->getValue('payment_reminder_day', 1);

        if ($frequency === 'weekly' && $today->format('N') != $reminderDay) {
            echo "Not reminder day (today is day {$today->format('N')}, reminder day is {$reminderDay})\n";
            return;
        }
        if ($frequency === 'monthly' && $currentDay != $reminderDay) {
            echo "Not reminder day (today is {$currentDay}, reminder day is {$reminderDay})\n";
            return;
        }

        $year = (int)$today->format('Y');
        $month = (int)$today->format('m');
        $checkMonth = $month;
        if ($currentDay <= $graceDays && $month > 1) {
            $checkMonth = $month - 1;
        }

        $model = new Contribution();
        $unpaid = $model->getUnpaidAndPartialMembers($year, $checkMonth);

        if (empty($unpaid)) {
            echo "All members have paid for {$year}-{$checkMonth}\n";
            return;
        }

        $subject = $settingModel->getValue('payment_reminder_subject', 'Payment Reminder - Membership Contribution');
        $messageTemplate = $settingModel->getValue('payment_reminder_message', '');
        $monthName = date('F Y', strtotime("{$year}-{$checkMonth}-01"));

        $sent = 0;
        foreach ($unpaid as $member) {
            $memberName = $member->first_name ? trim($member->first_name . ' ' . $member->last_name) : $member->name;
            $balanceInfo = '';
            if ($member->balance > 0) {
                $balanceInfo = " (Balance: GHS " . number_format($member->balance, 2) . ")";
            }
            $body = str_replace(
                ['{name}', '{month}', '{email}', '{amount}'],
                [$memberName, $monthName, $member->email, $balanceInfo],
                $messageTemplate
            );
            $body = nl2br(htmlspecialchars($body));

            $htmlBody = "<div style='font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;'>";
            $htmlBody .= "<div style='background: #4e73df; color: white; padding: 20px; border-radius: 10px 10px 0 0; text-align: center;'><h2 style='margin: 0;'>Payment Reminder</h2></div>";
            $htmlBody .= "<div style='border: 1px solid #e0e0e0; border-top: none; padding: 30px; border-radius: 0 0 10px 10px;'>";
            $htmlBody .= $body;
            if ($member->balance > 0) {
                $htmlBody .= "<p style='margin-top: 15px; padding: 10px; background: #fff3cd; border: 1px solid #ffc107; border-radius: 5px;'>Your outstanding balance is <strong>GHS " . number_format($member->balance, 2) . "</strong></p>";
            }
            $htmlBody .= "<p style='margin-top: 20px;'><a href='" . Url::to('/contributions/member') . "' style='display: inline-block; padding: 12px 30px; background: linear-gradient(135deg, #4e73df, #224abe); color: white; text-decoration: none; border-radius: 8px;'>View My Contributions</a></p>";
            $htmlBody .= "</div></div>";

            Mailer::send($member->email, $subject, $htmlBody);

            $balanceLine = $member->balance > 0 ? " (outstanding balance: GHS " . number_format($member->balance, 2) . ")" : "";
            $this->db->prepare("INSERT INTO messages (sender_id, subject, body) VALUES (1, ?, ?)")->execute([$subject, "Dear {$memberName},\n\nThis is a reminder that your contribution for {$monthName} has not been fully paid yet{$balanceLine}.\n\nPlease log in to the portal to make your payment."]);
            $msgId = $this->db->lastInsertId();
            $this->db->prepare("INSERT INTO message_recipients (message_id, recipient_id) VALUES (?, ?)")->execute([$msgId, $member->id]);

            $sent++;
        }

        echo "Sent {$sent} payment reminders for {$monthName}\n";
    }

    private function notifyMember($userId, $data, $receiptId = null)
    {
        $userModel = new User();
        $member = $userModel->find($userId);
        if (!$member || !$member->email) return;

        $memberName = $member->first_name ? trim($member->first_name . ' ' . $member->last_name) : $member->name;
        $amount = number_format($data['amount'] ?? 0, 2);
        $balance = !empty($data['balance']) ? (float)$data['balance'] : 0;
        $typeName = '';
        if (!empty($data['contribution_type_id'])) {
            $ct = new ContributionType();
            $type = $ct->find((int)$data['contribution_type_id']);
            $typeName = $type ? $type->name : '';
        }

        $isPartial = $balance > 0;
        $subject = $isPartial ? "Partial Payment Received - GHS {$amount}" : "Payment Confirmation - GHS {$amount}";

        $receiptHtml = '';
        $receiptObj = null;
        if ($receiptId) {
            $receiptModel = new Receipt();
            $receiptObj = $receiptModel->find($receiptId);
            if ($receiptObj) {
                $receiptHtml = "<p><strong>Receipt:</strong> {$receiptObj->receipt_number}</p>";
            }
        }

        $accentColor = $isPartial ? '#f6c23e' : '#1cc88a';
        $headerText = $isPartial ? 'Partial Payment Received' : 'Payment Received';

        $body = "<div style='font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;'>";
        $body .= "<div style='background: {$accentColor}; color: white; padding: 20px; border-radius: 10px 10px 0 0; text-align: center;'><h2 style='margin: 0;'>{$headerText}</h2></div>";
        $body .= "<div style='border: 1px solid #e0e0e0; border-top: none; padding: 30px; border-radius: 0 0 10px 10px;'>";
        $body .= "<p>Dear <strong>{$memberName}</strong>,</p>";
        $body .= $isPartial ? "<p>Your partial contribution has been recorded. Please complete the remaining balance.</p>" : "<p>Your contribution has been recorded successfully.</p>";
        $body .= "<table style='width: 100%; border-collapse: collapse; margin: 15px 0;'>";
        $body .= "<tr><td style='padding: 8px; border-bottom: 1px solid #eee; color: #666;'>Amount Paid</td><td style='padding: 8px; border-bottom: 1px solid #eee; font-weight: bold;'>GHS {$amount}</td></tr>";
        if ($balance > 0) {
            $body .= "<tr><td style='padding: 8px; border-bottom: 1px solid #eee; color: #666;'>Remaining Balance</td><td style='padding: 8px; border-bottom: 1px solid #eee; font-weight: bold; color: #e74a3b;'>GHS " . number_format($balance, 2) . "</td></tr>";
        }
        if ($typeName) $body .= "<tr><td style='padding: 8px; border-bottom: 1px solid #eee; color: #666;'>Type</td><td style='padding: 8px; border-bottom: 1px solid #eee;'>{$typeName}</td></tr>";
        $body .= "<tr><td style='padding: 8px; border-bottom: 1px solid #eee; color: #666;'>Date</td><td style='padding: 8px; border-bottom: 1px solid #eee;'>{$data['payment_date']}</td></tr>";
        $body .= $receiptHtml;
        $body .= "</table>";
        $body .= "<p>You can view your full contribution history and download receipts from the member portal.</p>";
        $body .= "<p style='margin-top: 20px;'><a href='" . Url::to('/contributions/member') . "' style='display: inline-block; padding: 12px 30px; background: linear-gradient(135deg, {$accentColor}, #17a673); color: white; text-decoration: none; border-radius: 8px;'>View Contributions</a></p>";
        $body .= "</div></div>";

        Mailer::send($member->email, $subject, $body);

        $receiptNumber = $receiptObj ? $receiptObj->receipt_number : 'Pending';
        $balanceLine = $balance > 0 ? "\nRemaining Balance: GHS " . number_format($balance, 2) : "";
        $this->db->prepare("INSERT INTO messages (sender_id, subject, body) VALUES (1, ?, ?)")->execute([$subject, "Dear {$memberName},\n\nYour contribution of GHS {$amount} has been recorded successfully.\n\nReceipt: {$receiptNumber}{$balanceLine}\n\nLog in to the portal to view details."]);
        $msgId = $this->db->lastInsertId();
        $this->db->prepare("INSERT INTO message_recipients (message_id, recipient_id) VALUES (?, ?)")->execute([$msgId, $userId]);
    }
}
