<?php
/**
 * Combined guest payment for multiple unpaid table orders.
 *
 * @package Maca_Menu_List
 */

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

/**
 * Whether an order can be included in a combined guest payment.
 *
 * @param object $order Order with lines.
 * @return bool
 */
function maca_menulist_order_eligible_for_combined_payment($order, $is_primary = false) {
    if (!$order || !maca_menulist_payments_guest_flow_enabled()) {
        return false;
    }

    if (!maca_menulist_table_order_has_checkout_total($order)) {
        return false;
    }

    if (maca_menulist_order_is_payment_complete($order)) {
        return false;
    }

    if (maca_menulist_order_bill_split_is_active($order) && !$is_primary) {
        return false;
    }

    if (!maca_menulist_order_payment_timing_allows_action($order)) {
        return 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) : '';

    if ($payment_status === 'pending') {
        return false;
    }

    if ($payment_method !== '' && $payment_method !== 'online') {
        return false;
    }

    $split_in_progress = $is_primary
        && maca_menulist_order_bill_split_is_active($order)
        && !maca_menulist_order_bill_split_is_complete($order);

    $balance = maca_menulist_get_order_payment_balance($order);

    if (!$split_in_progress && $balance['paid'] > 0.009) {
        return false;
    }

    return $balance['remaining'] > 0.009;
}

/**
 * Whether two orders belong to the same combined-payment group.
 *
 * @param object $order_a Order row.
 * @param object $order_b Order row.
 * @return bool
 */
function maca_menulist_orders_share_combined_payment_group($order_a, $order_b) {
    if (!$order_a || !$order_b) {
        return false;
    }

    $mode_a = function_exists('maca_menulist_get_order_service_mode')
        ? maca_menulist_get_order_service_mode($order_a)
        : 'dine_in';
    $mode_b = function_exists('maca_menulist_get_order_service_mode')
        ? maca_menulist_get_order_service_mode($order_b)
        : 'dine_in';

    if ($mode_a !== $mode_b) {
        return false;
    }

    if ($mode_a === 'takeaway') {
        $phone_a = isset($order_a->pickup_phone) ? preg_replace('/\s+/', '', (string) $order_a->pickup_phone) : '';
        $phone_b = isset($order_b->pickup_phone) ? preg_replace('/\s+/', '', (string) $order_b->pickup_phone) : '';
        $name_a = isset($order_a->pickup_name) ? strtolower(trim((string) $order_a->pickup_name)) : '';
        $name_b = isset($order_b->pickup_name) ? strtolower(trim((string) $order_b->pickup_name)) : '';

        return $phone_a !== '' && $phone_a === $phone_b && $name_a === $name_b;
    }

    $table_a = isset($order_a->table_number) ? trim((string) $order_a->table_number) : '';
    $table_b = isset($order_b->table_number) ? trim((string) $order_b->table_number) : '';

    return $table_a !== '' && $table_a === $table_b;
}

/**
 * Find sibling orders that share a combined bill split on the primary order.
 *
 * @param object $reference_order Order row (with lines when totals are needed).
 * @return array<int, object>|null Primary order first, or null when not a combined split group.
 */
