<?php
/**
 * Guest payment flow: timing, methods, order state.
 *
 * @package Maca_Menu_List
 */

if (!defined('ABSPATH')) {
    exit;
}

/**
 * When guests may choose or complete payment: at_order | after_meal.
 *
 * @return string
 */
function maca_menulist_payments_timing() {
    $timing = sanitize_key((string) get_option('maca_menulist_payments_timing', 'after_meal'));

    if (!in_array($timing, array('at_order', 'after_meal'), true)) {
        return 'after_meal';
    }

    return $timing;
}

/**
 * Whether guests may choose to pay the server manually.
 *
 * @return bool
 */
function maca_menulist_payments_allow_manual() {
    return get_option('maca_menulist_payments_allow_manual', '1') === '1';
}

/**
 * Whether online payments are allowed (requires Payments → online enabled + Stripe ready).
 *
 * @return bool
 */
function maca_menulist_payments_allow_online() {
    if (!maca_menulist_payments_use_online()) {
        return false;
    }

    return maca_menulist_payments_enabled() && get_option('maca_menulist_payments_allow_online', '1') === '1';
}

/**
 * Whether any guest payment UI should appear.
 *
 * @return bool
 */
function maca_menulist_payments_guest_flow_enabled() {
    if (!maca_menulist_table_order_enabled() || !maca_menulist_table_order_uses_server()) {
        return false;
    }

    if (!maca_menulist_table_order_show_prices()) {
        return false;
    }

    return maca_menulist_payments_allow_manual()
        || (maca_menulist_payments_allow_online() && maca_menulist_payments_online_ready());
}

/**
 * Whether Stripe is configured for guest checkout.
 *
 * @return bool
 */
function maca_menulist_payments_online_ready() {
    $stripe = maca_menulist_payments_get_provider('stripe');

    return $stripe && $stripe->is_configured();
}

/**
 * Valid payment_method values stored on orders.
 *
 * @return array<int, string>
 */
function maca_menulist_payment_methods() {
    return array('online', 'manual');
}

/**
 * Sum line totals for an order with lines attached.
 *
 * @param object $order Order row with ->lines.
 * @return float
 */
function maca_menulist_calculate_table_order_total($order) {
    $total = 0.0;

    if (empty($order->lines) || !is_array($order->lines)) {
        return $total;
    }

    foreach ($order->lines as $line) {
        if ($line->price === null || $line->price === '') {
            continue;
        }
        $total += (float) $line->price * max(1, (int) $line->qty);
    }

    return round($total, 2);
}

/**
 * Whether the order has priced lines suitable for online checkout.
 *
 * @param object $order Order with lines.
 * @return bool
 */
function maca_menulist_table_order_has_checkout_total($order) {
    return maca_menulist_calculate_table_order_total($order) > 0;
}

/**
 * Whether payment choice / pay actions are available for this order state.
 *
 * @param object $order Order row.
 * @return bool
 */
function maca_menulist_order_payment_timing_allows_action($order) {
    $status = maca_menulist_normalize_table_order_status((string) $order->status);

    if (in_array($status, array('closed'), true)) {
        return false;
    }

    if (
        function_exists('maca_menulist_order_requires_takeaway_prepayment')
        && maca_menulist_order_requires_takeaway_prepayment($order)
    ) {
        $balance = maca_menulist_get_order_payment_balance($order);
        if ($balance['remaining'] > 0.009) {
            return true;
        }

        return !maca_menulist_order_is_payment_complete($order);
    }

    if (maca_menulist_payments_timing() === 'at_order') {
        return !in_array($status, array('paid', 'closed'), true);
    }

    return in_array($status, array('served', 'paid'), true);
}

/**
 * Paid, total, and remaining amounts for an order (subtotal + tip).
 *
 * @param object $order Order row with lines.
 * @return array{total: float, paid: float, remaining: float}
 */
function maca_menulist_get_order_payment_balance($order) {
    $total = round(max(0, maca_menulist_order_payment_total($order)), 2);
    $payment_status = isset($order->payment_status) ? sanitize_key((string) $order->payment_status) : 'none';

    if (maca_menulist_order_bill_split_is_active($order)) {
        $total = round(max(0, maca_menulist_order_bill_split_target_total($order)), 2);
        $paid = round(max(0, (float) ($order->payment_amount ?? 0)), 2);

        if (maca_menulist_order_is_payment_complete($order)) {
            $paid = max($paid, $total);
        }

        $paid = round(min($paid, $total), 2);

        return array(
            'total' => $total,
            'paid' => $paid,
            'remaining' => round(max(0, $total - $paid), 2),
            'split_mode' => maca_menulist_order_bill_split_mode($order),
            'split_count' => maca_menulist_order_bill_split_count($order),
            'paid_shares' => maca_menulist_order_bill_split_paid_shares($order),
        );
    }

    $paid = round(max(0, (float) ($order->payment_amount ?? 0)), 2);

    if (maca_menulist_order_is_payment_complete($order)) {
        if ($paid <= 0) {
            $paid = $total;
        } else {
            $paid = round(min($paid, $total), 2);
        }
    }

    return array(
        'total' => $total,
        'paid' => $paid,
        'remaining' => round(max(0, $total - $paid), 2),
    );
}

