<?php
require_once __DIR__ . '/../../includes/bootstrap.php';
require_login();

$currentType = strtolower(trim((string)($_SESSION['ss_role_id'] ?? '')));
$canVoidPayments = in_array($currentType, ['master', 'manager', 'accounts', '2'], true);

$pageTitle = 'Payments';

const CC_SURCHARGE_SERVICE_ID = 11;
const CC_SURCHARGE_RATE = 0.03;
const OPTIONAL_GRATUITY_INVENTORY_ITEM_ID = 2133;

if (!function_exists('h')) {
    function h($value): string
    {
        return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
    }
}

if (!function_exists('folio_tx_badge')) {
    function folio_tx_badge(string $type): string
    {
        $type = strtolower(trim($type));

        switch ($type) {
            case 'room_charge':
                return '<span class="badge text-bg-primary">Room Charge</span>';
            case 'payment':
                return '<span class="badge text-bg-success">Payment</span>';
            case 'extra':
                return '<span class="badge text-bg-warning text-dark">Extra</span>';
            case 'cc_charge':
            case 'card_surcharge':
                return '<span class="badge text-bg-info text-dark">CC Charge</span>';
            case 'adjustment':
                return '<span class="badge text-bg-secondary">Adjustment</span>';
            case 'restaurant':
            case 'minibar':
            case 'laundry':
            case 'transfer':
            case 'park_fee':
                return '<span class="badge text-bg-warning text-dark">' . htmlspecialchars(ucwords(str_replace('_', ' ', $type)), ENT_QUOTES, 'UTF-8') . '</span>';
            case 'payment_void':
            case 'void_payment':
            case 'payment_reversal':
            case 'payment_refund':
            case 'refund':
            case 'reversal':
                return '<span class="badge text-bg-danger">Payment Reversal</span>';
            default:
                return '<span class="badge text-bg-light text-dark border">' . htmlspecialchars(ucwords(str_replace('_', ' ', $type)), ENT_QUOTES, 'UTF-8') . '</span>';
        }
    }
}

if (!function_exists('money_fmt')) {
    function money_fmt($amount): string
    {
        return number_format((float)$amount, 2);
    }
}

if (!function_exists('currency_rate_direction_note')) {
    function currency_rate_direction_note(): string
    {
        return 'ROE is locked as Currency per 1 USD. Example: ZAR 18.50, EUR 1.02, GBP 0.85.';
    }
}

if (!function_exists('currency_code_from_row')) {
    function currency_code_from_row(array $row): string
    {
        $code = strtoupper(trim((string)($row['CurrencyCode'] ?? '')));
        if ($code === '') {
            $code = strtoupper(trim((string)($row['Currency'] ?? '')));
        }
        return $code;
    }
}


if (!function_exists('pay_table_exists')) {
    function pay_table_exists(PDO $pdo, string $tableName): bool
    {
        static $cache = [];
        if (array_key_exists($tableName, $cache)) {
            return $cache[$tableName];
        }
        try {
            $stmt = $pdo->prepare("SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = :table_name");
            $stmt->execute([':table_name' => $tableName]);
            $cache[$tableName] = ((int)$stmt->fetchColumn() > 0);
        } catch (Throwable $e) {
            $cache[$tableName] = false;
        }
        return $cache[$tableName];
    }
}

if (!function_exists('pay_column_exists')) {
    function pay_column_exists(PDO $pdo, string $tableName, string $columnName): bool
    {
        static $cache = [];
        $key = $tableName . '.' . $columnName;
        if (array_key_exists($key, $cache)) {
            return $cache[$key];
        }
        try {
            $stmt = $pdo->prepare("SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = :table_name AND COLUMN_NAME = :column_name");
            $stmt->execute([':table_name' => $tableName, ':column_name' => $columnName]);
            $cache[$key] = ((int)$stmt->fetchColumn() > 0);
        } catch (Throwable $e) {
            $cache[$key] = false;
        }
        return $cache[$key];
    }
}

if (!function_exists('pay_column_max_length')) {
    function pay_column_max_length(PDO $pdo, string $tableName, string $columnName, int $fallback = 100): int
    {
        static $cache = [];
        $key = $tableName . '.' . $columnName;
        if (array_key_exists($key, $cache)) {
            return $cache[$key];
        }
        try {
            $stmt = $pdo->prepare("SELECT CHARACTER_MAXIMUM_LENGTH, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = :table_name AND COLUMN_NAME = :column_name LIMIT 1");
            $stmt->execute([':table_name' => $tableName, ':column_name' => $columnName]);
            $row = $stmt->fetch(PDO::FETCH_ASSOC);
            $len = $row ? (int)($row['CHARACTER_MAXIMUM_LENGTH'] ?? 0) : 0;
            $dataType = $row ? strtolower((string)($row['DATA_TYPE'] ?? '')) : '';
            if ($len <= 0 && in_array($dataType, ['text', 'mediumtext', 'longtext'], true)) {
                $len = 65000; // effectively unbounded for our purposes
            }
            $cache[$key] = $len > 0 ? $len : $fallback;
        } catch (Throwable $e) {
            $cache[$key] = $fallback;
        }
        return $cache[$key];
    }
}

if (!function_exists('current_user_id_for_audit')) {
    function current_user_id_for_audit(): ?int
    {
        foreach (['user_id', 'userid', 'id', 'staffid'] as $key) {
            if (!empty($_SESSION[$key]) && (int)$_SESSION[$key] > 0) {
                return (int)$_SESSION[$key];
            }
        }
        return null;
    }
}


if (!function_exists('season_badge_class')) {
    function season_badge_class(?string $color): string
    {
        $allowed = ['primary', 'secondary', 'success', 'danger', 'warning', 'info', 'dark'];
        $color = strtolower(trim((string)$color));
        return in_array($color, $allowed, true) ? $color : 'secondary';
    }
}