function maca_menulist_discover_combined_payment_group_orders($reference_order) {
    global $wpdb;

    if (!$reference_order || !maca_menulist_payments_guest_flow_enabled()) {
        return null;
    }

    maca_menulist_db_ensure_table_order_tables();
    $orders_table = maca_menulist_db_table_orders_table();

    $service_mode = function_exists('maca_menulist_get_order_service_mode')
        ? maca_menulist_get_order_service_mode($reference_order)
        : 'dine_in';

    if ($service_mode === 'takeaway') {
        $phone = isset($reference_order->pickup_phone) ? preg_replace('/\s+/', '', (string) $reference_order->pickup_phone) : '';
        $name = isset($reference_order->pickup_name) ? strtolower(trim((string) $reference_order->pickup_name)) : '';
        if ($phone === '' || $name === '') {
            return null;
        }

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $rows = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT * FROM $orders_table WHERE status <> 'closed' AND service_mode = %s ORDER BY id ASC",
                'takeaway'
            )
        );
        $candidates = array();
        foreach ($rows as $row) {
            $row_phone = isset($row->pickup_phone) ? preg_replace('/\s+/', '', (string) $row->pickup_phone) : '';
            $row_name = isset($row->pickup_name) ? strtolower(trim((string) $row->pickup_name)) : '';
            if ($row_phone === $phone && $row_name === $name) {
                $candidates[] = $row;
            }
        }
    } else {
        $table = isset($reference_order->table_number) ? trim((string) $reference_order->table_number) : '';
        if ($table === '') {
            return null;
        }

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $candidates = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT * FROM $orders_table WHERE status <> 'closed' AND table_number = %s ORDER BY id ASC",
                $table
            )
        );
    }

    if (count($candidates) < 2) {
        return null;
    }

    $candidates = maca_menulist_db_attach_table_order_lines($candidates);
    $group = array();

    foreach ($candidates as $candidate) {
        if (!maca_menulist_orders_share_combined_payment_group($reference_order, $candidate)) {
            continue;
        }

        if (!maca_menulist_table_order_has_checkout_total($candidate)) {
            continue;
        }

        $group[] = $candidate;
    }

    if (count($group) < 2) {
        return null;
    }

    usort(
        $group,
        static function ($a, $b) {
            return (int) $a->id <=> (int) $b->id;
        }
    );

    $primary = $group[0];
    if (!maca_menulist_order_bill_split_is_active($primary)) {
        return null;
    }

    $reference_in_group = false;
    foreach ($group as $order) {
        if ((int) $order->id === (int) $reference_order->id) {
            $reference_in_group = true;
            break;
        }
    }

    if (!$reference_in_group) {
        return null;
    }

    $payable = array();
    foreach ($group as $index => $order) {
        if ($index === 0) {
            $payable[] = $order;
            continue;
        }

        if (maca_menulist_order_eligible_for_combined_payment($order, false)) {
            $payable[] = $order;
            continue;
        }

        $balance = maca_menulist_get_order_payment_balance($order);
        if ($balance['paid'] > 0.009 || sanitize_key((string) ($order->payment_status ?? 'none')) === 'partial') {
            $payable[] = $order;
        }
    }

    if (count($payable) < 2) {
        return null;
    }

    return $payable;
}

/**
 * Load guest orders for tokens (with lines attached).
 *
 * @param array<int, string> $guest_tokens Tokens.
 * @return array<int, object>
 */
function maca_menulist_get_guest_orders_for_tokens($guest_tokens) {
    $orders = array();

    foreach ($guest_tokens as $guest_token) {
        $order = maca_menulist_get_guest_order_for_token((string) $guest_token);
        if ($order) {
            $orders[] = $order;
        }
    }

    return $orders;
}

/**
 * Parse guest token list from AJAX request.
 *
 * @return array<int, string>
 */
function maca_menulist_parse_guest_tokens_from_request() {
    $tokens = array();

    if (isset($_POST['guest_tokens'])) {
        $raw = wp_unslash($_POST['guest_tokens']);
        if (is_string($raw)) {
            $decoded = json_decode($raw, true);
            if (is_array($decoded)) {
                $raw = $decoded;
            }
        }
        if (is_array($raw)) {
            foreach ($raw as $token) {
                $token = sanitize_text_field((string) $token);
                if ($token !== '') {
                    $tokens[] = $token;
                }
            }
        }
    }

    if (empty($tokens) && isset($_POST['guest_token'])) {
        $token = sanitize_text_field(wp_unslash($_POST['guest_token']));
        if ($token !== '') {
            $tokens[] = $token;
        }
    }

    return array_values(array_unique($tokens));
}

/**
 * Validate orders for combined payment.
 *
 * @param array<int, object> $orders Orders with lines.
 * @return array<int, object>|WP_Error
 */
function maca_menulist_validate_combined_payment_orders($orders) {
    if (count($orders) < 2) {
        return new WP_Error('count', __('Select at least two orders to pay together.', 'maca-restu-pro'));
    }

    usort(
        $orders,
        static function ($a, $b) {
            return (int) $a->id <=> (int) $b->id;
        }
    );

    $reference = $orders[0];

    foreach ($orders as $index => $order) {
        if (!maca_menulist_order_eligible_for_combined_payment($order, $index === 0)) {
            return new WP_Error('ineligible', __('One or more orders cannot be paid together.', 'maca-restu-pro'));
        }

        if (!maca_menulist_orders_share_combined_payment_group($reference, $order)) {
            return new WP_Error('group', __('Orders must belong to the same table or pickup.', 'maca-restu-pro'));
        }
    }

    return $orders;
}

/**
 * Combined subtotal across orders (before tip).
 *
 * @param array<int, object> $orders Orders with lines.
 * @return float
 */
function maca_menulist_get_combined_order_subtotal($orders) {
    $subtotal = 0.0;

    foreach ($orders as $order) {
        $subtotal += maca_menulist_get_order_subtotal($order);
    }

    return round(max(0.0, $subtotal), 2);
}