/**
 * Format a single payment line: amount paid and amount left (when relevant).
 *
 * @param object $order           Order row with lines.
 * @param bool   $include_thanks  Append thank-you when fully paid (guest UI).
 * @return string
 */
function maca_menulist_format_order_payment_summary_label($order, $include_thanks = false) {
    $balance = maca_menulist_get_order_payment_balance($order);

    if ($balance['total'] <= 0) {
        return '';
    }

    $currency = isset($order->payment_currency) ? (string) $order->payment_currency : '';
    $format_amount = static function ($amount) use ($currency) {
        if ($currency !== '' && function_exists('maca_menulist_format_order_total')) {
            return maca_menulist_format_order_total($amount, $currency);
        }

        return maca_menulist_format_price($amount);
    };

    $paid_label = $format_amount($balance['paid']);
    $remaining_label = $format_amount($balance['remaining']);
    $is_paid = maca_menulist_order_is_payment_complete($order);

    if (maca_menulist_order_bill_split_is_active($order) && !$is_paid) {
        if (($balance['split_mode'] ?? '') === 'equal' && !empty($balance['split_count'])) {
            return sprintf(
                /* translators: 1: shares paid, 2: total shares, 3: amount remaining */
                __('%1$d of %2$d paid · %3$s left', 'maca-restu-pro'),
                (int) $balance['paid_shares'],
                (int) $balance['split_count'],
                $remaining_label
            );
        }

        if (($balance['split_mode'] ?? '') === 'fixed') {
            if ((int) $balance['paid_shares'] > 0) {
                return sprintf(
                    /* translators: 1: amount paid, 2: amount remaining */
                    __('%1$s paid · %2$s left', 'maca-restu-pro'),
                    $paid_label,
                    $remaining_label
                );
            }

            return sprintf(
                /* translators: %s: amount remaining */
                __('%s left to pay', 'maca-restu-pro'),
                $remaining_label
            );
        }
    }

    $tx_state = function_exists('maca_menulist_build_order_payment_transactions_state')
        ? maca_menulist_build_order_payment_transactions_state($order)
        : array('count' => 0);

    if ($is_paid && !empty($tx_state['count']) && $tx_state['count'] > 1) {
        if ($include_thanks) {
            return sprintf(
                /* translators: 1: payment breakdown, 2: total paid */
                __('Paid %1$s (total %2$s) — thank you!', 'maca-restu-pro'),
                $tx_state['amounts_breakdown'],
                $paid_label
            );
        }

        return sprintf(
            /* translators: 1: payment breakdown, 2: total paid */
            __('Paid %1$s (total %2$s)', 'maca-restu-pro'),
            $tx_state['amounts_breakdown'],
            $paid_label
        );
    }

    if ($is_paid) {
        if ($balance['remaining'] > 0) {
            return sprintf(
                /* translators: 1: amount paid, 2: amount remaining */
                __('Paid %1$s · %2$s left to pay', 'maca-restu-pro'),
                $paid_label,
                $remaining_label
            );
        }

        if ($include_thanks) {
            return sprintf(
                /* translators: %s: amount paid */
                __('Paid %s — thank you!', 'maca-restu-pro'),
                $paid_label
            );
        }

        return sprintf(
            /* translators: %s: amount paid */
            __('Paid %s', 'maca-restu-pro'),
            $paid_label
        );
    }

    if ($balance['paid'] > 0 && $balance['remaining'] > 0) {
        if (!empty($tx_state['count']) && $tx_state['count'] > 1) {
            return sprintf(
                /* translators: 1: payments so far, 2: amount remaining */
                __('%1$s paid so far · %2$s left', 'maca-restu-pro'),
                $tx_state['amounts_breakdown'],
                $remaining_label
            );
        }

        return sprintf(
            /* translators: 1: amount paid, 2: amount remaining */
            __('%1$s paid · %2$s left', 'maca-restu-pro'),
            $paid_label,
            $remaining_label
        );
    }

    if ($balance['remaining'] > 0) {
        return sprintf(
            /* translators: %s: amount remaining */
            __('%s left to pay', 'maca-restu-pro'),
            $remaining_label
        );
    }

    return '';
}

/**
 * Guest payment block for JSON responses.
 *
 * @param object $order Order with lines.
 * @return array<string, mixed>
 */