if (!function_exists('next_folio_no')) {
    function next_folio_no(PDO $pdo, int $propertyId = 0): string
    {
        do {
            $prefix = 'FOL';
            if ($propertyId > 0) {
                $stmt = $pdo->prepare("SELECT code FROM properties WHERE id = :id LIMIT 1");
                $stmt->execute([':id' => $propertyId]);
                $code = trim((string)($stmt->fetchColumn() ?: ''));
                if ($code !== '') {
                    $prefix = 'FOL-' . strtoupper($code);
                }
            }

            $folioNo = $prefix . '-' . date('YmdHis') . '-' . random_int(100, 999);

            $stmt = $pdo->prepare("
                SELECT COUNT(*)
                FROM folios
                WHERE folio_no = :folio_no
            ");
            $stmt->execute([':folio_no' => $folioNo]);
            $exists = (int)$stmt->fetchColumn() > 0;
        } while ($exists);

        return $folioNo;
    }
}

if (!function_exists('build_room_mix_map')) {
    function build_room_mix_map(PDO $pdo, int $orderId): array
    {
        $stmt = $pdo->prepare("
            SELECT
                rmg.room_type_id,
                rt.title AS room_type_title,
                COUNT(*) AS qty_rooms
            FROM order_rooming rmg
            LEFT JOIN room_types rt ON rt.id = rmg.room_type_id
            WHERE rmg.order_id = :order_id
            GROUP BY rmg.room_type_id, rt.title
            ORDER BY rt.title ASC, rmg.room_type_id ASC
        ");
        $stmt->execute([':order_id' => $orderId]);
        $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

        $parts = [];
        $map = [];
        foreach ($rows as $row) {
            $roomTypeId = (int)($row['room_type_id'] ?? 0);
            $title = trim((string)($row['room_type_title'] ?? ''));
            if ($title === '') {
                $title = 'Room Type #' . $roomTypeId;
            }
            $qty = (int)($row['qty_rooms'] ?? 0);
            $label = $title . ' x ' . $qty;
            $parts[] = $label;
            if ($roomTypeId > 0) {
                $map[$roomTypeId] = $title;
            }
        }

        return [
            'mix' => implode(', ', $parts),
            'map' => $map,
        ];
    }
}

if (!function_exists('get_or_create_master_folio')) {
    function get_or_create_master_folio(PDO $pdo, int $orderId): int
    {
        $stmt = $pdo->prepare("
            SELECT id
            FROM folios
            WHERE order_id = ? AND folio_type = 'master'
            LIMIT 1
        ");
        $stmt->execute([$orderId]);
        $id = (int)($stmt->fetchColumn() ?: 0);
        if ($id > 0) {
            return $id;
        }

        $stmt = $pdo->prepare("
            SELECT property_id, order_no
            FROM orders
            WHERE id = ?
            LIMIT 1
        ");
        $stmt->execute([$orderId]);
        $o = $stmt->fetch(PDO::FETCH_ASSOC);

        if (!$o) {
            throw new RuntimeException('Booking not found while creating master folio.');
        }

        $pdo->prepare("
            INSERT INTO folios
            (
                property_id,
                order_id,
                folio_no,
                folio_name,
                folio_type,
                status,
                total_charges,
                total_payments,
                balance
            )
            VALUES
            (
                ?, ?, ?, ?, 'master', 1, 0, 0, 0
            )
        ")->execute([
            (int)($o['property_id'] ?? 0),
            $orderId,
            'FOL-M-' . $orderId,
            'Master Folio - ' . ($o['order_no'] ?? ('Booking #' . $orderId))
        ]);

        return (int)$pdo->lastInsertId();
    }
}

if (!function_exists('get_room_folios')) {
    function get_room_folios(PDO $pdo, int $orderId): array
    {
        $rows = $pdo->prepare("
            SELECT 
                orm.id AS rooming_id,
                orm.room_id,
                orm.room_type_id,
                rt.title AS room_type_title,
                r.room_no,
                CONCAT(COALESCE(g.firstname,''),' ',COALESCE(g.lastname,'')) AS guest
            FROM order_rooming orm
            LEFT JOIN rooms r ON r.id = orm.room_id
            LEFT JOIN room_types rt ON rt.id = orm.room_type_id
            LEFT JOIN orders o ON o.id = orm.order_id
            LEFT JOIN guests g ON g.id = o.guest_id
            WHERE orm.order_id = ?
            ORDER BY r.room_no ASC, orm.room_no ASC, orm.id ASC
        ");
        $rows->execute([$orderId]);
        return $rows->fetchAll(PDO::FETCH_ASSOC);
    }
}


if (!function_exists('get_guest_folios')) {
    function get_guest_folios(PDO $pdo, int $orderId): array
    {
        $stmt = $pdo->prepare("
            SELECT
                f.id AS folio_id,
                f.order_id,
                f.room_id,
                f.rooming_id,
                f.guest_id,
                f.occupant_id,
                f.occupant_name,
                f.folio_no,
                f.folio_name,
                f.balance,
                r.room_no,
                rt.title AS room_type_title
            FROM folios f
            LEFT JOIN rooms r ON r.id = f.room_id
            LEFT JOIN order_rooming orm ON orm.id = f.rooming_id
            LEFT JOIN room_types rt ON rt.id = orm.room_type_id
            WHERE f.order_id = :order_id
              AND f.folio_type = 'guest'
              AND COALESCE(f.status,1) = 1
            ORDER BY COALESCE(r.room_no, ''), f.occupant_name ASC, f.id ASC
        ");
        $stmt->execute([':order_id' => $orderId]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
}

if (!function_exists('get_payment_target_folio')) {
    function get_payment_target_folio(PDO $pdo, int $orderId, string $postTo, int $masterFolioId): int
    {
        if ($postTo === '' || $postTo === 'master') {
            return $masterFolioId;
        }

        $target = json_decode($postTo, true);
        if (!is_array($target)) {
            throw new RuntimeException('Invalid payment folio selection.');
        }

        $targetType = strtolower(trim((string)($target['target_type'] ?? $target['folio_type'] ?? 'room')));

        if ($targetType === 'guest') {
            $folioId = (int)($target['folio_id'] ?? 0);
            if ($folioId <= 0) {
                throw new RuntimeException('Invalid guest folio selected.');
            }

            $stmt = $pdo->prepare("
                SELECT id
                FROM folios
                WHERE id = :folio_id
                  AND order_id = :order_id
                  AND folio_type = 'guest'
                  AND COALESCE(status,1) = 1
                LIMIT 1
            ");
            $stmt->execute([
                ':folio_id' => $folioId,
                ':order_id' => $orderId,
            ]);

            $validFolioId = (int)($stmt->fetchColumn() ?: 0);
            if ($validFolioId <= 0) {
                throw new RuntimeException('Selected guest folio does not belong to this booking or is inactive.');
            }

            return $validFolioId;
        }

        return get_or_create_room_folio($pdo, $orderId, $target);
    }
}


if (!function_exists('ensure_folio_exists')) {
    function ensure_folio_exists(PDO $pdo, int $orderId): int
    {
        $stmt = $pdo->prepare("
            SELECT id
            FROM folios
            WHERE order_id = :order_id
            LIMIT 1
        ");
        $stmt->execute([':order_id' => $orderId]);
        $folioId = (int)($stmt->fetchColumn() ?: 0);

        if ($folioId > 0) {
            return $folioId;
        }

        $stmt = $pdo->prepare("
            SELECT
                o.id,
                o.property_id,
                o.order_no,
                g.firstname,
                g.lastname
            FROM orders o
            LEFT JOIN guests g ON g.id = o.guest_id
            WHERE o.id = :id
            LIMIT 1
        ");
        $stmt->execute([':id' => $orderId]);
        $order = $stmt->fetch(PDO::FETCH_ASSOC);

        if (!$order) {
            throw new RuntimeException('Booking not found for folio creation.');
        }

        $guestName = trim(($order['firstname'] ?? '') . ' ' . ($order['lastname'] ?? ''));
        if ($guestName === '') {
            $guestName = 'Guest';
        }

        $propertyId = (int)($order['property_id'] ?? 0);
        $folioNo = next_folio_no($pdo, $propertyId);
        $folioName = ($order['order_no'] ?: ('Booking #' . $orderId)) . ' - ' . $guestName;

        $stmt = $pdo->prepare("
            INSERT INTO folios
            (
                property_id,
                order_id,
                folio_no,
                folio_name,
                status,
                total_charges,
                total_payments,
                balance
            )
            VALUES
            (
                :property_id,
                :order_id,
                :folio_no,
                :folio_name,
                1,
                0,
                0,
                0
            )
        ");
        $stmt->execute([
            ':property_id' => $propertyId,
            ':order_id'    => $orderId,
            ':folio_no'    => $folioNo,
            ':folio_name'  => $folioName,
        ]);

        return (int)$pdo->lastInsertId();
    }
}

if (!function_exists('get_or_create_room_folio')) {
    function get_or_create_room_folio(PDO $pdo, int $orderId, array $rooming): int
    {
        $roomingId = (int)($rooming['rooming_id'] ?? 0);
        if ($roomingId <= 0) {
            throw new RuntimeException('Invalid room folio target.');
        }

        $stmt = $pdo->prepare("
            SELECT id
            FROM folios
            WHERE order_id = ? AND rooming_id = ? AND folio_type = 'room'
            LIMIT 1
        ");
        $stmt->execute([$orderId, $roomingId]);
        $id = (int)($stmt->fetchColumn() ?: 0);
        if ($id > 0) {
            return $id;
        }

        $stmt = $pdo->prepare("
            SELECT property_id, order_no
            FROM orders
            WHERE id = ?
            LIMIT 1
        ");
        $stmt->execute([$orderId]);
        $order = $stmt->fetch(PDO::FETCH_ASSOC);

        if (!$order) {
            throw new RuntimeException('Booking not found while creating room folio.');
        }

        $roomNo = trim((string)($rooming['room_no'] ?? ''));
        $guest  = trim((string)($rooming['guest'] ?? ''));
        $roomTypeTitle = trim((string)($rooming['room_type_title'] ?? ''));
        if ($guest === '') {
            $guest = 'Guest';
        }

        $folioNo = 'FOL-R-' . ($roomNo !== '' ? $roomNo : $roomingId) . '-' . $orderId;
        $folioName = 'Room ' . ($roomNo !== '' ? $roomNo : 'N/A') . ' - ' . $guest;
        if ($roomTypeTitle !== '') {
            $folioName .= ' • ' . $roomTypeTitle;
        }

        $pdo->prepare("
            INSERT INTO folios
            (
                order_id,
                property_id,
                folio_no,
                folio_name,
                folio_type,
                room_id,
                rooming_id,
                status,
                total_charges,
                total_payments,
                balance
            )
            VALUES
            (
                ?, ?, ?, ?, 'room', ?, ?, 1, 0, 0, 0
            )
        ")->execute([
            $orderId,
            (int)($order['property_id'] ?? 0),
            $folioNo,
            $folioName,
            (int)($rooming['room_id'] ?? 0),
            $roomingId
        ]);

        return (int)$pdo->lastInsertId();
    }
}

if (!function_exists('recalculate_folio_totals')) {
    function recalculate_folio_totals(PDO $pdo, int $folioId): void
    {
        /*
           IMPORTANT ACCOUNTING RULE
           -------------------------
           A payment void/reversal must NOT reduce booking/accommodation value.

           Correct behaviour:
             - charges = real revenue/charge debits only (room_charge, extra, etc.)
             - payments = net valid payment credits less payment void/reversal debits
             - balance = charges - net payments

           This supports both common void styles:
             1) original payment row/folio transaction marked is_voided = 1
             2) separate reversal row posted with debit > 0 or negative credit
        */
        $stmt = $pdo->prepare("
            SELECT
                COALESCE(SUM(
                    CASE
                        WHEN COALESCE(is_voided, 0) = 0
                             AND LOWER(COALESCE(tx_type, '')) NOT IN (
                                'payment', 'receipt', 'deposit', 'payment_received',
                                'folio_payment', 'guest_payment',
                                'payment_void', 'void_payment', 'payment_reversal',
                                'reversal', 'refund', 'payment_refund'
                             )
                             AND LOWER(COALESCE(source_table, '')) NOT IN (
                                'payment', 'payments', 'receipt', 'receipts'
                             )
                        THEN COALESCE(debit, 0)
                        ELSE 0
                    END
                ), 0) AS total_charges,

                GREATEST(
                    0,
                    COALESCE(SUM(
                        CASE
                            WHEN COALESCE(is_voided, 0) = 0
                                 AND (
                                    LOWER(COALESCE(tx_type, '')) IN (
                                        'payment', 'receipt', 'deposit', 'payment_received',
                                        'folio_payment', 'guest_payment'
                                    )
                                    OR LOWER(COALESCE(source_table, '')) IN (
                                        'payment', 'payments', 'receipt', 'receipts'
                                    )
                                 )
                                 AND COALESCE(credit, 0) > 0
                            THEN COALESCE(credit, 0)

                            WHEN COALESCE(is_voided, 0) = 0
                                 AND (
                                    LOWER(COALESCE(tx_type, '')) IN (
                                        'payment_void', 'void_payment', 'payment_reversal',
                                        'reversal', 'refund', 'payment_refund'
                                    )
                                    OR LOWER(COALESCE(description, '')) LIKE '%void%'
                                    OR LOWER(COALESCE(description, '')) LIKE '%reversal%'
                                    OR LOWER(COALESCE(description, '')) LIKE '%refund%'
                                 )
                                 AND COALESCE(debit, 0) > 0
                            THEN -1 * COALESCE(debit, 0)

                            WHEN COALESCE(is_voided, 0) = 0
                                 AND (
                                    LOWER(COALESCE(tx_type, '')) IN (
                                        'payment_void', 'void_payment', 'payment_reversal',
                                        'reversal', 'refund', 'payment_refund'
                                    )
                                    OR LOWER(COALESCE(description, '')) LIKE '%void%'
                                    OR LOWER(COALESCE(description, '')) LIKE '%reversal%'
                                    OR LOWER(COALESCE(description, '')) LIKE '%refund%'
                                 )
                                 AND COALESCE(credit, 0) < 0
                            THEN COALESCE(credit, 0)

                            ELSE 0
                        END
                    ), 0)
                ) AS total_payments
            FROM folio_transactions
            WHERE folio_id = :folio_id
        ");
        $stmt->execute([':folio_id' => $folioId]);
        $totals = $stmt->fetch(PDO::FETCH_ASSOC);

        $charges  = (float)($totals['total_charges'] ?? 0);
        $payments = (float)($totals['total_payments'] ?? 0);
        $balance  = $charges - $payments;

        $stmt = $pdo->prepare("
            UPDATE folios
            SET total_charges = :total_charges,
                total_payments = :total_payments,
                balance = :balance,
                updated_at = NOW()
            WHERE id = :id
        ");
        $stmt->execute([
            ':total_charges'  => round($charges, 2),
            ':total_payments' => round($payments, 2),
            ':balance'        => round($balance, 2),
            ':id'             => $folioId,
        ]);
    }
}

if (!function_exists('sync_order_payment_status')) {
    function sync_order_payment_status(PDO $pdo, int $orderId): void
    {
        $stmt = $pdo->prepare("
            SELECT
                COALESCE(SUM(total_charges), 0) AS total_charges,
                COALESCE(SUM(total_payments), 0) AS total_payments,
                COALESCE(SUM(balance), 0) AS total_balance
            FROM folios
            WHERE order_id = :order_id
        ");
        $stmt->execute([':order_id' => $orderId]);
        $folio = $stmt->fetch(PDO::FETCH_ASSOC);

        $charges  = (float)($folio['total_charges'] ?? 0);
        $payments = (float)($folio['total_payments'] ?? 0);
        $balance  = (float)($folio['total_balance'] ?? 0);

        if ($charges <= 0.00001 || $payments <= 0.00001) {
            $status = 0;
        } elseif ($balance <= 0.00001) {
            $status = 2;
        } else {
            $status = 1;
        }

        $stmt = $pdo->prepare("
            UPDATE orders
            SET payment_status = :payment_status
            WHERE id = :id
        ");
        $stmt->execute([
            ':payment_status' => $status,
            ':id'             => $orderId,
        ]);
    }
}



if (!function_exists('folio_move_label')) {
    function folio_move_label(array $folio): string
    {
        $type = strtolower(trim((string)($folio['folio_type'] ?? '')));
        $name = trim((string)($folio['folio_name'] ?? ''));
        $room = trim((string)($folio['room_no'] ?? ''));
        $occupant = trim((string)($folio['occupant_name'] ?? ''));

        if ($type === 'master') {
            return 'Master Folio' . ($name !== '' ? ' - ' . $name : '');
        }
        if ($type === 'room') {
            return 'Room Folio' . ($room !== '' ? ' - Room ' . $room : '') . ($name !== '' ? ' - ' . $name : '');
        }
        if ($type === 'guest') {
            return 'Guest Folio' . ($occupant !== '' ? ' - ' . $occupant : '') . ($name !== '' ? ' - ' . $name : '');
        }
        return ucwords(str_replace('_', ' ', $type ?: 'Folio')) . ($name !== '' ? ' - ' . $name : '');
    }
}

if (!function_exists('get_booking_folios_for_move')) {
    function get_booking_folios_for_move(PDO $pdo, int $orderId): array
    {
        $stmt = $pdo->prepare("\n            SELECT\n                f.id,\n                f.order_id,\n                f.folio_no,\n                f.folio_name,\n                f.folio_type,\n                f.room_id,\n                f.rooming_id,\n                f.occupant_name,\n                f.balance,\n                r.room_no\n            FROM folios f\n            LEFT JOIN rooms r ON r.id = f.room_id\n            WHERE f.order_id = :order_id\n              AND COALESCE(f.status,1) = 1\n            ORDER BY\n                CASE f.folio_type\n                    WHEN 'master' THEN 1\n                    WHEN 'room' THEN 2\n                    WHEN 'guest' THEN 3\n                    ELSE 9\n                END,\n                r.room_no ASC, f.id ASC\n        ");
        $stmt->execute([':order_id' => $orderId]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
}

if (!function_exists('write_payment_audit_log_safe')) {
    function write_payment_audit_log_safe(PDO $pdo, int $orderId, string $actionType, string $reason, array $payload): void
    {
        if (!pay_table_exists($pdo, 'payment_audit_log')) {
            return;
        }

        $cols = ['order_id', 'action_type', 'action_reason', 'payload_json'];
        $vals = [':order_id', ':action_type', ':action_reason', ':payload_json'];
        $params = [
            ':order_id' => $orderId,
            ':action_type' => $actionType,
            ':action_reason' => $reason,
            ':payload_json' => json_encode($payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES),
        ];

        if (pay_column_exists($pdo, 'payment_audit_log', 'action_by')) {
            $cols[] = 'action_by';
            $vals[] = ':action_by';
            $params[':action_by'] = current_user_id_for_audit();
        }
        if (pay_column_exists($pdo, 'payment_audit_log', 'action_at')) {
            $cols[] = 'action_at';
            $vals[] = 'NOW()';
        }

        $sql = 'INSERT INTO payment_audit_log (' . implode(',', $cols) . ') VALUES (' . implode(',', $vals) . ')';
        $stmt = $pdo->prepare($sql);
        $stmt->execute($params);
    }
}

if (!function_exists('sync_source_folio_reference_if_present')) {
    function sync_source_folio_reference_if_present(PDO $pdo, array $tx, int $newFolioId): void
    {
        $sourceTable = trim((string)($tx['source_table'] ?? ''));
        $sourceId = (int)($tx['source_id'] ?? 0);
        if ($sourceTable === '' || $sourceId <= 0) {
            return;
        }

        // Whitelist real application source tables only. Do not build SQL from arbitrary values.
        $allowed = ['payment', 'payments', 'extras', 'folio_extras', 'order_extras', 'tblbookingdetail', 'pos_orders', 'pos_order_items'];
        if (!in_array($sourceTable, $allowed, true)) {
            return;
        }
        if (!pay_table_exists($pdo, $sourceTable) || !pay_column_exists($pdo, $sourceTable, 'folio_id')) {
            return;
        }

        $stmt = $pdo->prepare("UPDATE `$sourceTable` SET folio_id = :folio_id WHERE id = :id");
        $stmt->execute([':folio_id' => $newFolioId, ':id' => $sourceId]);
    }
}

if (!function_exists('move_folio_transaction')) {
    function move_folio_transaction(PDO $pdo, int $orderId, int $transactionId, int $targetFolioId, string $reason): void
    {
        if ($transactionId <= 0) {
            throw new RuntimeException('Invalid transaction selected.');
        }
        if ($targetFolioId <= 0) {
            throw new RuntimeException('Please select the destination folio.');
        }
        if (trim($reason) === '') {
            throw new RuntimeException('Please enter a reason for moving this transaction.');
        }

        $stmt = $pdo->prepare("\n            SELECT\n                ft.*,\n                f.folio_name AS old_folio_name,\n                f.folio_type AS old_folio_type,\n                f.order_id AS old_order_id\n            FROM folio_transactions ft\n            INNER JOIN folios f ON f.id = ft.folio_id\n            WHERE ft.id = :id\n              AND ft.order_id = :order_id\n            LIMIT 1\n            FOR UPDATE\n        ");
        $stmt->execute([':id' => $transactionId, ':order_id' => $orderId]);
        $tx = $stmt->fetch(PDO::FETCH_ASSOC);
        if (!$tx) {
            throw new RuntimeException('Selected folio transaction was not found for this booking.');
        }

        $oldFolioId = (int)$tx['folio_id'];
        if ($oldFolioId === $targetFolioId) {
            throw new RuntimeException('The selected transaction is already on that folio.');
        }

        if (!empty($tx['is_voided'])) {
            throw new RuntimeException('Voided transactions cannot be moved.');
        }

        $txType = strtolower(trim((string)($tx['tx_type'] ?? '')));
        if ($txType === 'room_charge') {
            throw new RuntimeException('Room charges cannot be moved here. Rebuild or correct room allocations instead.');
        }

        $stmt = $pdo->prepare("\n            SELECT *\n            FROM folios\n            WHERE id = :folio_id\n              AND order_id = :order_id\n              AND COALESCE(status,1) = 1\n            LIMIT 1\n            FOR UPDATE\n        ");
        $stmt->execute([':folio_id' => $targetFolioId, ':order_id' => $orderId]);
        $targetFolio = $stmt->fetch(PDO::FETCH_ASSOC);
        if (!$targetFolio) {
            throw new RuntimeException('Destination folio does not belong to this booking or is inactive.');
        }

        $stmt = $pdo->prepare("\n            UPDATE folio_transactions\n            SET folio_id = :new_folio_id\n            WHERE id = :id\n              AND order_id = :order_id\n        ");
        $stmt->execute([
            ':new_folio_id' => $targetFolioId,
            ':id' => $transactionId,
            ':order_id' => $orderId,
        ]);

        sync_source_folio_reference_if_present($pdo, $tx, $targetFolioId);

        recalculate_folio_totals($pdo, $oldFolioId);
        recalculate_folio_totals($pdo, $targetFolioId);
        sync_order_payment_status($pdo, $orderId);

        write_payment_audit_log_safe($pdo, $orderId, 'move_folio_transaction', $reason, [
            'transaction_id' => $transactionId,
            'tx_type' => $tx['tx_type'] ?? '',
            'description' => $tx['description'] ?? '',
            'debit' => $tx['debit'] ?? 0,
            'credit' => $tx['credit'] ?? 0,
            'source_table' => $tx['source_table'] ?? '',
            'source_id' => $tx['source_id'] ?? '',
            'from_folio_id' => $oldFolioId,
            'from_folio_name' => $tx['old_folio_name'] ?? '',
            'to_folio_id' => $targetFolioId,
            'to_folio_name' => $targetFolio['folio_name'] ?? '',
        ]);
    }
}

if (!function_exists('calculate_saved_room_charge_total')) {
    function calculate_saved_room_charge_total(
        int $adults,
        int $children,
        float $sharingRate,
        float $singleRate,
        float $singleSupplement,
        float $childRatePercent
    ): array {
        $adults = max(0, $adults);
        $children = max(0, $children);
        $childRatePercent = max(0, $childRatePercent);

        if ($adults <= 0) {
            return [
                'adult_total'  => 0.00,
                'child_total'  => 0.00,
                'room_total'   => 0.00,
                'rate_type'    => 'Empty',
            ];
        }

        if ($adults === 1) {
            $adultTotal = $singleRate > 0
                ? $singleRate
                : ($sharingRate + $singleSupplement);

            $rateType = $singleRate > 0 ? 'Single' : 'Sharing + Supplement';
        } else {
            $adultTotal = $adults * $sharingRate;
            $rateType = 'Sharing';
        }

        $childRate  = $sharingRate * ($childRatePercent / 100);
        $childTotal = $children * $childRate;
        $roomTotal  = $adultTotal + $childTotal;

        return [
            'adult_total'  => round($adultTotal, 2),
            'child_total'  => round($childTotal, 2),
            'room_total'   => round($roomTotal, 2),
            'rate_type'    => $rateType,
        ];
    }
}

if (!function_exists('ensure_room_charges_posted_to_folio')) {
    function ensure_room_charges_posted_to_folio(PDO $pdo, int $orderId, int $folioId): void
    {
        $stmt = $pdo->prepare("
            SELECT COUNT(*)
            FROM folio_transactions
            WHERE folio_id = :folio_id
              AND tx_type = 'room_charge'
        ");
        $stmt->execute([':folio_id' => $folioId]);
        $exists = (int)$stmt->fetchColumn();

        if ($exists > 0) {
            return;
        }

        $stmt = $pdo->prepare("
            SELECT
                property_id,
                date,
                sharing_rate,
                single_rate,
                single_supplement,
                child_rate_percent,
                rate_plan_id,
                rate_plan_title,
                rate_plan_currency,
                rate_plan_season_label,
                rate_plan_season_color
            FROM rel_orders_prices
            WHERE order_id = :order_id
            ORDER BY date ASC
        ");
        $stmt->execute([':order_id' => $orderId]);
        $nightRows = $stmt->fetchAll(PDO::FETCH_ASSOC);

        if (!$nightRows) {
            return;
        }

        $stmt = $pdo->prepare("
            SELECT
                property_id,
                room_no,
                room_type_id,
                adults,
                children
            FROM order_rooming
            WHERE order_id = :order_id
            ORDER BY room_no ASC
        ");
        $stmt->execute([':order_id' => $orderId]);
        $roomingRows = $stmt->fetchAll(PDO::FETCH_ASSOC);

        if (!$roomingRows) {
            return;
        }

        $roomTypeMap = build_room_mix_map($pdo, $orderId)['map'];

        $propertyId = 0;
        if (!empty($nightRows[0]['property_id'])) {
            $propertyId = (int)$nightRows[0]['property_id'];
        } elseif (!empty($roomingRows[0]['property_id'])) {
            $propertyId = (int)$roomingRows[0]['property_id'];
        }

        $insert = $pdo->prepare("
            INSERT INTO folio_transactions
            (
                property_id,
                folio_id,
                order_id,
                tx_date,
                tx_type,
                description,
                debit,
                credit,
                source_table,
                source_id
            )
            VALUES
            (
                :property_id,
                :folio_id,
                :order_id,
                :tx_date,
                :tx_type,
                :description,
                :debit,
                :credit,
                :source_table,
                :source_id
            )
        ");

        $seq = 1;

        foreach ($nightRows as $night) {
            $stayDate = (string)$night['date'];
            $sharingRate = (float)$night['sharing_rate'];
            $singleRate = (float)$night['single_rate'];
            $singleSupplement = (float)$night['single_supplement'];
            $childRatePercent = (float)$night['child_rate_percent'];

            $ratePlanTitle = trim((string)($night['rate_plan_title'] ?? ''));
            $ratePlanCurrency = trim((string)($night['rate_plan_currency'] ?? ''));
            $ratePlanSeason = trim((string)($night['rate_plan_season_label'] ?? ''));
            $ratePlanId = (int)($night['rate_plan_id'] ?? 0);

            foreach ($roomingRows as $slot) {
                $roomNo = (int)$slot['room_no'];
                $adults = (int)$slot['adults'];
                $children = (int)$slot['children'];
                $roomTypeTitle = trim((string)($roomTypeMap[(int)($slot['room_type_id'] ?? 0)] ?? ''));

                $calc = calculate_saved_room_charge_total(
                    $adults,
                    $children,
                    $sharingRate,
                    $singleRate,
                    $singleSupplement,
                    $childRatePercent
                );

                $roomCharge = (float)$calc['room_total'];

                if ($roomCharge <= 0) {
                    continue;
                }

                $descParts = [];
                $slotLabel = 'Room Slot ' . $roomNo;
                if ($roomTypeTitle !== '') {
                    $slotLabel .= ' (' . $roomTypeTitle . ')';
                }
                $descParts[] = $slotLabel . ' charge for ' . $stayDate;

                if ($ratePlanTitle !== '') {
                    $planText = $ratePlanTitle;
                    if ($ratePlanCurrency !== '') {
                        $planText .= ' (' . $ratePlanCurrency . ')';
                    }
                    if ($ratePlanSeason !== '') {
                        $planText .= ' - ' . $ratePlanSeason;
                    }
                    if ($ratePlanId > 0) {
                        $planText .= ' [Plan #' . $ratePlanId . ']';
                    }
                    $descParts[] = $planText;
                }

                $descParts[] = $calc['rate_type'];

                if ($adults > 0 || $children > 0) {
                    $descParts[] = $adults . 'A' . ($children > 0 ? '/' . $children . 'C' : '');
                }

                $desc = implode(' | ', $descParts);

                $insert->execute([
                    ':property_id'  => $propertyId,
                    ':folio_id'     => $folioId,
                    ':order_id'     => $orderId,
                    ':tx_date'      => $stayDate . ' 00:00:00',
                    ':tx_type'      => 'room_charge',
                    ':description'  => $desc,
                    ':debit'        => $roomCharge,
                    ':credit'       => 0,
                    ':source_table' => 'rel_orders_prices',
                    ':source_id'    => $seq,
                ]);

                $seq++;
            }
        }
    }
}

if (!function_exists('rebuild_folio_room_charges')) {
    function rebuild_folio_room_charges(PDO $pdo, int $orderId, int $folioId): void
    {
        $stmt = $pdo->prepare("
            DELETE FROM folio_transactions
            WHERE folio_id = :folio_id
              AND tx_type = 'room_charge'
        ");
        $stmt->execute([':folio_id' => $folioId]);

        ensure_room_charges_posted_to_folio($pdo, $orderId, $folioId);
        recalculate_folio_totals($pdo, $folioId);
        sync_order_payment_status($pdo, $orderId);
    }
}


if (!function_exists('is_card_surcharge_method')) {
    function is_card_surcharge_method(string $method): bool
    {
        $normalized = strtolower(trim($method));
        return in_array($normalized, ['pos', 'card'], true);
    }
}


if (!function_exists('is_bank_payment_method')) {
    function is_bank_payment_method(string $method): bool
    {
        $normalized = strtolower(trim($method));
        return strpos($normalized, 'bank') !== false || strpos($normalized, 'transfer') !== false;
    }
}

if (!function_exists('fetch_payment_bank_accounts')) {
    function fetch_payment_bank_accounts(PDO $pdo, int $propertyId = 0): array
    {
        try {
            $sql = "
                SELECT
                    b.ID,
                    b.property_id,
                    b.AccountName,
                    b.Bank,
                    b.Branch,
                    b.AccountNo,
                    b.Currency,
                    cur.CurrencyCode,
                    p.name AS property_name
                FROM tblbankdetails b
                LEFT JOIN tblcurrency cur ON cur.CurrencyID = b.Currency
                LEFT JOIN properties p ON p.id = b.property_id
                WHERE b.bank_status = 1
            ";
            $params = [];

            if ($propertyId > 0) {
                $sql .= " AND (b.property_id IS NULL OR b.property_id = 0 OR b.property_id = :property_id) ";
                $params[':property_id'] = $propertyId;
            }

            $sql .= " ORDER BY COALESCE(p.name, 'General') ASC, b.Bank ASC, b.AccountName ASC ";
            $stmt = $pdo->prepare($sql);
            $stmt->execute($params);
            return $stmt->fetchAll(PDO::FETCH_ASSOC);
        } catch (Throwable $e) {
            return [];
        }
    }
}

if (!function_exists('fetch_payment_pos_machines')) {
    function fetch_payment_pos_machines(PDO $pdo, int $propertyId = 0): array
    {
        try {
            $sql = "
                SELECT
                    pm.id,
                    pm.bank_detail_id,
                    pm.machine_name,
                    pm.terminal_id,
                    pm.merchant_id,
                    pm.provider,
                    pm.location,
                    b.Bank,
                    b.AccountName,
                    b.AccountNo,
                    b.property_id,
                    cur.CurrencyCode,
                    p.name AS property_name
                FROM pos_machines pm
                INNER JOIN tblbankdetails b ON b.ID = pm.bank_detail_id
                LEFT JOIN tblcurrency cur ON cur.CurrencyID = b.Currency
                LEFT JOIN properties p ON p.id = b.property_id
                WHERE pm.status = 1
                  AND b.bank_status = 1
            ";
            $params = [];

            if ($propertyId > 0) {
                $sql .= " AND (b.property_id IS NULL OR b.property_id = 0 OR b.property_id = :property_id) ";
                $params[':property_id'] = $propertyId;
            }

            $sql .= " ORDER BY COALESCE(p.name, 'General') ASC, pm.machine_name ASC, pm.terminal_id ASC ";
            $stmt = $pdo->prepare($sql);
            $stmt->execute($params);
            return $stmt->fetchAll(PDO::FETCH_ASSOC);
        } catch (Throwable $e) {
            return [];
        }
    }
}

if (!function_exists('post_cc_surcharge_to_folio')) {
    function post_cc_surcharge_to_folio(PDO $pdo, int $orderId, int $propertyId, int $folioId, string $paymentDate, float $surchargeUsd, float $surchargeEntered, float $roeValue, string $paymentMethod): void
    {
        if ($surchargeUsd <= 0) {
            return;
        }

        $serviceId = CC_SURCHARGE_SERVICE_ID;
        $serviceName = 'CC Charge';
        $categoryLabel = 'Extra';

        try {
            $stmtService = $pdo->prepare("\n                SELECT\n                    s.service,\n                    s.price_per_person,\n                    c.category AS category_name\n                FROM tblservices s\n                LEFT JOIN tblservicecategory c ON c.id = s.category\n                WHERE s.id = :id\n                LIMIT 1\n            ");
            $stmtService->execute([':id' => $serviceId]);
            $svc = $stmtService->fetch(PDO::FETCH_ASSOC);
            if ($svc) {
                $serviceName = trim((string)($svc['service'] ?? '')) ?: $serviceName;
                $categoryLabel = trim((string)($svc['category_name'] ?? '')) ?: $categoryLabel;
            }
        } catch (Throwable $e) {
            // Do not block payment if the service label cannot be loaded; source_id still references service 11.
        }

        $txType = strtolower(preg_replace('/[^a-zA-Z0-9]+/', '_', $categoryLabel));
        $txType = trim($txType, '_');
        if ($txType === '') {
            $txType = 'cc_charge';
        }

        $description = $serviceName
            . ' - 3% surcharge for ' . $paymentMethod
            . ' | Charged ' . number_format($surchargeEntered, 2, '.', '')
            . ' / ROE ' . number_format($roeValue, 5, '.', '')
            . ' = USD ' . number_format($surchargeUsd, 2, '.', '');

        $stmt = $pdo->prepare("\n            INSERT INTO folio_transactions\n            (\n                property_id,\n                folio_id,\n                order_id,\n                tx_date,\n                tx_type,\n                description,\n                debit,\n                credit,\n                source_table,\n                source_id\n            )\n            VALUES\n            (\n                :property_id,\n                :folio_id,\n                :order_id,\n                :tx_date,\n                :tx_type,\n                :description,\n                :debit,\n                0,\n                'tblservices',\n                :source_id\n            )\n        ");
        $stmt->execute([
            ':property_id' => $propertyId,
            ':folio_id'    => $folioId,
            ':order_id'    => $orderId,
            ':tx_date'     => $paymentDate . ' 00:00:00',
            ':tx_type'     => $txType,
            ':description' => $description,
            ':debit'       => round($surchargeUsd, 2),
            ':source_id'   => $serviceId,
        ]);
    }
}


if (!function_exists('post_optional_gratuity_to_folio')) {
    function post_optional_gratuity_to_folio(PDO $pdo, int $orderId, int $propertyId, int $folioId, string $paymentDate, float $gratuityUsd, float $gratuityEntered, float $roeValue): void
    {
        if ($gratuityUsd <= 0) {
            return;
        }

        $itemName = 'Optional Gratuity';
        try {
            if (pay_table_exists($pdo, 'inventory_items')) {
                $nameColumns = ['item_name', 'name', 'description', 'item_description', 'product_name'];
                $nameColumn = '';
                foreach ($nameColumns as $col) {
                    if (pay_column_exists($pdo, 'inventory_items', $col)) {
                        $nameColumn = $col;
                        break;
                    }
                }

                if ($nameColumn !== '') {
                    $stmtItem = $pdo->prepare("SELECT `{$nameColumn}` FROM inventory_items WHERE id = :id LIMIT 1");
                    $stmtItem->execute([':id' => OPTIONAL_GRATUITY_INVENTORY_ITEM_ID]);
                    $loadedName = trim((string)$stmtItem->fetchColumn());
                    if ($loadedName !== '') {
                        $itemName = $loadedName;
                    }
                }
            }
        } catch (Throwable $e) {
            $itemName = 'Optional Gratuity';
        }

        $description = $itemName
            . ' | Optional guest gratuity'
            . ' | Entered ' . number_format($gratuityEntered, 2, '.', '')
            . ' / ROE ' . number_format($roeValue, 5, '.', '')
            . ' = USD ' . number_format($gratuityUsd, 2, '.', '')
            . ' | No POS/Card surcharge applied';

        $stmt = $pdo->prepare("\n            INSERT INTO folio_transactions\n            (\n                property_id,\n                folio_id,\n                order_id,\n                tx_date,\n                tx_type,\n                description,\n                debit,\n                credit,\n                source_table,\n                source_id\n            )\n            VALUES\n            (\n                :property_id,\n                :folio_id,\n                :order_id,\n                :tx_date,\n                'gratuity',\n                :description,\n                :debit,\n                0,\n                'inventory_items',\n                :source_id\n            )\n        ");
        $stmt->execute([
            ':property_id' => $propertyId,
            ':folio_id'    => $folioId,
            ':order_id'    => $orderId,
            ':tx_date'     => $paymentDate . ' 00:00:00',
            ':description' => $description,
            ':debit'       => round($gratuityUsd, 2),
            ':source_id'   => OPTIONAL_GRATUITY_INVENTORY_ITEM_ID,
        ]);
    }
}

if (!function_exists('post_payment_to_folio')) {
    function post_payment_to_folio(PDO $pdo, int $paymentId, int $folioId): void
    {
        $stmt = $pdo->prepare("
            SELECT
                p.*,
                b.Bank AS linked_bank_name,
                b.AccountName AS linked_bank_account_name,
                b.AccountNo AS linked_bank_account_no,
                pm.machine_name AS linked_pos_machine_name,
                pm.terminal_id AS linked_pos_terminal_id
            FROM payment p
            LEFT JOIN tblbankdetails b ON b.ID = p.bank_account_id
            LEFT JOIN pos_machines pm ON pm.id = p.pos_machine_id
            WHERE p.id = :id
            LIMIT 1
        ");
        $stmt->execute([':id' => $paymentId]);
        $payment = $stmt->fetch(PDO::FETCH_ASSOC);

        if (!$payment) {
            throw new RuntimeException('Payment not found.');
        }

        $check = $pdo->prepare("
            SELECT COUNT(*)
            FROM folio_transactions
            WHERE source_table = 'payment'
              AND source_id = :source_id
              AND folio_id = :folio_id
        ");
        $check->execute([
            ':source_id' => $paymentId,
            ':folio_id'  => $folioId,
        ]);

        if ((int)$check->fetchColumn() > 0) {
            return;
        }

        $method = trim((string)($payment['payment_method'] ?? 'Payment'));
        $methodExtra = '';
        if (!empty($payment['linked_bank_name'])) {
            $methodExtra .= ' | Bank: ' . trim((string)$payment['linked_bank_name']);
            if (!empty($payment['linked_bank_account_name'])) {
                $methodExtra .= ' - ' . trim((string)$payment['linked_bank_account_name']);
            }
            if (!empty($payment['linked_bank_account_no'])) {
                $methodExtra .= ' (' . trim((string)$payment['linked_bank_account_no']) . ')';
            }
        }
        if (!empty($payment['linked_pos_machine_name'])) {
            $methodExtra .= ' | POS Machine: ' . trim((string)$payment['linked_pos_machine_name']);
            if (!empty($payment['linked_pos_terminal_id'])) {
                $methodExtra .= ' / Terminal ' . trim((string)$payment['linked_pos_terminal_id']);
            }
        }
        $roeText = '';
        if (!empty($payment['roe_atm']) && (float)$payment['roe_atm'] > 0) {
            $roeText = ' | USD base = Amount / ROE ' . number_format((float)$payment['roe_atm'], 5, '.', '');
        }

        $txDate = !empty($payment['date_time'])
            ? (string)$payment['date_time']
            : (!empty($payment['added_date']) ? $payment['added_date'] . ' 00:00:00' : date('Y-m-d H:i:s'));

        $stmt = $pdo->prepare("
            INSERT INTO folio_transactions
            (
                property_id,
                folio_id,
                order_id,
                tx_date,
                tx_type,
                description,
                debit,
                credit,
                source_table,
                source_id
            )
            VALUES
            (
                :property_id,
                :folio_id,
                :order_id,
                :tx_date,
                :tx_type,
                :description,
                :debit,
                :credit,
                :source_table,
                :source_id
            )
        ");
        $stmt->execute([
            ':property_id'  => (int)($payment['property_id'] ?? 0),
            ':folio_id'     => $folioId,
            ':order_id'     => (int)$payment['order_id'],
            ':tx_date'      => $txDate,
            ':tx_type'      => 'payment',
            ':description'  => 'Payment received - ' . $method . $methodExtra . $roeText,
            ':debit'        => 0,
            ':credit'       => round((float)$payment['amount'], 2),
            ':source_table' => 'payment',
            ':source_id'    => $paymentId,
        ]);
    }
}

if (!function_exists('get_booking_folios_with_transactions')) {
    function get_booking_folios_with_transactions(PDO $pdo, int $orderId): array
    {
        $roomTypeMap = build_room_mix_map($pdo, $orderId)['map'];

        $stmt = $pdo->prepare("
            SELECT
                f.*,
                r.room_no,
                orm.room_type_id,
                CASE
                    WHEN f.folio_type = 'master' THEN 'Master Folio'
                    WHEN f.folio_type = 'room' THEN CONCAT('Room ', COALESCE(r.room_no, 'N/A'))
                    WHEN f.folio_type = 'guest' THEN CONCAT('Guest Folio', CASE WHEN COALESCE(f.occupant_name,'') <> '' THEN CONCAT(' • ', f.occupant_name) ELSE '' END)
                    ELSE CONCAT(UCASE(LEFT(f.folio_type,1)), SUBSTRING(f.folio_type,2), ' Folio')
                END AS folio_label
            FROM folios f
            LEFT JOIN rooms r ON r.id = f.room_id
            LEFT JOIN order_rooming orm ON orm.id = f.rooming_id
            WHERE f.order_id = :order_id
            ORDER BY
                CASE f.folio_type
                    WHEN 'master' THEN 1
                    WHEN 'room' THEN 2
                    WHEN 'guest' THEN 3
                    ELSE 9
                END,
                f.id ASC
        ");
        $stmt->execute([':order_id' => $orderId]);
        $folios = $stmt->fetchAll(PDO::FETCH_ASSOC);

        if (!$folios) {
            return [];
        }

        $folioIds = [];
        foreach ($folios as $f) {
            $folioIds[] = (int)$f['id'];
        }

        $placeholders = implode(',', array_fill(0, count($folioIds), '?'));

        $txStmt = $pdo->prepare("
            SELECT *
            FROM folio_transactions
            WHERE folio_id IN ($placeholders)
            ORDER BY tx_date ASC, id ASC
        ");
        $txStmt->execute($folioIds);
        $txRows = $txStmt->fetchAll(PDO::FETCH_ASSOC);

        $groupedTx = [];
        foreach ($txRows as $tx) {
            $fid = (int)$tx['folio_id'];
            if (!isset($groupedTx[$fid])) {
                $groupedTx[$fid] = [];
            }
            $groupedTx[$fid][] = $tx;
        }

        foreach ($folios as &$folio) {
            $fid = (int)$folio['id'];
            $roomTypeTitle = trim((string)($roomTypeMap[(int)($folio['room_type_id'] ?? 0)] ?? ''));
            if ((string)$folio['folio_type'] === 'room' && $roomTypeTitle !== '') {
                $folio['folio_label'] .= ' • ' . $roomTypeTitle;
            }
            $folio['transactions'] = $groupedTx[$fid] ?? [];
        }
        unset($folio);

        return $folios;
    }
}

if (!function_exists('get_payment_folio_map')) {
    function get_payment_folio_map(PDO $pdo, int $orderId): array
    {
        $roomTypeMap = build_room_mix_map($pdo, $orderId)['map'];

        $stmt = $pdo->prepare("
            SELECT
                ft.source_id AS payment_id,
                f.folio_name,
                f.folio_type,
                f.occupant_name,
                r.room_no,
                orm.room_type_id
            FROM folio_transactions ft
            INNER JOIN folios f ON f.id = ft.folio_id
            LEFT JOIN rooms r ON r.id = f.room_id
            LEFT JOIN order_rooming orm ON orm.id = f.rooming_id
            WHERE ft.order_id = :order_id
              AND ft.source_table = 'payment'
              AND ft.tx_type = 'payment'
        ");
        $stmt->execute([':order_id' => $orderId]);
        $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

        $map = [];
        foreach ($rows as $row) {
            $label = (string)$row['folio_name'];
            if ((string)$row['folio_type'] === 'master') {
                $label = 'Master Folio';
            } elseif ((string)$row['folio_type'] === 'room') {
                $label = 'Room ' . (($row['room_no'] ?? '') !== '' ? $row['room_no'] : 'N/A');
                $roomTypeTitle = trim((string)($roomTypeMap[(int)($row['room_type_id'] ?? 0)] ?? ''));
                if ($roomTypeTitle !== '') {
                    $label .= ' • ' . $roomTypeTitle;
                }
            } elseif ((string)$row['folio_type'] === 'guest') {
                $guestFolioName = trim((string)($row['occupant_name'] ?? ''));
                $label = 'Guest Folio' . ($guestFolioName !== '' ? ' • ' . $guestFolioName : '');
                if (($row['room_no'] ?? '') !== '') {
                    $label .= ' / Room ' . $row['room_no'];
                }
            }
            $map[(int)$row['payment_id']] = $label;
        }

        return $map;
    }
}

$properties = $pdo->query("
    SELECT id, name, code
    FROM properties
    WHERE is_active = 1
    ORDER BY name ASC
")->fetchAll(PDO::FETCH_ASSOC);

$currencies = [];
$currencies = [];
$currencyLabels = [];

try {
    $stmtCurrency = $pdo->query("
        SELECT 
            CurrencyID, 
            CurrencyCode, 
            Currency, 
            ROE,
            `order_no`
        FROM `tblcurrency`
        WHERE `status` = 1
        ORDER BY 
            CASE WHEN `order_no` IS NULL THEN 999999 ELSE `order_no` END ASC,
            `CurrencyCode` ASC
    ");

    $currencies = $stmtCurrency->fetchAll(PDO::FETCH_ASSOC);

    foreach ($currencies as $currencyRow) {
        $currencyId = (int)$currencyRow['CurrencyID'];

        $currencyLabels[$currencyId] =
            trim((string)$currencyRow['CurrencyCode']) !== ''
                ? (string)$currencyRow['CurrencyCode']
                : (string)$currencyRow['Currency'];
    }

} catch (Throwable $e) {
    die('Currency query failed: ' . $e->getMessage());
}

/*
   ROE direction lock:
   tblcurrency.ROE must always mean Currency per 1 USD.
   We add lightweight audit columns if possible. These are optional and safe.
*/
try {
    $pdo->exec("ALTER TABLE tblcurrency ADD COLUMN IF NOT EXISTS roe_direction VARCHAR(30) NOT NULL DEFAULT 'CURRENCY_PER_USD'");
} catch (Throwable $e) {}
try {
    $pdo->exec("ALTER TABLE tblcurrency ADD COLUMN IF NOT EXISTS roe_updated_at DATETIME NULL");
} catch (Throwable $e) {}
try {
    $pdo->exec("ALTER TABLE tblcurrency ADD COLUMN IF NOT EXISTS roe_source VARCHAR(80) NULL");
} catch (Throwable $e) {}


/* Optional payment source tracking. Safe if the columns already exist. */
try {
    $pdo->exec("ALTER TABLE payment ADD COLUMN IF NOT EXISTS bank_account_id INT UNSIGNED NULL");
} catch (Throwable $e) {}
try {
    $pdo->exec("ALTER TABLE payment ADD COLUMN IF NOT EXISTS pos_machine_id INT UNSIGNED NULL");
} catch (Throwable $e) {}

$selectedPropertyId = (int)($_GET['property_id'] ?? $_POST['property_id'] ?? 0);
$orderId = (int)($_GET['order_id'] ?? $_POST['order_id'] ?? 0);
$errors = [];
$success = '';

if ($orderId <= 0) {
    $q = trim((string)($_GET['q'] ?? ''));
    if ($q !== '') {
        $sql = "
            SELECT id
            FROM orders
            WHERE (order_no = :q OR id = :idq)
        ";
        $params = [
            ':q'   => $q,
            ':idq' => (int)$q,
        ];

        if ($selectedPropertyId > 0) {
            $sql .= " AND property_id = :property_id ";
            $params[':property_id'] = $selectedPropertyId;
        }

        $sql .= " LIMIT 1 ";

        $stmt = $pdo->prepare($sql);
        $stmt->execute($params);
        $orderId = (int)($stmt->fetchColumn() ?: 0);
    }
}

$order = null;
$folio = null; // master summary
$payments = [];
$bookingFolios = [];
$paymentFolioMap = [];
$moveTargetFolios = [];
$auditLogs = [];
$orderRoomMix = '';
$roomTypeMap = [];


if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['move_folio_transaction'])) {
    $orderId = (int)($_POST['order_id'] ?? 0);
    $transactionId = (int)($_POST['transaction_id'] ?? 0);
    $targetFolioId = (int)($_POST['target_folio_id'] ?? 0);
    $reason = trim((string)($_POST['move_reason'] ?? ''));

    if ($orderId <= 0) {
        $errors[] = 'Invalid booking selected.';
    } else {
        try {
            $pdo->beginTransaction();
            move_folio_transaction($pdo, $orderId, $transactionId, $targetFolioId, $reason);
            $pdo->commit();

            header('Location: payments.php?order_id=' . $orderId . '&moved=1');
            exit;
        } catch (Throwable $e) {
            if ($pdo->inTransaction()) {
                $pdo->rollBack();
            }
            $errors[] = $e->getMessage();
        }
    }
}

if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['rebuild_folio_charges'])) {
    $orderId = (int)($_POST['order_id'] ?? 0);

    if ($orderId <= 0) {
        $errors[] = 'Invalid booking selected.';
    } else {
        try {
            $pdo->beginTransaction();

            $masterFolioId = get_or_create_master_folio($pdo, $orderId);
            rebuild_folio_room_charges($pdo, $orderId, $masterFolioId);

            $pdo->commit();

            header('Location: payments.php?order_id=' . $orderId . '&rebuilt=1');
            exit;
        } catch (Throwable $e) {
            if ($pdo->inTransaction()) {
                $pdo->rollBack();
            }
            $errors[] = $e->getMessage();
        }
    }
}

if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['save_payment'])) {
    $orderId = (int)($_POST['order_id'] ?? 0);

    /*
       ENTERED amount is in selected currency.
       BASE accounting is USD.
       Locked formula:
       Effective USD = entered amount / ROE
       where ROE = selected currency per 1 USD.
    */
    $enteredAmount = (float)($_POST['amount'] ?? 0);
    $gratuityEntered = max(0, round((float)($_POST['gratuity_amount'] ?? 0), 2));
    $paymentMethod = trim((string)($_POST['payment_method'] ?? ''));
    $paymentDate = trim((string)($_POST['payment_date'] ?? date('Y-m-d')));
    $currency = trim((string)($_POST['currency'] ?? '1'));
    $invoice = (int)($_POST['invoice'] ?? 0);
    $roeAtm = trim((string)($_POST['roe_atm'] ?? ''));
    $roeValue = (float)($roeAtm !== '' ? $roeAtm : 0);

    if ($roeValue <= 0) {
        $roeValue = 0;
    }

    $applyCardSurcharge = is_card_surcharge_method($paymentMethod);
    // Only the principal payment amount is subject to the 3% POS/Card surcharge.
    // Optional gratuity is posted separately using inventory item 2133 and is never surcharged.
    $surchargeEntered = $applyCardSurcharge ? round($enteredAmount * CC_SURCHARGE_RATE, 2) : 0.00;
    $amountToDebitEntered = round($enteredAmount + $gratuityEntered + $surchargeEntered, 2);
    $surchargeUsd = $roeValue > 0 ? round($surchargeEntered / $roeValue, 2) : 0.00;
    $gratuityUsd = $roeValue > 0 ? round($gratuityEntered / $roeValue, 2) : 0.00;
    $amount = $roeValue > 0 ? round($amountToDebitEntered / $roeValue, 2) : 0.00;
    $idUpload = trim((string)($_POST['id_upload'] ?? ''));
    $postTo = trim((string)($_POST['post_to'] ?? 'master'));
    $bankAccountId = (int)($_POST['bank_account_id'] ?? 0);
    $posMachineId = (int)($_POST['pos_machine_id'] ?? 0);

    if (!is_bank_payment_method($paymentMethod)) {
        $bankAccountId = 0;
    }
    if (!is_card_surcharge_method($paymentMethod)) {
        $posMachineId = 0;
    }

    if ($orderId <= 0) {
        $errors[] = 'Invalid booking selected.';
    }
    if ($enteredAmount <= 0) {
        $errors[] = 'Payment amount must be greater than zero.';
    }
    if ($roeValue <= 0) {
        $errors[] = 'Invalid ROE. ROE must be greater than zero and must mean Currency per 1 USD.';
    }
    if ($amount <= 0) {
        $errors[] = 'Effective USD payment amount must be greater than zero. Please check Amount, gratuity, surcharge and ROE.';
    }
    if ($paymentMethod === '') {
        $errors[] = 'Please select or enter a payment method.';
    }
    if ($paymentDate === '') {
        $errors[] = 'Please enter payment date.';
    }
    if (is_bank_payment_method($paymentMethod) && $bankAccountId > 0) {
        $stmtBankCheck = $pdo->prepare("SELECT COUNT(*) FROM tblbankdetails WHERE ID = :id AND bank_status = 1");
        $stmtBankCheck->execute([':id' => $bankAccountId]);
        if ((int)$stmtBankCheck->fetchColumn() <= 0) {
            $errors[] = 'Selected bank account is not active or does not exist.';
        }
    }
    if (is_card_surcharge_method($paymentMethod) && $posMachineId > 0) {
        $stmtPosCheck = $pdo->prepare("
            SELECT COUNT(*)
            FROM pos_machines pm
            INNER JOIN tblbankdetails b ON b.ID = pm.bank_detail_id
            WHERE pm.id = :id
              AND pm.status = 1
              AND b.bank_status = 1
        ");
        $stmtPosCheck->execute([':id' => $posMachineId]);
        if ((int)$stmtPosCheck->fetchColumn() <= 0) {
            $errors[] = 'Selected POS machine is not active or does not exist.';
        }
    }

    if (!$errors) {
        try {
            $stmtOrder = $pdo->prepare("SELECT property_id FROM orders WHERE id = :id LIMIT 1");
            $stmtOrder->execute([':id' => $orderId]);
            $bookingPropertyId = (int)($stmtOrder->fetchColumn() ?: 0);

            if ($bookingPropertyId <= 0) {
                throw new RuntimeException('Booking property not found.');
            }

            $pdo->beginTransaction();

            $masterFolioId = get_or_create_master_folio($pdo, $orderId);
            ensure_room_charges_posted_to_folio($pdo, $orderId, $masterFolioId);

            $targetFolioId = get_payment_target_folio($pdo, $orderId, $postTo, $masterFolioId);

            if ($gratuityUsd > 0) {
                post_optional_gratuity_to_folio(
                    $pdo,
                    $orderId,
                    $bookingPropertyId,
                    $targetFolioId,
                    $paymentDate,
                    $gratuityUsd,
                    $gratuityEntered,
                    $roeValue
                );
            }

            if ($applyCardSurcharge && $surchargeUsd > 0) {
                post_cc_surcharge_to_folio(
                    $pdo,
                    $orderId,
                    $bookingPropertyId,
                    $targetFolioId,
                    $paymentDate,
                    $surchargeUsd,
                    $surchargeEntered,
                    $roeValue,
                    $paymentMethod
                );
            }

            $stmt = $pdo->prepare("
                INSERT INTO payment
                (
                    property_id,
                    order_id,
                    date_time,
                    amount,
                    invoice,
                    is_main_amount,
                    payment_method,
                    added_date,
                    id_upload,
                    currency,
                    roe_atm,
                    bank_account_id,
                    pos_machine_id
                )
                VALUES
                (
                    :property_id,
                    :order_id,
                    :date_time,
                    :amount,
                    :invoice,
                    :is_main_amount,
                    :payment_method,
                    :added_date,
                    :id_upload,
                    :currency,
                    :roe_atm,
                    :bank_account_id,
                    :pos_machine_id
                )
            ");
            $stmt->execute([
                ':property_id'      => $bookingPropertyId,
                ':order_id'         => $orderId,
                ':date_time'        => $paymentDate . ' 00:00:00',
                /*
                   Store payment.amount as USD base/effective amount actually collected.
                   For POS/Card this includes the 3% CC surcharge.
                   Optional gratuity is included in the collected amount, but is posted as a separate gratuity charge and is not surcharged.
                   Original currency and ROE are retained in currency / roe_atm.
                */
                ':amount'           => round($amount, 2),
                ':invoice'          => $invoice,
                ':is_main_amount'   => 1,
                ':payment_method'   => $paymentMethod,
                ':added_date'       => $paymentDate,
                ':id_upload'        => (function () use ($pdo, $idUpload, $enteredAmount, $gratuityEntered, $applyCardSurcharge, $surchargeEntered, $amountToDebitEntered, $roeValue, $amount) {
                    $value = $idUpload !== '' ? $idUpload : (
                        'Principal ' . number_format($enteredAmount, 2, '.', '')
                        . ($gratuityEntered > 0 ? (' + Gratuity ' . number_format($gratuityEntered, 2, '.', '')) : '')
                        . ($applyCardSurcharge ? (' + 3% Surcharge ' . number_format($surchargeEntered, 2, '.', '') . ' = ' . number_format($amountToDebitEntered, 2, '.', '')) : '')
                        . ' @ ROE ' . number_format($roeValue, 5, '.', '')
                        . ' = USD ' . number_format($amount, 2, '.', '')
                    );
                    /*
                       id_upload is a short receipt/bank-ref field, not a narrative -
                       the auto-generated summary above can run long once gratuity and
                       card surcharge both apply. Truncate to the column's real length
                       so this can never hit "Data too long for column" again, no matter
                       how many optional pieces get appended.
                    */
                    $maxLen = pay_column_max_length($pdo, 'payment', 'id_upload', 100);
                    return mb_strlen($value) > $maxLen ? mb_substr($value, 0, $maxLen) : $value;
                })(),
                ':currency'         => $currency !== '' ? (int)$currency : null,
                ':roe_atm'          => $roeValue,
                ':bank_account_id'  => $bankAccountId > 0 ? $bankAccountId : null,
                ':pos_machine_id'   => $posMachineId > 0 ? $posMachineId : null,
            ]);

            $paymentId = (int)$pdo->lastInsertId();

            post_payment_to_folio($pdo, $paymentId, $targetFolioId);
            recalculate_folio_totals($pdo, $targetFolioId);

            if ($masterFolioId !== $targetFolioId) {
                recalculate_folio_totals($pdo, $masterFolioId);
            }

            sync_order_payment_status($pdo, $orderId);

            $pdo->commit();

            header('Location: payments.php?order_id=' . $orderId . '&saved=1');
            exit;
        } catch (Throwable $e) {
            if ($pdo->inTransaction()) {
                $pdo->rollBack();
            }
            $errors[] = $e->getMessage();
        }
    }
}

if (isset($_GET['saved']) && (int)$_GET['saved'] === 1) {
    $success = 'Payment saved successfully.';
}
if (isset($_GET['rebuilt']) && (int)$_GET['rebuilt'] === 1) {
    $success = 'Folio room charges rebuilt successfully.';
}
if (isset($_GET['voided']) && (int)$_GET['voided'] === 1) {
    $success = 'Payment voided successfully.';
}
if (isset($_GET['moved']) && (int)$_GET['moved'] === 1) {
    $success = 'Transaction moved to the selected folio successfully.';
}

if ($orderId > 0) {
    $stmt = $pdo->prepare("
        SELECT
            o.*,
            g.firstname,
            g.lastname,
            p.name AS property_name,
            p.code AS property_code,
            CONCAT(COALESCE(a.firstname,''), ' ') AS agent_name
        FROM orders o
        LEFT JOIN guests g ON g.id = o.guest_id
        LEFT JOIN properties p ON p.id = o.property_id
        LEFT JOIN agents a ON a.id = o.agent_id
        WHERE o.id = :id
        LIMIT 1
    ");
    $stmt->execute([':id' => $orderId]);
    $order = $stmt->fetch(PDO::FETCH_ASSOC);

    if (!$order) {
        $errors[] = 'Booking not found.';
        $orderId = 0;
    } else {
        $selectedPropertyId = (int)($order['property_id'] ?? 0);
        $roomMixData = build_room_mix_map($pdo, $orderId);
        $orderRoomMix = (string)($roomMixData['mix'] ?? '');
        $roomTypeMap = (array)($roomMixData['map'] ?? []);

        try {
            $pdo->beginTransaction();

            $masterFolioId = get_or_create_master_folio($pdo, $orderId);
            ensure_room_charges_posted_to_folio($pdo, $orderId, $masterFolioId);
            recalculate_folio_totals($pdo, $masterFolioId);
            sync_order_payment_status($pdo, $orderId);

            $pdo->commit();
        } catch (Throwable $e) {
            if ($pdo->inTransaction()) {
                $pdo->rollBack();
            }
            $errors[] = $e->getMessage();
        }

        $stmt = $pdo->prepare("
            SELECT *
            FROM folios
            WHERE order_id = :order_id
              AND folio_type = 'master'
            LIMIT 1
        ");
        $stmt->execute([':order_id' => $orderId]);
        $folio = $stmt->fetch(PDO::FETCH_ASSOC);

        $bookingFolios = get_booking_folios_with_transactions($pdo, $orderId);
        $moveTargetFolios = get_booking_folios_for_move($pdo, $orderId);

        $stmt = $pdo->prepare("
            SELECT
                p.*,
                b.Bank AS linked_bank_name,
                b.AccountName AS linked_bank_account_name,
                b.AccountNo AS linked_bank_account_no,
                pm.machine_name AS linked_pos_machine_name,
                pm.terminal_id AS linked_pos_terminal_id
            FROM payment p
            LEFT JOIN tblbankdetails b ON b.ID = p.bank_account_id
            LEFT JOIN pos_machines pm ON pm.id = p.pos_machine_id
            WHERE p.order_id = :order_id
            ORDER BY COALESCE(p.date_time, CONCAT(p.added_date, ' 00:00:00')) DESC, p.id DESC
        ");
        $stmt->execute([':order_id' => $orderId]);
        $payments = $stmt->fetchAll(PDO::FETCH_ASSOC);

        $paymentFolioMap = get_payment_folio_map($pdo, $orderId);

        $stmt = $pdo->prepare("
            SELECT
                l.*,
                CONCAT(COALESCE(u.firstname,''), ' ', COALESCE(u.lastname,'')) AS action_by_name
            FROM payment_audit_log l
            LEFT JOIN users u ON u.id = l.action_by
            WHERE l.order_id = :order_id
            ORDER BY l.action_at DESC, l.id DESC
            LIMIT 50
        ");
        $stmt->execute([':order_id' => $orderId]);
        $auditLogs = $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
}

$paymentBankAccounts = fetch_payment_bank_accounts($pdo, $selectedPropertyId);
$paymentPosMachines = fetch_payment_pos_machines($pdo, $selectedPropertyId);

require_once __DIR__ . '/../../includes/header.php';
?>

<div class="hero-band">
    <div class="row g-4 align-items-center">
        <div class="col-lg-8">
            <div class="page-eyebrow text-white-50"><i class="bi bi-cash-stack"></i> Folios & Payments</div>
            <div class="hero-title mt-2">Payments</div>
            <div class="hero-subtitle mt-2">
                Record payments, post them to the correct folio ledger, and keep booking balances up to date by property.
            </div>
        </div>
        <div class="col-lg-4 text-lg-end">
            <a href="/rooms/modules/reservations/reservation_list.php<?php echo $selectedPropertyId > 0 ? '?property_id=' . (int)$selectedPropertyId : ''; ?>" class="btn btn-outline-light">
                <i class="bi bi-arrow-left me-2"></i>Back to Reservations
            </a>
        </div>
    </div>
</div>

<?php if ($success): ?>
    <div class="alert alert-success"><?php echo h($success); ?></div>
<?php endif; ?>

<?php if ($errors): ?>
    <div class="alert alert-danger">
        <div class="fw-bold mb-2">Please fix the following:</div>
        <ul class="mb-0">
            <?php foreach ($errors as $err): ?>
                <li><?php echo h($err); ?></li>
            <?php endforeach; ?>
        </ul>
    </div>
<?php endif; ?>

<div class="card panel-card mb-4">
    <div class="card-body">
        <form method="get" class="row g-3 align-items-end">
            <div class="col-md-4">
                <label class="form-label">Property</label>
                <select name="property_id" class="form-select">
                    <option value="0">All properties</option>
                    <?php foreach ($properties as $property): ?>
                        <option value="<?php echo (int)$property['id']; ?>" <?php echo $selectedPropertyId === (int)$property['id'] ? 'selected' : ''; ?>>
                            <?php echo h($property['name']); ?> - <?php echo h($property['code']); ?>
                        </option>
                    <?php endforeach; ?>
                </select>
            </div>

<div class="col-md-4">
    <label class="form-label">Booking ID or Booking No</label>
    <input type="text" name="q" class="form-control"
           value="<?php echo h((string)($_GET['q'] ?? '')); ?>"
           placeholder="e.g. BK-20260412... or 15">
</div>

<?php if ($orderId > 0): ?>
<div class="col-md-4">
    <label class="form-label">&nbsp;</label>
    <div class="d-flex gap-2 flex-wrap">

        <!-- Record Payment -->
        <button type="button"
                class="btn btn-success btn-sm"
                data-bs-toggle="modal"
                data-bs-target="#recordPaymentModal">
            <i class="bi bi-credit-card">Record Payment</i>
        </button>

        <!-- Rebuild -->
        <form method="post" class="d-inline">
            <input type="hidden" name="order_id" value="<?php echo (int)$orderId; ?>">
            <button type="submit"
                    name="rebuild_folio_charges"
                    value="1"
                    class="btn btn-outline-secondary btn-sm">
                <i class="bi bi-arrow-repeat">Folio</i>
            </button>
        </form>

        <!-- Proforma -->
        <a href="/rooms/modules/payments/proforma_invoice.php?order_id=<?php echo (int)$orderId; ?>"
           target="_blank"
           class="btn btn-outline-primary btn-sm">
            <i class="bi bi-file-earmark-text">PDF</i>
        </a>

        <!-- Receipt -->
        <a href="/rooms/modules/payments/receipt.php?order_id=<?php echo (int)$orderId; ?>"
           target="_blank"
           class="btn btn-outline-success btn-sm">
            <i class="bi bi-receipt">Receipt</i>
        </a>

    </div>
</div>
<?php endif; ?>

<div class="col-md-2 d-grid">
    <button class="btn btn-primary">Load</button>
</div>
        </form>
    </div>
</div>

<?php if ($order): ?>
    <?php
    $guestName = trim(($order['firstname'] ?? '') . ' ' . ($order['lastname'] ?? ''));
    if ($guestName === '') {
        $guestName = 'Guest';
    }
    $agentName = trim((string)($order['agent_name'] ?? ''));
    if ($agentName === '') {
        $agentName = 'Direct';
    }
    $propertyName = trim((string)($order['property_name'] ?? ''));
    if ($propertyName === '') {
        $propertyName = 'Not set';
    }
    $propertyLabel = $propertyName;
    if (!empty($order['property_code'])) {
        $propertyLabel .= ' (' . $order['property_code'] . ')';
    }

    $roomFolios = $orderId ? get_room_folios($pdo, $orderId) : [];
    $guestFolios = $orderId ? get_guest_folios($pdo, $orderId) : [];

    $roomFolioBalances = [];
    $guestFolioBalances = [];
    foreach ($bookingFolios as $bfBalance) {
        if ((string)($bfBalance['folio_type'] ?? '') === 'room') {
            $roomFolioBalances[(int)($bfBalance['rooming_id'] ?? 0)] = (float)($bfBalance['balance'] ?? 0);
        } elseif ((string)($bfBalance['folio_type'] ?? '') === 'guest') {
            $guestFolioBalances[(int)($bfBalance['id'] ?? 0)] = (float)($bfBalance['balance'] ?? 0);
        }
    }

    $grandCharges = 0;
    $grandPayments = 0;
    $grandBalance = 0;
    foreach ($bookingFolios as $bfSum) {
        $grandCharges += (float)($bfSum['total_charges'] ?? 0);
        $grandPayments += (float)($bfSum['total_payments'] ?? 0);
        $grandBalance += (float)($bfSum['balance'] ?? 0);
    }
    ?>
    <div class="row g-4">
        <div class="col-xl-4">
            <div class="form-section compact-summary-card">
                <div class="form-section-title">
                    <i class="bi bi-journal-text"></i>
                    Booking & Folio Summary
                </div>

                <div class="mini-stat mb-3">
                    <span>Property</span>
                    <strong><?php echo h($propertyLabel); ?></strong>
                </div>

                <div class="mini-stat mb-3">
                    <span>Booking</span>
                    <strong><?php echo h($order['order_no'] ?: ('#' . $order['id'])); ?></strong>
                </div>

                <div class="mini-stat mb-3">
                    <span>Guest</span>
                    <strong><?php echo h($guestName); ?></strong>
                </div>

                <div class="mini-stat mb-3">
                    <span>Agent</span>
                    <strong><?php echo h($agentName); ?></strong>
                </div>

                <div class="mini-stat mb-3">
                    <span>Stay</span>
                    <strong><?php echo h($order['check_in']); ?> to <?php echo h($order['check_out']); ?></strong>
                </div>

                <div class="mini-stat mb-3">
                    <span>Room Mix</span>
                    <strong><?php echo h($orderRoomMix !== '' ? $orderRoomMix : 'Not set'); ?></strong>
                </div>

                <div class="mini-stat mb-3">
                    <span>Rooms / Pax</span>
                    <strong><?php echo (int)($order['totalrooms'] ?? 1); ?> rooms • <?php echo (int)($order['adults'] ?? 0); ?>A / <?php echo (int)($order['kids'] ?? 0); ?>C</strong>
                </div>

                <?php if ($folio): ?>
                    <div class="mini-stat mb-3">
                        <span>Master Folio No</span>
                        <strong><?php echo h($folio['folio_no']); ?></strong>
                    </div>
                <?php endif; ?>

                <div class="mini-stat mb-3">
                    <span>Booking Value / Charges</span>
                    <strong><?php echo money_fmt($grandCharges); ?></strong>
                </div>

                <div class="mini-stat mb-3">
                    <span>Net Payments</span>
                    <strong><?php echo money_fmt($grandPayments); ?></strong>
                </div>

                <div class="mini-stat mb-3">
                    <span>Outstanding Balance</span>
                    <strong class="<?php echo abs($grandBalance) > 0.00001 ? 'balance-positive' : 'balance-zero'; ?>"><?php echo money_fmt($grandBalance); ?></strong>
                </div>

                <div class="alert alert-light border small mb-3">
                    <i class="bi bi-info-circle me-1"></i>
                    Voided payments reduce net payments only. They do not reduce the booking value/charges.
                </div>

                <div class="summary-actions">
                    <button type="button"
                            class="btn btn-primary btn-lg w-100"
                            data-bs-toggle="modal"
                            data-bs-target="#recordPaymentModal">
                        <i class="bi bi-credit-card me-2"></i>Record Payment
                    </button>

                    <form method="post">
                        <input type="hidden" name="order_id" value="<?php echo (int)$order['id']; ?>">
                        <button type="submit" name="rebuild_folio_charges" value="1" class="btn btn-outline-secondary w-100">
                            <i class="bi bi-arrow-repeat me-2"></i>Rebuild Folio Charges
                        </button>
                    </form>

                    <a href="/rooms/modules/payments/proforma_invoice.php?order_id=<?php echo (int)$order['id']; ?>"
                       class="btn btn-outline-primary w-100"
                       target="_blank">
                        <i class="bi bi-file-earmark-text me-2"></i>Proforma Invoice PDF
                    </a>

                    <a href="/rooms/modules/payments/receipt.php?order_id=<?php echo (int)$order['id']; ?>"
                       class="btn btn-outline-success w-100"
                       target="_blank">
                        <i class="bi bi-receipt me-2"></i>Receipt PDF
                    </a>
                </div>
            </div>

                    </div>

        <div class="col-xl-8">
            <div class="card panel-card mb-4">
                <div class="card-header">
                    <h5 class="mb-0">Booking Folios & Ledgers</h5>
                </div>
                <div class="card-body">
                    <?php if (empty($bookingFolios)): ?>
                        <div class="text-center text-muted py-4">No folios found for this booking.</div>
                    <?php else: ?>
                        <div class="accordion" id="folioAccordion">
                            <?php foreach ($bookingFolios as $index => $bf): ?>
                                <?php
                                $collapseId = 'folioCollapse' . (int)$bf['id'];
                                $headingId = 'folioHeading' . (int)$bf['id'];
                                $isOpen = $index === 0 ? 'show' : '';
                                $btnState = $index === 0 ? '' : 'collapsed';
                                ?>
                                <div class="accordion-item mb-3 border rounded-3 overflow-hidden">
                                    <h2 class="accordion-header" id="<?php echo $headingId; ?>">
                                        <button class="accordion-button <?php echo $btnState; ?>" type="button"
                                                data-bs-toggle="collapse"
                                                data-bs-target="#<?php echo $collapseId; ?>"
                                                aria-expanded="<?php echo $index === 0 ? 'true' : 'false'; ?>"
                                                aria-controls="<?php echo $collapseId; ?>">
                                            <div class="w-100 d-flex justify-content-between align-items-center pe-3">
                                                <div>
                                                    <strong><?php echo h($bf['folio_name']); ?></strong>
                                                    <div class="small text-muted">
                                                        <?php echo h($bf['folio_label']); ?>
                                                        <?php if (!empty($bf['room_no'])): ?>
                                                            | Room <?php echo h($bf['room_no']); ?>
                                                        <?php endif; ?>
                                                    </div>
                                                </div>
                                                <div class="text-end">
                                                    <div class="small text-muted">Balance</div>
                                                    <strong><?php echo money_fmt($bf['balance'] ?? 0); ?></strong>
                                                </div>
                                            </div>
                                        </button>
                                    </h2>

                                    <div id="<?php echo $collapseId; ?>"
                                         class="accordion-collapse collapse <?php echo $isOpen; ?>"
                                         aria-labelledby="<?php echo $headingId; ?>"
                                         data-bs-parent="#folioAccordion">
                                        <div class="accordion-body p-0">
                                            <div class="p-3 border-bottom bg-light">
                                                <div class="row g-3">
                                                    <div class="col-md-4">
                                                        <div class="mini-stat">
                                                            <span>Charges</span>
                                                            <strong><?php echo money_fmt($bf['total_charges'] ?? 0); ?></strong>
                                                        </div>
                                                    </div>
                                                    <div class="col-md-4">
                                                        <div class="mini-stat">
                                                            <span>Net Payments</span>
                                                            <strong><?php echo money_fmt($bf['total_payments'] ?? 0); ?></strong>
                                                        </div>
                                                    </div>
                                                    <div class="col-md-4">
                                                        <div class="mini-stat">
                                                            <span>Balance</span>
                                                            <strong><?php echo money_fmt($bf['balance'] ?? 0); ?></strong>
                                                        </div>
                                                    </div>
                                                </div>
                                            </div>

                                            <div class="table-responsive">
                                                <table class="table table-modern align-middle mb-0">
                                                    <thead>
                                                        <tr>
                                                            <th>Date</th>
                                                            <th>Type</th>
                                                            <th>Description</th>
                                                            <th class="text-end">Debit</th>
                                                            <th class="text-end">Credit</th>
                                                            <th class="text-end">Balance</th>
                                                            <th class="text-end">Action</th>
                                                        </tr>
                                                    </thead>
                                                    <tbody>
                                                        <?php if (empty($bf['transactions'])): ?>
                                                            <tr>
                                                                <td colspan="7" class="text-center text-muted py-4">
                                                                    No folio transactions yet.
                                                                </td>
                                                            </tr>
                                                        <?php else: ?>
                                                            <?php $runningBalance = 0.0; ?>
                                                            <?php foreach ($bf['transactions'] as $tx): ?>
                                                                <?php $runningBalance += ((float)$tx['debit'] - (float)$tx['credit']); ?>
                                                                <tr>
                                                                    <td><?php echo h($tx['tx_date']); ?></td>
                                                                    <td><?php echo folio_tx_badge((string)$tx['tx_type']); ?></td>
                                                                    <td><?php echo h($tx['description']); ?></td>
                                                                    <td class="text-end"><?php echo money_fmt($tx['debit']); ?></td>
                                                                    <td class="text-end"><?php echo money_fmt($tx['credit']); ?></td>
                                                                    <td class="text-end fw-bold"><?php echo money_fmt($runningBalance); ?></td>
                                                                    <td class="text-end">
                                                                        <?php
                                                                        $txTypeForMove = strtolower(trim((string)($tx['tx_type'] ?? '')));
                                                                        $canMoveTx = empty($tx['is_voided']) && $txTypeForMove !== 'room_charge';
                                                                        ?>
                                                                        <?php if ($canMoveTx): ?>
                                                                            <button type="button"
                                                                                    class="btn btn-sm btn-outline-primary btn-move-folio-tx"
                                                                                    data-bs-toggle="modal"
                                                                                    data-bs-target="#moveFolioTransactionModal"
                                                                                    data-tx-id="<?php echo (int)$tx['id']; ?>"
                                                                                    data-current-folio-id="<?php echo (int)$bf['id']; ?>"
                                                                                    data-current-folio="<?php echo h($bf['folio_name']); ?>"
                                                                                    data-description="<?php echo h($tx['description']); ?>"
                                                                                    data-amount="<?php echo h(money_fmt(((float)$tx['debit'] > 0 ? $tx['debit'] : $tx['credit']))); ?>"
                                                                                    data-type="<?php echo h($tx['tx_type']); ?>">
                                                                                <i class="bi bi-arrow-left-right me-1"></i>Move
                                                                            </button>
                                                                        <?php else: ?>
                                                                            <span class="badge text-bg-light text-dark border">Locked</span>
                                                                        <?php endif; ?>
                                                                    </td>
                                                                </tr>
                                                            <?php endforeach; ?>
                                                        <?php endif; ?>
                                                    </tbody>
                                                    <tfoot>
                                                        <tr>
                                                            <th colspan="3" class="text-end">Charges</th>
                                                            <th class="text-end"><?php echo money_fmt($bf['total_charges'] ?? 0); ?></th>
                                                            <th></th>
                                                            <th></th>
                                                            <th></th>
                                                        </tr>
                                                        <tr>
                                                            <th colspan="3" class="text-end">Payments</th>
                                                            <th></th>
                                                            <th class="text-end"><?php echo money_fmt($bf['total_payments'] ?? 0); ?></th>
                                                            <th></th>
                                                        </tr>
                                                        <tr>
                                                            <th colspan="5" class="text-end">Balance</th>
                                                            <th class="text-end"><?php echo money_fmt($bf['balance'] ?? 0); ?></th>
                                                            <th></th>
                                                        </tr>
                                                    </tfoot>
                                                </table>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            <?php endforeach; ?>
                        </div>
                    <?php endif; ?>
                </div>
            </div>

            <div class="card panel-card">
                <div class="card-header">
                    <h5 class="mb-0">Payment History</h5>
                </div>
                <div class="card-body p-0">
                    <div class="table-responsive">
                        <table class="table table-modern align-middle mb-0">
                            <thead>
                                <tr>
                                    <th>Date</th>
                                    <th>Method</th>
                                    <th>Posted To</th>
                                    <th>Reference</th>
                                    <th>Currency</th>
                                    <th class="text-end">Amount</th>
                                    <th class="text-end">Actions</th>
                                </tr>
                            </thead>
                            <tbody>
                            <?php if (!$payments): ?>
                                <tr>
                                    <td colspan="7" class="text-center text-muted py-4">No payments recorded yet.</td>
                                </tr>
                            <?php else: ?>
                                <?php foreach ($payments as $pay): ?>
                                    <tr class="<?php echo !empty($pay['is_voided']) ? 'table-secondary' : ''; ?>">
                                        <td><?php echo h($pay['date_time'] ?: $pay['added_date']); ?></td>
                                        <td>
                                            <div><?php echo h($pay['payment_method']); ?></div>
                                            <?php if (!empty($pay['linked_bank_name'])): ?>
                                                <div class="small text-muted">
                                                    Bank: <?php echo h(trim((string)$pay['linked_bank_name'] . ' - ' . (string)$pay['linked_bank_account_name'])); ?>
                                                    <?php if (!empty($pay['linked_bank_account_no'])): ?> / <?php echo h($pay['linked_bank_account_no']); ?><?php endif; ?>
                                                </div>
                                            <?php endif; ?>
                                            <?php if (!empty($pay['linked_pos_machine_name'])): ?>
                                                <div class="small text-muted">
                                                    POS: <?php echo h($pay['linked_pos_machine_name']); ?>
                                                    <?php if (!empty($pay['linked_pos_terminal_id'])): ?> / Terminal <?php echo h($pay['linked_pos_terminal_id']); ?><?php endif; ?>
                                                </div>
                                            <?php endif; ?>
                                        </td>
                                        <td><?php echo h($paymentFolioMap[(int)$pay['id']] ?? 'Master Folio'); ?></td>
                                        <td>
                                            <?php echo h($pay['id_upload']); ?>
                                            <?php if (!empty($pay['is_voided'])): ?>
                                                <div class="small text-danger mt-1">
                                                    Voided<?php echo !empty($pay['void_reason']) ? ': ' . h($pay['void_reason']) : ''; ?>
                                                </div>
                                            <?php endif; ?>
                                        </td>
                                        <td><?php echo h($currencyLabels[(int)$pay['currency']] ?? (string)$pay['currency']); ?></td>
                                        <td class="text-end fw-bold"><?php echo money_fmt($pay['amount']); ?></td>
                                        <td class="text-end">
                                            <div class="d-inline-flex gap-2 flex-wrap justify-content-end">
                                                <a href="/rooms/modules/payments/receipt.php?order_id=<?php echo (int)$order['id']; ?>&payment_id=<?php echo (int)$pay['id']; ?>"
                                                   class="btn btn-sm btn-outline-success"
                                                   target="_blank"
                                                   title="Print receipt for this payment only">
                                                    <i class="bi bi-receipt me-1"></i>Receipt
                                                </a>

                                                <?php if ($canVoidPayments && empty($pay['is_voided'])): ?>
                                                    <button type="button"
                                                            class="btn btn-sm btn-outline-danger btn-void-payment"
                                                            data-payment-id="<?php echo (int)$pay['id']; ?>"
                                                            data-order-id="<?php echo (int)$order['id']; ?>"
                                                            data-payment-method="<?php echo h($pay['payment_method']); ?>"
                                                            data-payment-amount="<?php echo h(money_fmt($pay['amount'])); ?>"
                                                            data-payment-date="<?php echo h($pay['date_time'] ?: $pay['added_date']); ?>">
                                                        <i class="bi bi-x-circle me-1"></i>Void
                                                    </button>
                                                <?php endif; ?>
                                            </div>
                                        </td>
                                    </tr>
                                <?php endforeach; ?>
                            <?php endif; ?>
                        </tbody>
                        </table>
                    </div>
                </div>
            </div>

            <div class="card panel-card mt-4">
                <div class="card-header d-flex justify-content-between align-items-center">
                    <h5 class="mb-0">Audit Trail</h5>
                    <?php if (!empty($order['id'])): ?>
                        <a href="/rooms/modules/payments/export_audit_log.php?order_id=<?php echo (int)$order['id']; ?>"
                           class="btn btn-sm btn-outline-primary">
                            <i class="bi bi-download me-1"></i>Export CSV
                        </a>
                    <?php endif; ?>
                </div>
                <div class="card-body p-0">
                    <div class="table-responsive">
                        <table class="table table-modern align-middle mb-0">
                            <thead>
                                <tr>
                                    <th>Date</th>
                                    <th>Action</th>
                                    <th>Reason</th>
                                    <th>Done By</th>
                                    <th>User Type</th>
                                    <th>Payment</th>
                                    <th>Folio TX</th>
                                    <th class="text-end">Actions</th>
                                </tr>
                            </thead>
                            <tbody>
                                <?php if (empty($auditLogs)): ?>
                                <tr>
                                    <td colspan="8" class="text-center text-muted py-4">No audit log entries yet.</td>
                                </tr>
                                <?php else: ?>
                                    <?php foreach ($auditLogs as $log): ?>
                                        <tr>
                                            <td><?php echo h($log['action_at']); ?></td>
                                            <td><?php echo h($log['action_type']); ?></td>
                                            <td><?php echo h($log['action_reason']); ?></td>
                                            <td>
                                                <?php
                                                $doneBy = trim((string)($log['action_by_name'] ?? ''));
                                                $userId = (int)($log['action_by'] ?? 0);
                                                ?>
                                                <div><?php echo h($doneBy !== '' ? $doneBy : ('User #' . $userId)); ?></div>
                                            </td>
                                            <td><?php echo h($log['action_by_type']); ?></td>
                                            <td><?php echo (int)($log['payment_id'] ?? 0); ?></td>
                                            <td><?php echo (int)($log['folio_transaction_id'] ?? 0); ?></td>
                                            <td class="text-end">
                                                <?php if (!empty($log['payload_json']) && $canVoidPayments): ?>
                                                    <button type="button"
                                                            class="btn btn-sm btn-outline-secondary btn-audit-details"
                                                            data-audit-id="<?php echo (int)$log['id']; ?>"
                                                            data-audit-action="<?php echo h($log['action_type']); ?>"
                                                            data-audit-date="<?php echo h($log['action_at']); ?>"
                                                            data-audit-reason="<?php echo h($log['action_reason']); ?>"
                                                            data-audit-payload="<?php echo h($log['payload_json']); ?>">
                                                        <i class="bi bi-eye me-1"></i>Details
                                                    </button>
                                                <?php else: ?>
                                                    <span class="text-muted small">No details</span>
                                                <?php endif; ?>
                                            </td>
                                        </tr>
                                    <?php endforeach; ?>
                                <?php endif; ?>
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>

        </div>
    </div>
<?php endif; ?>

<style>
    /* Compact premium spacing for Payments summary and ledger page */
    .compact-summary-card {
        padding: 14px 16px !important;
        border-radius: 18px;
        background: #fff;
        border: 1px solid #eef2f7;
        box-shadow: 0 8px 22px rgba(15, 23, 42, 0.06);
    }

    .compact-summary-card .form-section-title {
        margin-bottom: 12px !important;
        padding-bottom: 8px;
        border-bottom: 1px solid #eef2f7;
    }

    .compact-summary-card .mini-stat,
    .accordion-body .mini-stat {
        display: flex;
        justify-content: space-between;
        align-items: flex-start;
        gap: 12px;
        padding: 7px 0;
        margin-bottom: 0 !important;
        border-bottom: 1px dashed #edf1f5;
    }

    .compact-summary-card .mini-stat:last-of-type,
    .accordion-body .mini-stat:last-child {
        border-bottom: 0;
    }

    .compact-summary-card .mini-stat span,
    .accordion-body .mini-stat span {
        color: #6b7280;
        font-size: 12.5px;
        line-height: 1.25;
        min-width: 88px;
    }

    .compact-summary-card .mini-stat strong,
    .accordion-body .mini-stat strong {
        color: #111827;
        font-size: 13px;
        line-height: 1.3;
        font-weight: 700;
        text-align: right;
        word-break: break-word;
    }

    @media (min-width: 1200px) {
        .compact-summary-card .mini-stat {
            display: grid;
            grid-template-columns: minmax(85px, 0.9fr) minmax(0, 1.2fr);
        }

        .col-xl-4 > .compact-summary-card {
            position: sticky;
            top: 92px;
        }
    }

    .summary-actions {
        display: grid;
        gap: 8px;
        margin-top: 12px;
        padding-top: 10px;
        border-top: 1px solid #eef2f7;
    }

    .summary-actions .btn {
        border-radius: 12px;
        padding-top: 9px;
        padding-bottom: 9px;
    }

    .summary-actions .btn-lg {
        padding-top: 10px;
        padding-bottom: 10px;
        font-size: 1rem;
    }

    .balance-positive {
        color: #dc2626 !important;
    }

    .balance-zero {
        color: #16a34a !important;
    }

    .panel-card .card-body {
        padding: 14px 16px;
    }

    .accordion-item {
        margin-bottom: 10px !important;
    }

    .accordion-button {
        padding: 12px 14px;
    }

    .accordion-body {
        padding: 14px !important;
    }

    .table-modern > :not(caption) > * > * {
        padding-top: 9px;
        padding-bottom: 9px;
    }

    .card.panel-card.mt-4 {
        margin-top: 14px !important;
    }
</style>


<?php if ($order): ?>
<div class="modal fade" id="recordPaymentModal" tabindex="-1" aria-labelledby="recordPaymentModalLabel" aria-hidden="true">
    <div class="modal-dialog modal-lg modal-dialog-scrollable">
        <form method="post" class="modal-content">
            <div class="modal-header">
                <div>
                    <h5 class="modal-title" id="recordPaymentModalLabel">
                        <i class="bi bi-credit-card me-2"></i>Record Payment
                    </h5>
                    <div class="small text-muted">
                        Booking <?php echo h($order['order_no'] ?: ('#' . $order['id'])); ?> • Balance <?php echo money_fmt($grandBalance); ?>
                    </div>
                </div>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>

            <div class="modal-body">
                <input type="hidden" name="order_id" value="<?php echo (int)$order['id']; ?>">
                <input type="hidden" name="property_id" value="<?php echo (int)$order['property_id']; ?>">

                <div class="row g-3">
                    <div class="col-md-12">
                        <label class="form-label">Post To</label>
                        <select name="post_to" id="payment_post_to" class="form-select form-select-lg">
                            <option value="master" data-balance-usd="<?php echo h((string)round((float)($folio['balance'] ?? $grandBalance), 2)); ?>">Master Folio (Agent / Master Account)</option>

                            <?php if (!empty($roomFolios)): ?>
                                <optgroup label="Room Folios">
                                    <?php foreach ($roomFolios as $r): ?>
                                        <?php $roomPayload = $r; $roomPayload['target_type'] = 'room'; ?>
                                        <option value='<?php echo h(json_encode($roomPayload, JSON_HEX_APOS | JSON_HEX_QUOT)); ?>' data-balance-usd="<?php echo h((string)round((float)($roomFolioBalances[(int)($r['rooming_id'] ?? 0)] ?? 0), 2)); ?>">
                                            Room <?php echo h($r['room_no'] ?: 'N/A'); ?>
                                            <?php if (!empty($r['room_type_title'])): ?>
                                                • <?php echo h($r['room_type_title']); ?>
                                            <?php endif; ?>
                                            - <?php echo h(trim((string)$r['guest']) !== '' ? $r['guest'] : 'Guest'); ?>
                                        </option>
                                    <?php endforeach; ?>
                                </optgroup>
                            <?php endif; ?>

                            <?php if (!empty($guestFolios)): ?>
                                <optgroup label="Individual Guest Folios">
                                    <?php foreach ($guestFolios as $gf): ?>
                                        <?php
                                            $guestPayload = [
                                                'target_type' => 'guest',
                                                'folio_id' => (int)$gf['folio_id'],
                                                'rooming_id' => (int)($gf['rooming_id'] ?? 0),
                                                'occupant_id' => (int)($gf['occupant_id'] ?? 0),
                                            ];
                                            $guestNameOpt = trim((string)($gf['occupant_name'] ?? ''));
                                            if ($guestNameOpt === '') {
                                                $guestNameOpt = trim((string)($gf['folio_name'] ?? 'Guest Folio'));
                                            }
                                        ?>
                                        <option value='<?php echo h(json_encode($guestPayload, JSON_HEX_APOS | JSON_HEX_QUOT)); ?>' data-balance-usd="<?php echo h((string)round((float)($guestFolioBalances[(int)$gf['folio_id']] ?? (float)($gf['balance'] ?? 0)), 2)); ?>">
                                            Guest: <?php echo h($guestNameOpt); ?>
                                            <?php if (!empty($gf['room_no'])): ?>
                                                • Room <?php echo h($gf['room_no']); ?>
                                            <?php endif; ?>
                                            <?php if (!empty($gf['room_type_title'])): ?>
                                                • <?php echo h($gf['room_type_title']); ?>
                                            <?php endif; ?>
                                        </option>
                                    <?php endforeach; ?>
                                </optgroup>
                            <?php endif; ?>
                        </select>
                        <div class="form-text">Post payment to Master, Room, or Individual Guest Folio.</div>
                        <div id="selected_folio_balance_hint" class="alert alert-light border mt-2 mb-0 py-2 small">
                            Select a folio to auto-fill the amount due.
                        </div>
                    </div>


                    <div class="col-md-6">
                        <label class="form-label">Amount Entered</label>
                        <input type="number"
                               step="0.01"
                               min="0.01"
                               name="amount"
                               id="payment_amount_entered"
                               class="form-control form-control-lg"
                               value=""
                               required>
                        <div class="form-text">Auto-filled from the selected folio balance. You can still override it for partial payments.</div>
                    </div>

                    <div class="col-md-6">
                        <label class="form-label">Optional Gratuity</label>
                        <input type="number"
                               step="0.01"
                               min="0"
                               name="gratuity_amount"
                               id="payment_gratuity_amount"
                               class="form-control form-control-lg"
                               value="0.00">
                        <div class="form-text">Uses inventory item #<?php echo (int)OPTIONAL_GRATUITY_INVENTORY_ITEM_ID; ?>. No 3% surcharge, even for Card/POS.</div>
                    </div>

                    <div class="col-md-6">
                        <label class="form-label">Payment Date</label>
                        <input type="date"
                               name="payment_date"
                               class="form-control form-control-lg"
                               value="<?php echo h(date('Y-m-d')); ?>"
                               required>
                    </div>

                    <div class="col-md-6">
                        <label class="form-label">Payment Method</label>
                        <select name="payment_method" id="payment_method" class="form-select form-select-lg" required>
                            <option value="">Select method</option>
                            <option value="Cash">Cash</option>
                            <option value="Bank Transfer">Bank Transfer</option>
                            <option value="Card">Card</option>
                            <option value="POS">POS</option>
                            <option value="Mobile Money">Mobile Money</option>
                        </select>
                    </div>

                    <div class="col-md-6 d-none" id="bank_account_wrap">
                        <label class="form-label">Bank Account <span class="text-muted fw-normal">(optional)</span></label>
                        <select name="bank_account_id" id="bank_account_id" class="form-select form-select-lg">
                            <option value="0">No specific bank account</option>
                            <?php foreach ($paymentBankAccounts as $bankOpt): ?>
                                <option value="<?php echo (int)$bankOpt['ID']; ?>">
                                    <?php echo h(
                                        trim((string)($bankOpt['Bank'] ?? ''))
                                        . ' - ' . trim((string)($bankOpt['AccountName'] ?? ''))
                                        . ' / ' . trim((string)($bankOpt['AccountNo'] ?? ''))
                                        . (!empty($bankOpt['CurrencyCode']) ? ' (' . trim((string)$bankOpt['CurrencyCode']) . ')' : '')
                                        . (!empty($bankOpt['property_name']) ? ' • ' . trim((string)$bankOpt['property_name']) : ' • General')
                                    ); ?>
                                </option>
                            <?php endforeach; ?>
                        </select>
                        <div class="form-text">Shown only for Bank/Transfer payment methods.</div>
                    </div>

                    <div class="col-md-6 d-none" id="pos_machine_wrap">
                        <label class="form-label">POS Machine <span class="text-muted fw-normal">(optional)</span></label>
                        <select name="pos_machine_id" id="pos_machine_id" class="form-select form-select-lg">
                            <option value="0">No specific POS machine</option>
                            <?php foreach ($paymentPosMachines as $posOpt): ?>
                                <option value="<?php echo (int)$posOpt['id']; ?>">
                                    <?php echo h(
                                        trim((string)($posOpt['machine_name'] ?? ''))
                                        . ' / Terminal ' . trim((string)($posOpt['terminal_id'] ?? ''))
                                        . (!empty($posOpt['Bank']) ? ' • ' . trim((string)$posOpt['Bank']) : '')
                                        . (!empty($posOpt['AccountName']) ? ' - ' . trim((string)$posOpt['AccountName']) : '')
                                        . (!empty($posOpt['property_name']) ? ' • ' . trim((string)$posOpt['property_name']) : ' • General')
                                    ); ?>
                                </option>
                            <?php endforeach; ?>
                        </select>
                        <div class="form-text">Shown only for POS/Card payment methods.</div>
                    </div>

                    <div class="col-md-6">
                        <label class="form-label">Currency</label>
                        <?php if (!empty($currencies)): ?>
                            <select name="currency" id="payment_currency" class="form-select" required>
                                <option value="">Select currency</option>
                                <?php foreach ($currencies as $cur): ?>
                                    <?php
                                    $curId = (int)$cur['CurrencyID'];
                                    $curCode = trim((string)$cur['CurrencyCode']);
                                    $curName = trim((string)$cur['Currency']);
                                    $curRoe = $cur['ROE'] !== null ? (string)$cur['ROE'] : '1';
                                    ?>
                                    <option value="<?php echo $curId; ?>"
                                            data-roe="<?php echo h($curRoe); ?>"
                                            data-code="<?php echo h($curCode); ?>"
                                            <?php echo $curId === 1 ? 'selected' : ''; ?>>
                                        <?php echo h($curCode . ($curName !== '' ? ' - ' . $curName : '')); ?>
                                    </option>
                                <?php endforeach; ?>
                            </select>
                        <?php else: ?>
                            <input type="number" name="currency" id="payment_currency" class="form-control" value="1" required>
                            <div class="form-text text-muted">Currency table unavailable, using manual currency ID.</div>
                        <?php endif; ?>
                    </div>

                    <div class="col-md-6">
                        <label class="form-label">ROE / Rate</label>
                        <input type="number" step="0.00001" name="roe_atm" id="payment_roe_atm" class="form-control" readonly>
                        <div class="form-text">Locked: Currency per 1 USD. Do not enter USD per currency.</div>
                    </div>

                    <div class="col-md-12">
                        <div class="alert alert-secondary mb-0 py-2">
                            <div class="fw-bold mb-1">Exchange Preview</div>
                            <div><span id="usd_to_currency_preview">1 USD = 1.00000</span></div>
                            <div><span id="currency_to_usd_preview">1 Currency = 1.00000 USD</span></div>
                            <div><strong>Optional Gratuity:</strong> <span id="gratuity_preview">0.00</span></div>
                            <div><strong>CC Surcharge:</strong> <span id="cc_surcharge_preview">0.00</span></div>
                            <div><strong>Amount to Debit Customer:</strong> <span id="amount_to_debit_customer_preview">0.00</span></div>
                            <div class="mt-1"><strong>Effective USD Payment:</strong> <span id="effective_usd_payment">0.00</span></div>
                        </div>
                    </div>

                    <div class="col-12">
                        <div class="alert alert-warning mb-0 py-2 small">
                            <strong>POS/Card surcharge:</strong> When Payment Method is <strong>POS</strong> or <strong>Card</strong>, the system automatically posts <strong>CC Charge</strong> at 3% on the principal amount only. Optional gratuity uses inventory item <strong>#<?php echo (int)OPTIONAL_GRATUITY_INVENTORY_ITEM_ID; ?></strong> and has no surcharge.
                        </div>
                    </div>

                    <div class="col-md-8">
                        <label class="form-label">Reference / Upload ID</label>
                        <input type="text" name="id_upload" class="form-control" placeholder="Receipt ref, bank ref or upload id">
                    </div>

                    <div class="col-md-4" style="display:none">
                        <label class="form-label">Invoice No</label>
                        <input type="number" name="invoice" class="form-control" value="0">
                    </div>
                </div>

                <div class="alert alert-info mt-3 mb-0">
                    <i class="bi bi-info-circle me-2"></i>
                    After saving, the page reloads automatically so the modal closes and folio balances/payment history are updated.
                </div>
            </div>

            <div class="modal-footer">
                <button type="button" class="btn btn-light" data-bs-dismiss="modal">Cancel</button>
                <button type="submit" name="save_payment" value="1" class="btn btn-primary btn-lg">
                    <i class="bi bi-save me-2"></i>Save Payment
                </button>
            </div>
        </form>
    </div>
</div>
<?php endif; ?>


<div class="modal fade" id="moveFolioTransactionModal" tabindex="-1" aria-labelledby="moveFolioTransactionModalLabel" aria-hidden="true">
    <div class="modal-dialog modal-lg">
        <form method="post" class="modal-content">
            <div class="modal-header">
                <div>
                    <h5 class="modal-title" id="moveFolioTransactionModalLabel">Move Folio Transaction</h5>
                    <div class="small text-muted">Move a payment or charge to the correct Master, Room, or Guest folio.</div>
                </div>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>

            <div class="modal-body">
                <input type="hidden" name="order_id" value="<?php echo isset($order['id']) ? (int)$order['id'] : 0; ?>">
                <input type="hidden" name="transaction_id" id="move_transaction_id" value="">

                <div class="alert alert-light border small">
                    <div><strong>Transaction:</strong> <span id="move_tx_description">—</span></div>
                    <div><strong>Type:</strong> <span id="move_tx_type">—</span></div>
                    <div><strong>Amount:</strong> <span id="move_tx_amount">0.00</span></div>
                    <div><strong>Current Folio:</strong> <span id="move_current_folio">—</span></div>
                </div>

                <div class="mb-3">
                    <label class="form-label fw-semibold">Move To Folio</label>
                    <select name="target_folio_id" id="move_target_folio_id" class="form-select" required>
                        <option value="">Select destination folio</option>
                        <?php foreach ($moveTargetFolios as $targetFolio): ?>
                            <option value="<?php echo (int)$targetFolio['id']; ?>">
                                <?php echo h(folio_move_label($targetFolio)); ?> | Balance <?php echo h(money_fmt($targetFolio['balance'] ?? 0)); ?>
                            </option>
                        <?php endforeach; ?>
                    </select>
                    <div class="form-text">The source and destination folio totals will be recalculated immediately.</div>
                </div>

                <div class="mb-0">
                    <label class="form-label fw-semibold">Reason / Audit Note</label>
                    <textarea name="move_reason" class="form-control" rows="4" required placeholder="Example: Extra was posted to Master but should be moved to Guest Folio - John Smith."></textarea>
                </div>
            </div>

            <div class="modal-footer">
                <button type="button" class="btn btn-light" data-bs-dismiss="modal">Cancel</button>
                <button type="submit" name="move_folio_transaction" value="1" class="btn btn-primary">
                    <i class="bi bi-arrow-left-right me-2"></i>Move Transaction
                </button>
            </div>
        </form>
    </div>
</div>

<div class="modal fade" id="voidPaymentModal" tabindex="-1" aria-labelledby="voidPaymentModalLabel" aria-hidden="true">
    <div class="modal-dialog">
        <form method="post" action="/rooms/modules/payments/void_payment.php" class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title" id="voidPaymentModalLabel">Void Payment</h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>

            <div class="modal-body">
                <input type="hidden" name="payment_id" id="void_payment_id" value="">
                <input type="hidden" name="order_id" id="void_order_id" value="<?php echo isset($order['id']) ? (int)$order['id'] : 0; ?>">

                <div class="mb-3">
                    <label class="form-label">Reason for voiding</label>
                    <textarea name="reason" id="void_reason" class="form-control" rows="4" required placeholder="Enter reason for voiding this payment"></textarea>
                </div>

                <div class="small text-muted">
                    This will mark the payment as voided and post a reversal into the folio ledger.
                </div>
            </div>

            <div class="modal-footer">
                <button type="button" class="btn btn-light" data-bs-dismiss="modal">Cancel</button>
                <button type="submit" class="btn btn-danger">
                    <i class="bi bi-x-circle me-2"></i>Void Payment
                </button>
            </div>
        </form>
    </div>
</div>

<div class="modal fade" id="auditDetailsModal" tabindex="-1" aria-labelledby="auditDetailsModalLabel" aria-hidden="true">
    <div class="modal-dialog modal-lg modal-dialog-scrollable">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">Audit Details</h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
            <div class="modal-body">
                <div class="mb-3">
                    <div><strong>Action:</strong> <span id="audit_details_action"></span></div>
                    <div><strong>Date:</strong> <span id="audit_details_date"></span></div>
                    <div><strong>Reason:</strong> <span id="audit_details_reason"></span></div>
                </div>

                <label class="form-label fw-semibold">Payload</label>
                <pre id="audit_details_payload"
                     class="bg-light border rounded p-3 small"
                     style="max-height: 420px; overflow:auto; white-space: pre-wrap; word-break: break-word;"></pre>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-light" data-bs-dismiss="modal">Close</button>
            </div>
        </div>
    </div>
</div>


<script>
document.addEventListener('DOMContentLoaded', function () {

    var moveModalEl = document.getElementById('moveFolioTransactionModal');
    if (moveModalEl) {
        var moveTxId = document.getElementById('move_transaction_id');
        var moveDesc = document.getElementById('move_tx_description');
        var moveType = document.getElementById('move_tx_type');
        var moveAmount = document.getElementById('move_tx_amount');
        var moveCurrent = document.getElementById('move_current_folio');
        var moveTarget = document.getElementById('move_target_folio_id');

        document.querySelectorAll('.btn-move-folio-tx').forEach(function (btn) {
            btn.addEventListener('click', function () {
                var currentFolioId = this.getAttribute('data-current-folio-id') || '';

                if (moveTxId) moveTxId.value = this.getAttribute('data-tx-id') || '';
                if (moveDesc) moveDesc.textContent = this.getAttribute('data-description') || '—';
                if (moveType) moveType.textContent = this.getAttribute('data-type') || '—';
                if (moveAmount) moveAmount.textContent = this.getAttribute('data-amount') || '0.00';
                if (moveCurrent) moveCurrent.textContent = this.getAttribute('data-current-folio') || '—';

                if (moveTarget) {
                    moveTarget.value = '';
                    Array.prototype.forEach.call(moveTarget.options, function (opt) {
                        if (!opt.value) return;
                        opt.disabled = (String(opt.value) === String(currentFolioId));
                    });
                }
            });
        });
    }

    var currencySelect = document.getElementById('payment_currency');
    var roeInput = document.getElementById('payment_roe_atm');

    function updatePaymentRate() {
        if (!currencySelect || !roeInput) return;

        var roe = '';
        if (currencySelect.tagName && currencySelect.tagName.toLowerCase() === 'select') {
            var opt = currencySelect.options[currencySelect.selectedIndex];
            roe = opt ? (opt.getAttribute('data-roe') || '') : '';
        }

        if (roe === '') {
            roe = '1';
        }

        roeInput.value = parseFloat(roe || 1).toFixed(5);
    }

    if (currencySelect) {
        currencySelect.addEventListener('change', updatePaymentRate);
        updatePaymentRate();
    }
});
</script>

<script>
document.addEventListener('DOMContentLoaded', function () {
    var modalEl = document.getElementById('auditDetailsModal');
    if (!modalEl) return;

    var actionEl  = document.getElementById('audit_details_action');
    var dateEl    = document.getElementById('audit_details_date');
    var reasonEl  = document.getElementById('audit_details_reason');
    var payloadEl = document.getElementById('audit_details_payload');

    function prettyJson(input) {
        try {
            var obj = JSON.parse(input);
            return JSON.stringify(obj, null, 2);
        } catch (e) {
            return input || '';
        }
    }

    document.querySelectorAll('.btn-audit-details').forEach(function (btn) {
        btn.addEventListener('click', function () {
            var action  = this.getAttribute('data-audit-action') || '';
            var date    = this.getAttribute('data-audit-date') || '';
            var reason  = this.getAttribute('data-audit-reason') || '';
            var payload = this.getAttribute('data-audit-payload') || '';

            actionEl.textContent = action;
            dateEl.textContent = date;
            reasonEl.textContent = reason !== '' ? reason : '-';
            payloadEl.textContent = prettyJson(payload);

            var modal = bootstrap.Modal.getOrCreateInstance(modalEl);
            modal.show();
        });
    });
});
</script>

<script>
document.addEventListener('DOMContentLoaded', function () {
    var modalEl = document.getElementById('voidPaymentModal');
    if (!modalEl) return;

    var paymentIdField = document.getElementById('void_payment_id');
    var orderIdField   = document.getElementById('void_order_id');
    var reasonField    = document.getElementById('void_reason');
    var titleEl        = document.getElementById('voidPaymentModalLabel');

    document.querySelectorAll('.btn-void-payment').forEach(function (btn) {
        btn.addEventListener('click', function () {
            var paymentId = this.getAttribute('data-payment-id') || '';
            var orderId   = this.getAttribute('data-order-id') || '';
            var method    = this.getAttribute('data-payment-method') || 'Payment';
            var amount    = this.getAttribute('data-payment-amount') || '';
            var date      = this.getAttribute('data-payment-date') || '';

            paymentIdField.value = paymentId;
            orderIdField.value = orderId;
            reasonField.value = '';

            if (titleEl) {
                titleEl.textContent = 'Void Payment - ' + method + ' ' + amount;
            }

            var helper = modalEl.querySelector('.small.text-muted');
            if (helper) {
                helper.textContent = 'Payment date: ' + date + '. This will mark the payment as voided and post a reversal into the folio ledger.';
            }

            var modal = bootstrap.Modal.getOrCreateInstance(modalEl);
            modal.show();
        });
    });
});
</script>


<script>
document.addEventListener('DOMContentLoaded', function () {
    var currencySelect = document.getElementById('payment_currency');
    var roeInput = document.getElementById('payment_roe_atm');
    var amountInput = document.getElementById('payment_amount_entered');
    var gratuityInput = document.getElementById('payment_gratuity_amount');
    var methodSelect = document.getElementById('payment_method');
    var effectiveEl = document.getElementById('effective_usd_payment');
    var surchargeEl = document.getElementById('cc_surcharge_preview');
    var gratuityPreviewEl = document.getElementById('gratuity_preview');
    var debitCustomerEl = document.getElementById('amount_to_debit_customer_preview');
    var usdToCurrencyEl = document.getElementById('usd_to_currency_preview');
    var currencyToUsdEl = document.getElementById('currency_to_usd_preview');
    var bankAccountWrap = document.getElementById('bank_account_wrap');
    var bankAccountSelect = document.getElementById('bank_account_id');
    var posMachineWrap = document.getElementById('pos_machine_wrap');
    var posMachineSelect = document.getElementById('pos_machine_id');
    var postToSelect = document.getElementById('payment_post_to');
    var balanceHint = document.getElementById('selected_folio_balance_hint');
    var amountWasAutoFilled = true;
    var lastAutoAmount = '';

    function formatMoney(value, decimals) {
        decimals = typeof decimals === 'number' ? decimals : 2;
        return Number(value || 0).toLocaleString(undefined, {
            minimumFractionDigits: decimals,
            maximumFractionDigits: decimals
        });
    }

    function currentRoeAndCode() {
        var roe = 0;
        var code = 'Currency';

        if (currencySelect) {
            var selected = currencySelect.options[currencySelect.selectedIndex];
            if (selected) {
                code = selected.getAttribute('data-code') || selected.textContent.trim().split(' ')[0] || 'Currency';
                roe = parseFloat(selected.getAttribute('data-roe') || '0') || 0;
            }
        }

        return { roe: roe, code: code };
    }

    function selectedFolioBalanceUsd() {
        if (!postToSelect || !postToSelect.options.length) return 0;
        var selected = postToSelect.options[postToSelect.selectedIndex];
        var balance = selected ? parseFloat(selected.getAttribute('data-balance-usd') || '0') : 0;
        return isNaN(balance) ? 0 : balance;
    }

    function selectedFolioLabel() {
        if (!postToSelect || !postToSelect.options.length) return 'Selected folio';
        var selected = postToSelect.options[postToSelect.selectedIndex];
        return selected ? selected.textContent.replace(/\s+/g, ' ').trim() : 'Selected folio';
    }

    function autoFillAmountFromSelectedFolio(force) {
        if (!amountInput) return;

        var rate = currentRoeAndCode();
        var balanceUsd = selectedFolioBalanceUsd();
        var roe = rate.roe > 0 ? rate.roe : 1;
        var tendered = Math.max(0, balanceUsd * roe);
        var tenderedText = tendered > 0 ? tendered.toFixed(2) : '';

        if (force || amountWasAutoFilled || amountInput.value === '' || amountInput.value === lastAutoAmount) {
            amountInput.value = tenderedText;
            lastAutoAmount = tenderedText;
            amountWasAutoFilled = true;
        }

        if (balanceHint) {
            var label = selectedFolioLabel();
            balanceHint.innerHTML =
                '<strong>Selected:</strong> ' + label +
                '<br><strong>Balance Due:</strong> USD ' + formatMoney(balanceUsd, 2) +
                (rate.roe > 0 ? (' &nbsp; | &nbsp; <strong>Amount Tendered Default:</strong> ' + formatMoney(tendered, 2) + ' ' + rate.code) : '');
        }
    }

    function refreshPaymentSourceSelectors() {
        var method = methodSelect ? String(methodSelect.value || '').toLowerCase() : '';
        var showBank = method.indexOf('bank') !== -1 || method.indexOf('transfer') !== -1;
        var showPos = method === 'pos' || method === 'card';

        if (bankAccountWrap) bankAccountWrap.classList.toggle('d-none', !showBank);
        if (posMachineWrap) posMachineWrap.classList.toggle('d-none', !showPos);

        if (!showBank && bankAccountSelect) bankAccountSelect.value = '0';
        if (!showPos && posMachineSelect) posMachineSelect.value = '0';
    }

    function refreshPaymentRoeAndEffective() {
        refreshPaymentSourceSelectors();
        autoFillAmountFromSelectedFolio(false);

        var rate = currentRoeAndCode();
        var roe = rate.roe;
        var code = rate.code;

        if (roeInput) {
            roeInput.value = roe > 0 ? roe.toFixed(5) : '';
        }

        if (!roe || roe <= 0) {
            if (roeInput) roeInput.classList.add('is-invalid');
            if (effectiveEl) effectiveEl.textContent = 'Invalid ROE';
            if (usdToCurrencyEl) usdToCurrencyEl.textContent = 'Invalid ROE';
            if (currencyToUsdEl) currencyToUsdEl.textContent = 'Invalid ROE';
            return;
        }

        if (roeInput) roeInput.classList.remove('is-invalid');

        var entered = amountInput ? (parseFloat(amountInput.value) || 0) : 0;
        var gratuity = gratuityInput ? Math.max(0, parseFloat(gratuityInput.value || '0') || 0) : 0;
        var method = methodSelect ? String(methodSelect.value || '').toLowerCase() : '';
        var applySurcharge = method === 'pos' || method === 'card';
        var surcharge = applySurcharge ? (entered * 0.03) : 0;
        var amountToDebitCustomer = entered + gratuity + surcharge;
        var effective = amountToDebitCustomer / roe;
        var reverse = 1 / roe;

        if (usdToCurrencyEl) {
            usdToCurrencyEl.textContent = '1 USD = ' + formatMoney(roe, 5) + ' ' + code;
        }
        if (currencyToUsdEl) {
            currencyToUsdEl.textContent = '1 ' + code + ' = ' + formatMoney(reverse, 5) + ' USD';
        }
        if (gratuityPreviewEl) {
            gratuityPreviewEl.textContent = gratuity > 0 ? (formatMoney(gratuity, 2) + ' ' + code + ' (no surcharge)') : '0.00';
        }
        if (surchargeEl) {
            surchargeEl.textContent = applySurcharge ? (formatMoney(surcharge, 2) + ' ' + code + ' (3% on principal only)') : '0.00';
        }
        if (debitCustomerEl) {
            debitCustomerEl.textContent = formatMoney(amountToDebitCustomer, 2) + ' ' + code;
        }
        if (effectiveEl) {
            effectiveEl.textContent = formatMoney(effective, 2);
        }
    }

    if (currencySelect) currencySelect.addEventListener('change', function () {
        if (amountWasAutoFilled) {
            autoFillAmountFromSelectedFolio(true);
        }
        refreshPaymentRoeAndEffective();
    });
    if (amountInput) amountInput.addEventListener('input', function () {
        if (amountInput.value !== lastAutoAmount) {
            amountWasAutoFilled = false;
        }
        refreshPaymentRoeAndEffective();
    });
    if (gratuityInput) gratuityInput.addEventListener('input', refreshPaymentRoeAndEffective);
    if (methodSelect) methodSelect.addEventListener('change', refreshPaymentRoeAndEffective);
    if (postToSelect) postToSelect.addEventListener('change', function () {
        amountWasAutoFilled = true;
        autoFillAmountFromSelectedFolio(true);
        refreshPaymentRoeAndEffective();
    });

    var recordModal = document.getElementById('recordPaymentModal');
    if (recordModal) {
        recordModal.addEventListener('shown.bs.modal', function () {
            amountWasAutoFilled = true;
            autoFillAmountFromSelectedFolio(true);
            refreshPaymentRoeAndEffective();
        });
    }

    autoFillAmountFromSelectedFolio(true);
    refreshPaymentRoeAndEffective();
});
</script>

<?php require_once __DIR__ . '/../../includes/footer.php'; ?>