/**
 * Combined amount due across orders (including per-order tips already saved).
 *
 * @param array<int, object> $orders Orders with lines.
 * @return float
 */
function maca_menulist_get_combined_order_payment_total($orders) {
    $total = 0.0;

    foreach ($orders as $order) {
        $balance = maca_menulist_get_order_payment_balance($order);
        $total += (float) $balance['remaining'];
    }

    return round(max(0.0, $total), 2);
}

/**
 * Apply combined tip from request to the primary order in the group.
 *
 * @param array<int, object> $orders Orders with lines (primary first).
 * @return true|WP_Error
 */
function maca_menulist_apply_combined_tip_from_request($orders) {
    if (empty($orders)) {
        return true;
    }

    if (!maca_menulist_tips_enabled()) {
        return true;
    }

    if (!isset($_POST['tip_amount']) && !isset($_POST['tip_choice']) && !isset($_POST['tip_custom_amount'])) {
        return true;
    }

    $subtotal = maca_menulist_get_combined_order_subtotal($orders);
    $tip = maca_menulist_resolve_tip_from_request_for_subtotal($subtotal);
    if (is_wp_error($tip)) {
        return $tip;
    }

    foreach ($orders as $index => $order) {
        $amount = $index === 0 ? $tip : 0.0;
        $saved = maca_menulist_db_set_order_tip_amount((int) $order->id, $amount);
        if (is_wp_error($saved)) {
            return $saved;
        }
    }

    return true;
}

/**
 * Reload orders with lines after tip updates.
 *
 * @param array<int, object> $orders Orders.
 * @return array<int, object>
 */
function maca_menulist_reload_combined_payment_orders($orders) {
    $reloaded = array();

    foreach ($orders as $order) {
        $guest_token = isset($order->guest_token) ? (string) $order->guest_token : '';
        if ($guest_token === '') {
            continue;
        }

        $fresh = maca_menulist_get_guest_order_for_token($guest_token);
        if ($fresh) {
            $reloaded[] = $fresh;
        }
    }

    return maca_menulist_validate_combined_payment_orders($reloaded);
}

/**
 * Combined bill total (all orders, including tip on the primary order).
 *
 * @param array<int, object> $orders Orders with lines (primary first).
 * @return float
 */
function maca_menulist_combined_bill_split_payment_total($orders) {
    $total = 0.0;

    foreach ($orders as $order) {
        $total += maca_menulist_order_payment_total($order);
    }

    return round(max(0.0, $total), 2);
}

/**
 * Equal split share amounts for a combined bill (stored on the primary order).
 *
 * @param array<int, object> $orders Orders with lines (primary first).
 * @return array<int, float>
 */
function maca_menulist_combined_bill_split_share_amounts($orders) {
    $primary = $orders[0];

    if (maca_menulist_order_bill_split_mode($primary) !== 'equal') {
        return array();
    }

    if (!maca_menulist_order_bill_split_is_active($primary)) {
        return array();
    }

    return maca_menulist_split_amount_into_shares(
        maca_menulist_combined_bill_split_payment_total($orders),
        maca_menulist_order_bill_split_count($primary)
    );
}

/**
 * Remaining amount on a combined split bill.
 *
 * @param array<int, object> $orders Orders with lines (primary first).
 * @return float
 */
function maca_menulist_combined_bill_split_remaining($orders) {
    $primary = $orders[0];
    $combined_total = maca_menulist_combined_bill_split_payment_total($orders);
    $paid = round(max(0, (float) ($primary->payment_amount ?? 0)), 2);

    if (maca_menulist_combined_bill_split_is_complete($orders)) {
        return 0.0;
    }

    return round(max(0, $combined_total - $paid), 2);
}

/**
 * Next share amount for a combined split bill.
 *
 * @param array<int, object> $orders Orders with lines (primary first).
 * @return float
 */
function maca_menulist_combined_bill_split_next_amount($orders) {
    $primary = $orders[0];
    $remaining = maca_menulist_combined_bill_split_remaining($orders);

    if ($remaining <= 0) {
        return 0.0;
    }

    $mode = maca_menulist_order_bill_split_mode($primary);

    if ($mode === 'fixed') {
        $fixed = maca_menulist_order_bill_split_fixed_amount($primary);

        return round(min($fixed, $remaining), 2);
    }

    $shares = maca_menulist_combined_bill_split_share_amounts($orders);

    if (empty($shares)) {
        return $remaining;
    }

    $index = maca_menulist_order_bill_split_paid_shares($primary);

    if ($index >= count($shares)) {
        return $remaining;
    }

    return round(min((float) $shares[ $index ], $remaining), 2);
}