function maca_menulist_build_guest_payment_state($order) {
    if (!maca_menulist_payments_guest_flow_enabled()) {
        return array('enabled' => false);
    }

    $payment_status = isset($order->payment_status) ? sanitize_key((string) $order->payment_status) : 'none';
    $payment_method = isset($order->payment_method) ? sanitize_key((string) $order->payment_method) : '';
    $order_status = maca_menulist_normalize_table_order_status((string) $order->status);
    $timing = maca_menulist_payments_timing();
    $allow_manual = maca_menulist_payments_allow_manual();
    $allow_online = maca_menulist_payments_allow_online() && maca_menulist_payments_online_ready();
    $has_total = maca_menulist_table_order_has_checkout_total($order);
    $timing_ok = maca_menulist_order_payment_timing_allows_action($order);
    $is_paid = maca_menulist_order_is_payment_complete($order);
    $show_checkout_ui = $timing_ok
        || $is_paid
        || $payment_method !== ''
        || $payment_status === 'pending';

    $can_choose = $timing_ok
        && !$is_paid
        && $payment_status !== 'pending'
        && $payment_method === ''
        && ($allow_manual || ($allow_online && $has_total));

    $effective_payment_status = maca_menulist_order_effective_payment_status($order);

    $can_pay_online = $timing_ok
        && !$is_paid
        && $payment_method !== 'manual'
        && $allow_online
        && $has_total
        && in_array($effective_payment_status, array('none', 'failed', 'pending', 'partial'), true);

    if (
        maca_menulist_order_bill_split_is_active($order)
        && !$is_paid
        && maca_menulist_order_bill_split_next_amount($order) > 0.009
        && $timing_ok
        && $allow_online
        && $has_total
        && $payment_method !== 'manual'
        && $payment_status !== 'pending'
    ) {
        $can_pay_online = true;
    }

    $subtotal = maca_menulist_get_order_subtotal($order);
    $tip_amount = maca_menulist_get_order_tip_amount($order);
    $grand_total = maca_menulist_order_payment_total($order);
    $tips_on = maca_menulist_tips_enabled() && $subtotal > 0 && $show_checkout_ui;

    $state = array(
        'enabled' => true,
        'timing' => $timing,
        'timing_ok' => $timing_ok,
        'show_checkout_ui' => $show_checkout_ui,
        'allow_manual' => $allow_manual,
        'allow_online' => $allow_online,
        'show_email_confirmation' => $show_checkout_ui && $allow_online && $has_total && !$is_paid,
        'can_choose' => $can_choose,
        'can_pay_online' => $can_pay_online,
        'method' => $payment_method !== '' ? $payment_method : null,
        'status' => $payment_status,
        'is_paid' => $is_paid,
        'order_status' => $order_status,
        'subtotal' => $subtotal,
        'subtotal_label' => $subtotal > 0 ? maca_menulist_format_price($subtotal) : '',
        'tip_amount' => $tip_amount,
        'tip_label' => $tip_amount > 0 ? maca_menulist_format_price($tip_amount) : '',
        'total' => $grand_total,
        'total_label' => $grand_total > 0 ? maca_menulist_format_price($grand_total) : '',
        'tips_enabled' => $tips_on,
        'tip_choices' => $tips_on ? maca_menulist_get_guest_tip_choices($order) : array(),
        'tips_mode' => maca_menulist_tips_mode(),
        'tips_allow_custom' => maca_menulist_tips_allow_custom(),
        'tips_allow_none' => maca_menulist_tips_allow_none(),
        'tip_ui_locked' => (string) $order->payment_method !== ''
            || $payment_status === 'pending'
            || $is_paid,
        'tip_selected' => (string) $order->payment_method !== ''
            || $payment_status === 'pending'
            || $is_paid,
    );

    if ($grand_total > 0 && !$can_choose) {
        $balance = maca_menulist_get_order_payment_balance($order);
        $currency = isset($order->payment_currency) ? (string) $order->payment_currency : '';
        $format_amount = static function ($amount) use ($currency) {
            if ($currency !== '' && function_exists('maca_menulist_format_order_total')) {
                return maca_menulist_format_order_total($amount, $currency);
            }

            return maca_menulist_format_price($amount);
        };

        $state['amount_paid_label'] = $balance['paid'] > 0 ? $format_amount($balance['paid']) : '';
        $state['amount_remaining_label'] = $balance['remaining'] > 0 ? $format_amount($balance['remaining']) : '';
        $summary = maca_menulist_format_order_payment_summary_label($order, $is_paid);

        if ($summary !== '') {
            $state['payment_summary_label'] = $summary;
        }

        $tx_state = maca_menulist_build_order_payment_transactions_state($order);
        $state['payment_transactions'] = $tx_state['items'];
        $state['payment_transaction_count'] = $tx_state['count'];
        $state['payment_transactions_label'] = $tx_state['summary_label'];
    }

    if (
        function_exists('maca_menulist_order_requires_takeaway_prepayment')
        && maca_menulist_order_requires_takeaway_prepayment($order)
        && !$is_paid
    ) {
        $state['message'] = __('Pay now — the kitchen will start preparing your order once payment is complete.', 'maca-restu-pro');
    } elseif ($is_paid) {
        $state['message'] = '';
    } elseif ($payment_method === 'manual' && !$is_paid) {
        $state['message'] = __('You chose to pay your server. Please pay when they come to your table.', 'maca-restu-pro');
    } elseif ($payment_status === 'pending') {
        $state['message'] = __('Payment started — complete checkout or try again.', 'maca-restu-pro');
    } elseif (!$timing_ok && $timing === 'after_meal') {
        $state['message'] = __('You can pay when your order has been served.', 'maca-restu-pro');
    } elseif (!$timing_ok) {
        $state['message'] = '';
    }

    if (
        function_exists('maca_menulist_order_requires_takeaway_prepayment')
        && maca_menulist_order_requires_takeaway_prepayment($order)
    ) {
        $state['bill_split'] = array('enabled' => false);
    } else {
        $state['bill_split'] = maca_menulist_build_order_bill_split_state($order);
    }

    return $state;
}

/**
 * Human-readable payment_status label for admin and reports.
 *
 * @param string $status Payment status key.
 * @return string
 */
function maca_menulist_get_payment_status_label($status) {
    $status = sanitize_key((string) $status);
    $labels = array(
        'none'      => __('Not paid', 'maca-restu-pro'),
        'pending'   => __('Payment pending', 'maca-restu-pro'),
        'partial'   => __('Partially paid', 'maca-restu-pro'),
        'paid'      => __('Paid', 'maca-restu-pro'),
        'failed'    => __('Payment failed', 'maca-restu-pro'),
        'refunded'  => __('Refunded', 'maca-restu-pro'),
        'cancelled' => __('Payment cancelled', 'maca-restu-pro'),
    );

    return isset($labels[$status]) ? $labels[$status] : $status;
}

/**
 * Payment status for display and filters (fixes legacy paid + partial amount).
 *
 * @param object $order Order row (order_total from list query is enough for totals).
 * @return string
 */
function maca_menulist_order_effective_payment_status($order) {
    if (!$order) {
        return 'none';
    }

    $stored = sanitize_key((string) ($order->payment_status ?? 'none'));

    if (in_array($stored, array('pending', 'failed', 'refunded', 'cancelled'), true)) {
        return $stored;
    }

    if (maca_menulist_order_is_payment_complete($order)) {
        return 'paid';
    }

    $paid = round(max(0, (float) ($order->payment_amount ?? 0)), 2);

    if ($paid > 0.009 || $stored === 'partial') {
        return 'partial';
    }

    return $stored === 'none' ? 'none' : $stored;
}

/**
 * Human-readable payment status for one order.
 *
 * @param object $order Order row.
 * @return string
 */
function maca_menulist_get_order_payment_status_label($order) {
    return maca_menulist_get_payment_status_label(maca_menulist_order_effective_payment_status($order));
}

/**
 * Amount column for admin payment history (paid / total when partially paid).
 *
 * @param object $order    Order row.
 * @param string $currency Currency code.
 * @return string
 */
function maca_menulist_format_order_payment_amount_display($order, $currency = '') {
    if (!$order) {
        return '—';
    }

    $currency = $currency !== '' ? $currency : maca_menulist_get_price_currency();
    $balance = maca_menulist_get_order_payment_balance($order);

    if ($balance['total'] <= 0) {
        return '—';
    }

    if (function_exists('maca_menulist_format_order_payment_transactions_short')) {
        $tx_short = maca_menulist_format_order_payment_transactions_short($order);
        if ($tx_short !== '') {
            $total_label = maca_menulist_format_order_total($balance['total'], $currency);
            return $tx_short . ' (' . sprintf(
                /* translators: %s: order total */
                __('total %s', 'maca-restu-pro'),
                $total_label
            ) . ')';
        }
    }

    if ($balance['paid'] > 0 && $balance['remaining'] > 0.009) {
        return sprintf(
            '%s / %s',
            maca_menulist_format_order_total($balance['paid'], $currency),
            maca_menulist_format_order_total($balance['total'], $currency)
        );
    }

    if ($balance['paid'] > 0) {
        return maca_menulist_format_order_total($balance['paid'], $currency);
    }

    if (maca_menulist_order_is_payment_complete($order)) {
        return maca_menulist_format_order_total($balance['total'], $currency);
    }

    return '—';
}

/**
 * Human-readable payment_method label.
 *
 * @param string $method online|manual or empty.
 * @return string
 */
function maca_menulist_get_payment_method_label($method) {
    $method = sanitize_key((string) $method);

    if ($method === '') {
        return '—';
    }

    $labels = array(
        'online' => __('Online (Stripe)', 'maca-restu-pro'),
        'manual' => __('With server', 'maca-restu-pro'),
    );

    return isset($labels[$method]) ? $labels[$method] : $method;
}

/**
 * Count orders matching payment history filters.
 *
 * @param array<string, mixed> $args Query args (see maca_menulist_db_build_table_orders_query).
 * @return int
 */
function maca_menulist_db_count_payment_history_orders($args = array()) {
    return maca_menulist_db_count_table_orders($args);
}

/**
 * Summary counts for payment history dashboard.
 *
 * @param array<string, mixed> $args Base filters (date range, table, etc.).
 * @return array{total: int, paid: int, paid_online: int, paid_manual: int, partial: int, pending: int, unpaid: int}
 */