/**
 * Whether all shares of a combined split bill are paid.
 *
 * @param array<int, object> $orders Orders with lines (primary first).
 * @return bool
 */
function maca_menulist_combined_bill_split_is_complete($orders) {
    $primary = $orders[0];

    if (!maca_menulist_order_bill_split_is_active($primary)) {
        return false;
    }

    $combined_total = maca_menulist_combined_bill_split_payment_total($orders);
    $paid = round(max(0, (float) ($primary->payment_amount ?? 0)), 2);

    if ($paid + 0.009 >= $combined_total) {
        return true;
    }

    if (maca_menulist_order_bill_split_mode($primary) !== 'equal') {
        return false;
    }

    return maca_menulist_order_bill_split_paid_shares($primary) >= maca_menulist_order_bill_split_count($primary);
}

/**
 * Guest bill-split payload for combined payment UI.
 *
 * @param array<int, object> $orders Orders with lines (primary first).
 * @return array<string, mixed>
 */
function maca_menulist_build_combined_bill_split_state($orders) {
    if (empty($orders)) {
        return array('feature_enabled' => false, 'enabled' => false);
    }

    $primary = $orders[0];
    $state = maca_menulist_build_order_bill_split_state($primary);

    if (empty($state['feature_enabled'])) {
        return $state;
    }

    $combined_total = maca_menulist_combined_bill_split_payment_total($orders);
    $currency = isset($primary->payment_currency) ? (string) $primary->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['order_total'] = $combined_total;
    $state['order_total_label'] = $combined_total > 0 ? $format_amount($combined_total) : '';

    if (!empty($state['active'])) {
        $remaining = maca_menulist_combined_bill_split_remaining($orders);
        $next_amount = maca_menulist_combined_bill_split_next_amount($orders);
        $state['remaining'] = $remaining;
        $state['remaining_label'] = $remaining > 0 ? $format_amount($remaining) : '';
        $state['next_share_amount'] = $next_amount;
        $state['next_share_label'] = $next_amount > 0 ? $format_amount($next_amount) : '';
        $state['complete'] = maca_menulist_combined_bill_split_is_complete($orders);

        if ($state['mode'] === 'fixed' && $state['fixed_amount'] > 0 && $remaining > 0) {
            $state['estimated_payments'] = (int) ceil($remaining / $state['fixed_amount']);
        }
    }

    $timing_ok = true;
    foreach ($orders as $order) {
        if (!maca_menulist_order_payment_timing_allows_action($order)) {
            $timing_ok = false;
            break;
        }
    }

    $state['enabled'] = !empty($state['feature_enabled'])
        && $timing_ok
        && !maca_menulist_combined_bill_split_is_complete($orders)
        && sanitize_key((string) ($primary->payment_status ?? 'none')) !== 'pending'
        && maca_menulist_table_order_has_checkout_total($primary);

    return $state;
}

/**
 * Configure or clear bill split for a combined payment (stored on the primary order).
 *
 * @param array<int, object>   $orders Orders with lines (primary first).
 * @param array<string, mixed> $args   mode, people, fixed_amount.
 * @return true|WP_Error
 */
function maca_menulist_db_set_combined_order_bill_split($orders, $args = array()) {
    $orders = maca_menulist_validate_combined_payment_orders($orders);
    if (is_wp_error($orders)) {
        return $orders;
    }

    $primary = $orders[0];

    foreach (array_slice($orders, 1) as $order) {
        if (maca_menulist_order_bill_split_is_active($order)) {
            $cleared = maca_menulist_db_set_order_bill_split((int) $order->id, array('mode' => ''));
            if (is_wp_error($cleared)) {
                return $cleared;
            }
        }
    }

    $combined_total = maca_menulist_combined_bill_split_payment_total($orders);

    return maca_menulist_db_set_order_bill_split((int) $primary->id, $args, $combined_total);
}

/**
 * Mark all orders in a combined group as paid after the split bill is complete.
 *
 * @param array<int, object>   $orders Orders with lines (primary first).
 * @param array<string, mixed> $args   Payment metadata.
 * @return void
 */
function maca_menulist_apply_combined_group_payment_complete($orders, $args = array()) {
    foreach ($orders as $order) {
        if (maca_menulist_order_is_payment_complete($order)) {
            continue;
        }

        $order_args = $args;
        $order_args['payment_amount'] = maca_menulist_order_payment_total($order);
        maca_menulist_db_mark_table_order_paid((int) $order->id, $order_args);
    }
}