function maca_menulist_get_payment_history_summary($args = array()) {
    $base = $args;
    $base['payment_status_filter'] = 'all';
    $base['payment_method_filter'] = 'all';

    $paid_args = array_merge($base, array('payment_status_filter' => 'paid'));
    $paid_online_args = array_merge($base, array(
        'payment_status_filter' => 'paid',
        'payment_method_filter' => 'online',
    ));
    $paid_manual_args = array_merge($base, array(
        'payment_status_filter' => 'paid',
        'payment_method_filter' => 'manual',
    ));
    $pending_args = array_merge($base, array('payment_status_filter' => 'pending'));
    $partial_args = array_merge($base, array('payment_status_filter' => 'partial'));
    $unpaid_args = array_merge($base, array('payment_status_filter' => 'unpaid'));

    return array(
        'total'       => maca_menulist_db_count_table_orders($base),
        'paid'        => maca_menulist_db_count_table_orders($paid_args),
        'paid_online' => maca_menulist_db_count_table_orders($paid_online_args),
        'paid_manual' => maca_menulist_db_count_table_orders($paid_manual_args),
        'partial'     => maca_menulist_db_count_table_orders($partial_args),
        'pending'     => maca_menulist_db_count_table_orders($pending_args),
        'unpaid'      => maca_menulist_db_count_table_orders($unpaid_args),
    );
}

/**
 * Provider label for admin (stripe, manual, swish).
 *
 * @param string $provider Provider key or empty.
 * @return string
 */
function maca_menulist_get_payment_provider_label($provider) {
    $provider = sanitize_key((string) $provider);

    if ($provider === '') {
        return '—';
    }

    $labels = array(
        'stripe' => __('Stripe', 'maca-restu-pro'),
        'manual' => __('Manual / cash', 'maca-restu-pro'),
        'swish'  => __('Swish', 'maca-restu-pro'),
    );

    return isset($labels[$provider]) ? $labels[$provider] : $provider;
}

/**
 * Payment summary for staff JSON.
 *
 * @param object $order Order row.
 * @param bool   $lite  Omit receipt rows and bill-split details for staff polling.
 * @return array<string, mixed>
 */
function maca_menulist_build_staff_payment_state($order, $lite = false) {
    $payment_status = maca_menulist_order_effective_payment_status($order);
    $payment_method = isset($order->payment_method) ? sanitize_key((string) $order->payment_method) : '';
    $payment_complete = maca_menulist_order_is_payment_complete($order);

    if ($lite) {
        $badge = '';
        if ($payment_complete) {
            $badge = __('Paid', 'maca-restu-pro');
        } elseif ($payment_status === 'pending') {
            $badge = __('Paying online', 'maca-restu-pro');
        } elseif ($payment_method === 'manual') {
            $badge = __('Pay at table', 'maca-restu-pro');
        }

        return array(
            'payment_status' => $payment_status,
            'payment_method' => $payment_method !== '' ? $payment_method : null,
            'payment_is_complete' => $payment_complete,
            'badge' => $badge,
            'payment_summary_label' => '',
            'show_receipt' => false,
        );
    }

    $subtotal = maca_menulist_get_order_subtotal($order);
    $tip_amount = maca_menulist_get_order_tip_amount($order);
    $grand_total = maca_menulist_order_payment_total($order);

    $badge = '';
    if ($payment_method === 'manual' && $payment_status !== 'paid') {
        $badge = __('Pay at table', 'maca-restu-pro');
    } elseif ($payment_status === 'pending') {
        $badge = __('Paying online', 'maca-restu-pro');
    }

    $payment_summary_label = $grand_total > 0
        ? maca_menulist_format_order_payment_summary_label($order, false)
        : '';

    $payment_complete = maca_menulist_order_is_payment_complete($order);
    $balance = maca_menulist_get_order_payment_balance($order);

    if ($payment_summary_label !== '') {
        $badge = $payment_summary_label;
    } elseif ($payment_complete) {
        $badge = __('Paid', 'maca-restu-pro');
    }

    $state = array(
        'payment_status' => $payment_status,
        'payment_method' => $payment_method !== '' ? $payment_method : null,
        'payment_is_complete' => $payment_complete,
        'badge' => $badge,
        'payment_summary_label' => $payment_summary_label,
        'subtotal_label' => $subtotal > 0 ? maca_menulist_format_price($subtotal) : '',
        'tip_label' => $tip_amount > 0 ? maca_menulist_format_price($tip_amount) : '',
        'total_label' => $grand_total > 0 ? maca_menulist_format_price($grand_total) : '',
        'show_receipt' => false,
    );

    if (!$lite) {
        $state['bill_split'] = maca_menulist_build_order_bill_split_state($order);
    }

    $show_receipt = (
        $payment_complete
        || $payment_status === 'pending'
        || ($payment_method === 'manual' && !$payment_complete)
        || $balance['paid'] > 0
        || (function_exists('maca_menulist_order_bill_split_is_active') && maca_menulist_order_bill_split_is_active($order))
    );

    if ($show_receipt) {
        $state['show_receipt'] = true;
        $currency_code = isset($order->payment_currency) ? (string) $order->payment_currency : maca_menulist_get_price_currency();
        $format_amount = static function ($amount) use ($currency_code) {
            return maca_menulist_format_order_total($amount, $currency_code);
        };

        if ($payment_summary_label === '') {
            if ($balance['paid'] > 0) {
                $state['amount_paid_label'] = $format_amount($balance['paid']);
            } elseif ($grand_total > 0) {
                $state['amount_paid_label'] = maca_menulist_format_price($grand_total);
            }
        }

        if ($balance['remaining'] > 0) {
            $state['amount_remaining_label'] = $format_amount($balance['remaining']);
        }

        $state['method_label'] = maca_menulist_get_payment_method_label($payment_method);
        $state['provider_label'] = maca_menulist_get_payment_provider_label(isset($order->payment_provider) ? (string) $order->payment_provider : '');

        if ($payment_complete && !empty($order->paid_at)) {
            $state['paid_at_label'] = maca_menulist_format_payment_datetime((string) $order->paid_at);
        } elseif ($payment_status === 'pending') {
            $state['paid_at_label'] = __('Awaiting payment', 'maca-restu-pro');
        }

        $tx_state = maca_menulist_build_order_payment_transactions_state($order);
        $state['payment_transactions'] = $tx_state['items'];
        $state['payment_transaction_count'] = $tx_state['count'];
        $state['payment_transactions_label'] = $tx_state['summary_label'];

        if ($tx_state['count'] > 1) {
            $state['payment_ids_label'] = $tx_state['summary_label'];
        } elseif ($tx_state['count'] === 1 && !empty($tx_state['items'][0]['external_id'])) {
            $state['payment_id'] = (string) $tx_state['items'][0]['external_id'];
        } elseif (!empty($order->payment_external_id)) {
            $state['payment_id'] = (string) $order->payment_external_id;
        }
    }

    return $state;
}

/**
 * Return URL after Stripe Checkout.
 *
 * @param string $guest_token Guest token.
 * @param string $result      success|cancel.
 * @return string
 */
function maca_menulist_payments_return_url($guest_token, $result = 'success') {
    $guest_token = sanitize_text_field($guest_token);
    $base = '';

    if (!empty($_POST['return_url'])) {
        $base = esc_url_raw(wp_unslash($_POST['return_url']));
    }

    if ($base === '') {
        $base = wp_get_referer();
    }

    if ($base === '') {
        $base = home_url('/');
    }

    $base = remove_query_arg(array('maca_payment', 'maca_order'), $base);

    return add_query_arg(
        array(
            'maca_payment' => sanitize_key($result),
            'maca_order' => $guest_token,
        ),
        $base
    );
}

/**
 * Set how the guest will pay.
 *
 * @param int    $order_id Order ID.
 * @param string $method   online|manual.
 * @return true|WP_Error
 */
function maca_menulist_db_set_order_payment_method($order_id, $method) {
    global $wpdb;

    $method = sanitize_key($method);
    if (!in_array($method, maca_menulist_payment_methods(), true)) {
        return new WP_Error('invalid_method', __('Invalid payment method.', 'maca-restu-pro'));
    }

    maca_menulist_db_ensure_table_order_tables();

    $order = maca_menulist_db_get_table_order($order_id);
    if (!$order) {
        return new WP_Error('not_found', __('Order not found.', 'maca-restu-pro'));
    }

    if (maca_menulist_order_is_payment_complete($order)) {
        return new WP_Error('already_paid', __('This order is already paid.', 'maca-restu-pro'));
    }

    if ((string) $order->payment_method !== '' && (string) $order->payment_method !== $method) {
        $payment_status = isset($order->payment_status) ? sanitize_key((string) $order->payment_status) : 'none';
        $can_switch = in_array($payment_status, array('none', 'failed', 'cancelled'), true)
            && !maca_menulist_order_is_payment_complete($order);

        if (!$can_switch) {
            return new WP_Error('method_locked', __('Payment method cannot be changed.', 'maca-restu-pro'));
        }
    }

    if (!maca_menulist_order_payment_timing_allows_action($order)) {
        return new WP_Error('timing', __('Payment is not available for this order yet.', 'maca-restu-pro'));
    }

    $orders_table = maca_menulist_db_table_orders_table();
    $updated = $wpdb->update(
        $orders_table,
        array('payment_method' => $method),
        array('id' => (int) $order_id),
        array('%s'),
        array('%d')
    );

    if ($updated === false) {
        return new WP_Error('db', __('Could not save payment choice.', 'maca-restu-pro'));
    }

    return true;
}

/**
 * Mark order as paid (online webhook or staff).
 *
 * @param int                  $order_id Order ID.
 * @param array<string, mixed> $args     provider, external_id, amount, method.
 * @return bool
 */
function maca_menulist_db_mark_table_order_paid($order_id, $args = array()) {
    global $wpdb;

    maca_menulist_db_ensure_table_order_tables();

    $order_id = absint($order_id);
    $order = maca_menulist_db_get_table_order($order_id);

    if (!$order) {
        return false;
    }

    $provider = isset($args['payment_provider']) ? sanitize_key((string) $args['payment_provider']) : 'manual';
    $method = isset($args['payment_method']) ? sanitize_key((string) $args['payment_method']) : '';
    $external_id = isset($args['payment_external_id']) ? sanitize_text_field((string) $args['payment_external_id']) : '';
    $order_with_lines = maca_menulist_db_attach_table_order_lines(array($order))[0];
    $amount = isset($args['payment_amount'])
        ? (float) $args['payment_amount']
        : maca_menulist_order_payment_total($order_with_lines);
    $currency = isset($args['payment_currency']) ? sanitize_text_field((string) $args['payment_currency']) : 'SEK';
    $order_total = round(maca_menulist_order_payment_total($order_with_lines), 2);

    if ($order_total > 0 && round(max(0, $amount), 2) + 0.009 < $order_total) {
        $args['payment_amount'] = round(max(0, $amount), 2);

        return maca_menulist_db_apply_order_payment_partial($order_id, $args);
    }

    if ($method === '' && $provider === 'manual') {
        $method = 'manual';
    }
    if ($method === '' && $provider === 'stripe') {
        $method = 'online';
    }

    $orders_table = maca_menulist_db_table_orders_table();
    if ($provider === '') {
        $provider = 'manual';
    }

    $data = array(
        'payment_status' => 'paid',
        'payment_provider' => $provider,
        'payment_external_id' => $external_id !== '' ? $external_id : null,
        'payment_amount' => $amount > 0 ? $amount : null,
        'payment_currency' => $currency,
        'paid_at' => maca_menulist_wp_now_mysql(),
    );

    if ($method !== '') {
        $data['payment_method'] = $method;
    }

    $formats = array('%s', '%s', '%s', '%f', '%s', '%s', '%s');

    $updated = (bool) $wpdb->update(
        $orders_table,
        $data,
        array('id' => $order_id),
        $formats,
        array('%d')
    );

    if ($updated && function_exists('maca_menulist_db_record_order_payment_transaction')) {
        maca_menulist_db_record_order_payment_transaction($order_id, $args);
    }

    return $updated;
}

/**
 * Update payment fields when checkout starts.
 *
 * @param int    $order_id    Order ID.
 * @param string $provider_id Provider id.
 * @param string $session_id  External session id.
 * @param float  $amount      Amount.
 * @return bool
 */
/**
 * Clear an abandoned online checkout so the guest can pay again.
 *
 * @param int  $order_id     Order ID.
 * @param bool $reset_method When true, unlock payment method choice (Stripe cancel return).
 * @return bool
 */
function maca_menulist_db_clear_order_payment_pending($order_id, $reset_method = false) {
    global $wpdb;

    maca_menulist_db_ensure_table_order_tables();

    $orders_table = maca_menulist_db_table_orders_table();
    $data = array(
        'payment_status' => 'none',
        'payment_external_id' => null,
    );
    $formats = array('%s', '%s');

    if ($reset_method) {
        $data['payment_method'] = '';
        $data['payment_provider'] = '';
        $formats = array('%s', '%s', '%s', '%s');
    }

    return (bool) $wpdb->update(
        $orders_table,
        $data,
        array('id' => absint($order_id)),
        $formats,
        array('%d')
    );
}

/**
 * Clear pending checkout for every order tied to the same Stripe session.
 *
 * @param string $session_id   Stripe Checkout session id.
 * @param bool   $reset_method When true, unlock payment method choice.
 * @return bool
 */
function maca_menulist_db_clear_combined_payment_pending_by_session($session_id, $reset_method = false) {
    global $wpdb;

    $session_id = sanitize_text_field((string) $session_id);
    if ($session_id === '') {
        return false;
    }

    maca_menulist_db_ensure_table_order_tables();
    $orders_table = maca_menulist_db_table_orders_table();

    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    $rows = $wpdb->get_results(
        $wpdb->prepare(
            "SELECT id FROM $orders_table WHERE payment_external_id = %s AND payment_status = 'pending'",
            $session_id
        )
    );

    if (empty($rows)) {
        return false;
    }

    $cleared = false;
    foreach ($rows as $row) {
        if (maca_menulist_db_clear_order_payment_pending((int) $row->id, $reset_method)) {
            $cleared = true;
        }
    }

    return $cleared;
}

/**
 * Reset pending checkout when Stripe redirects back with cancel.
 */
function maca_menulist_handle_payment_return() {
    if (!isset($_GET['maca_payment'], $_GET['maca_order'])) {
        return;
    }

    $result = sanitize_key(wp_unslash($_GET['maca_payment']));
    if ($result !== 'cancel') {
        return;
    }

    $guest_token = sanitize_text_field(wp_unslash($_GET['maca_order']));
    if ($guest_token === '') {
        return;
    }

    $order = maca_menulist_get_guest_order_for_token($guest_token);
    if (!$order) {
        return;
    }

    if (sanitize_key((string) ($order->payment_status ?? 'none')) !== 'pending') {
        return;
    }

    $session_id = isset($order->payment_external_id) ? sanitize_text_field((string) $order->payment_external_id) : '';
    if ($session_id !== '') {
        maca_menulist_db_clear_combined_payment_pending_by_session($session_id, true);
        return;
    }

    maca_menulist_db_clear_order_payment_pending((int) $order->id, true);
}
add_action('init', 'maca_menulist_handle_payment_return', 5);

/**
 * Align stored payment_status with line totals (e.g. after add-on lines).
 *
 * @param int $order_id Order ID.
 * @return void
 */
function maca_menulist_db_sync_order_payment_status_from_totals($order_id) {
    global $wpdb;

    $order_id = absint($order_id);
    if ($order_id <= 0) {
        return;
    }

    $order = maca_menulist_db_get_table_order($order_id);
    if (!$order) {
        return;
    }

    $orders = maca_menulist_db_attach_table_order_lines(array($order));
    $order = !empty($orders[0]) ? $orders[0] : $order;

    $stored = sanitize_key((string) ($order->payment_status ?? 'none'));
    $effective = maca_menulist_order_effective_payment_status($order);

    if ($effective === $stored) {
        return;
    }

    if (!in_array($effective, array('none', 'partial', 'paid', 'pending', 'failed'), true)) {
        return;
    }

    maca_menulist_db_ensure_table_order_tables();

    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    $wpdb->update(
        maca_menulist_db_table_orders_table(),
        array('payment_status' => $effective),
        array('id' => $order_id),
        array('%s'),
        array('%d')
    );
}

function maca_menulist_db_set_order_payment_pending($order_id, $provider_id, $session_id, $amount) {
    global $wpdb;

    maca_menulist_db_ensure_table_order_tables();

    $orders_table = maca_menulist_db_table_orders_table();

    // Do not set payment_amount while pending — webhook must not add the same share twice (split bill bug).
    return (bool) $wpdb->update(
        $orders_table,
        array(
            'payment_method' => 'online',
            'payment_status' => 'pending',
            'payment_provider' => sanitize_key($provider_id),
            'payment_external_id' => sanitize_text_field($session_id),
            'payment_currency' => maca_menulist_get_price_currency(),
        ),
        array('id' => absint($order_id)),
        array('%s', '%s', '%s', '%s', '%s'),
        array('%d')
    );
}

/**
 * Verify guest token and return order with lines.
 *
 * @param string $guest_token Token.
 * @return object|null
 */
function maca_menulist_get_guest_order_for_token($guest_token) {
    $guest_token = sanitize_text_field($guest_token);
    if ($guest_token === '') {
        return null;
    }

    $order = maca_menulist_db_get_table_order_by_token($guest_token);
    if (!$order) {
        return null;
    }

    $orders = maca_menulist_db_attach_table_order_lines(array($order));
    $order = $orders[0];

    if (function_exists('maca_menulist_order_reconcile_mistaken_split_paid_status')) {
        $order = maca_menulist_order_reconcile_mistaken_split_paid_status($order);
    }

    return $order;
}

/**
 * Format payment datetime for staff and guest displays.
 *
 * @param string $datetime MySQL datetime.
 * @return string
 */
function maca_menulist_format_payment_datetime($datetime) {
    return maca_menulist_format_wp_datetime($datetime, '', '');
}

/**
 * Guest payment config for table-order.js.
 *
 * @param string $context website|qr.
 * @return array<string, mixed>
 */
function maca_menulist_get_guest_payment_script_config($context = 'website') {
    $takeaway_requires_prepayment = false;
    if (
        ($context === 'takeaway' || (function_exists('maca_menulist_is_takeaway_order_channel') && maca_menulist_is_takeaway_order_channel()))
        && function_exists('maca_menulist_takeaway_prepayment_enabled')
    ) {
        $takeaway_requires_prepayment = maca_menulist_takeaway_prepayment_enabled();
    }

    $return_notice = null;
    if (isset($_GET['maca_payment'], $_GET['maca_order'])) {
        $result = sanitize_key(wp_unslash($_GET['maca_payment']));
        if ($result === 'success') {
            $return_notice = array(
                'type' => 'success',
                'token' => sanitize_text_field(wp_unslash($_GET['maca_order'])),
                'message' => __('Thank you — your payment was received.', 'maca-restu-pro'),
            );
        } elseif ($result === 'cancel') {
            $return_notice = array(
                'type' => 'cancel',
                'token' => sanitize_text_field(wp_unslash($_GET['maca_order'])),
                'message' => __('Payment was cancelled. You can try again when ready.', 'maca-restu-pro'),
            );
        }
    }

    return array(
        'flowEnabled' => maca_menulist_payments_guest_flow_enabled(),
        'onlineEnabled' => maca_menulist_payments_allow_online() && maca_menulist_payments_online_ready(),
        'manualEnabled' => maca_menulist_payments_allow_manual(),
        'takeawayRequiresPrepayment' => $takeaway_requires_prepayment,
        'timing' => maca_menulist_payments_timing(),
        'tipsEnabled' => maca_menulist_tips_enabled(),
        'tipsMode' => maca_menulist_tips_mode(),
        'tipsAllowCustom' => maca_menulist_tips_allow_custom(),
        'returnNotice' => $return_notice,
        'pageUrl' => maca_menulist_get_current_page_url_for_payments(),
    );
}

/**
 * Current request URL without payment query args (for Stripe return).
 *
 * @return string
 */
function maca_menulist_get_current_page_url_for_payments() {
    $uri = isset($_SERVER['REQUEST_URI']) ? wp_unslash($_SERVER['REQUEST_URI']) : '/';
    $url = home_url($uri);

    return remove_query_arg(array('maca_payment', 'maca_order', 'maca_orders'), $url);
}
