<?php
/**
 * Stripe Checkout Session API for guest table orders.
 *
 * @package Maca_Menu_List
 */

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

add_filter('maca_menulist_payments_webhook_stripe', 'maca_menulist_stripe_handle_webhook', 10, 2);

/**
 * Whether checkout filter allows guest Stripe payments.
 *
 * @return bool
 */
function maca_menulist_stripe_guest_checkout_active() {
    return maca_menulist_payments_allow_online() && maca_menulist_payments_online_ready();
}

/**
 * Stripe API request.
 *
 * @param string               $method HTTP method.
 * @param string               $path   API path without leading slash.
 * @param array<string, mixed> $body   Body fields.
 * @return array<string, mixed>|WP_Error
 */
function maca_menulist_stripe_api_request($method, $path, $body = array()) {
    $secret = maca_menulist_payments_get_setting('stripe', 'secret_key');
    if ($secret === '') {
        return new WP_Error('stripe_config', __('Stripe is not configured.', 'maca-restu-pro'));
    }

    $url = 'https://api.stripe.com/v1/' . ltrim($path, '/');
    $headers = array(
        'Authorization' => 'Bearer ' . $secret,
    );

    $account = maca_menulist_payments_get_setting('stripe', 'connected_account_id');
    if ($account !== '') {
        $headers['Stripe-Account'] = $account;
    }

    $args = array(
        'method' => strtoupper($method),
        'headers' => $headers,
        'timeout' => 30,
    );

    if (!empty($body)) {
        $args['body'] = $body;
    }

    $response = wp_remote_request($url, $args);
    if (is_wp_error($response)) {
        return $response;
    }

    $code = (int) wp_remote_retrieve_response_code($response);
    $raw = wp_remote_retrieve_body($response);
    $data = json_decode($raw, true);

    if ($code < 200 || $code >= 300) {
        $message = __('Stripe request failed.', 'maca-restu-pro');
        if (is_array($data) && !empty($data['error']['message'])) {
            $message = sanitize_text_field($data['error']['message']);
        }

        return new WP_Error('stripe_api', $message, array('status' => $code));
    }

    return is_array($data) ? $data : array();
}

/**
 * Create Checkout Session for a table order.
 *
 * @param object $order Order with lines and guest_token.
 * @param string $return_url Base page URL for redirects.
 * @param array<string, mixed> $opts Optional checkout options (confirmation email).
 * @return array{url: string, session_id: string}|WP_Error
 */
function maca_menulist_stripe_create_table_order_checkout($order, $return_url = '', $opts = array()) {
    if (!maca_menulist_stripe_guest_checkout_active()) {
        return new WP_Error('disabled', __('Online payment is not available.', 'maca-restu-pro'));
    }

    if (!maca_menulist_table_order_has_checkout_total($order)) {
        return new WP_Error('no_total', __('This order has no prices for online payment.', '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'));
    }

    if ((string) $order->payment_method === 'manual') {
        return new WP_Error('manual', __('This order is set to pay at the table.', 'maca-restu-pro'));
    }

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

    if (maca_menulist_order_bill_split_is_active($order) && maca_menulist_order_bill_split_next_amount($order) <= 0) {
        return new WP_Error('paid', __('This order is already paid.', 'maca-restu-pro'));
    }

    $guest_token = isset($order->guest_token) ? (string) $order->guest_token : '';
    if ($guest_token === '') {
        return new WP_Error('token', __('Invalid order.', 'maca-restu-pro'));
    }

    if ($return_url === '') {
        $return_url = maca_menulist_get_current_page_url_for_payments();
    }

    $success_url = add_query_arg(
        array(
            'maca_payment' => 'success',
            'maca_order' => $guest_token,
        ),
        $return_url
    );
    $cancel_url = add_query_arg(
        array(
            'maca_payment' => 'cancel',
            'maca_order' => $guest_token,
        ),
        $return_url
    );

    $currency = strtolower(maca_menulist_get_price_currency());
    if ($currency === '') {
        $currency = 'sek';
    }

    $body = array(
        'mode' => 'payment',
        'success_url' => $success_url,
        'cancel_url' => $cancel_url,
        'client_reference_id' => (string) (int) $order->id,
        'metadata[order_id]' => (string) (int) $order->id,
        'metadata[guest_token]' => $guest_token,
    );

    if (!empty($opts['send_confirmation']) && !empty($opts['confirmation_email'])) {
        $confirmation_email = sanitize_email((string) $opts['confirmation_email']);
        if (is_email($confirmation_email)) {
            $body['customer_email'] = $confirmation_email;
            $body['metadata[send_confirmation]'] = '1';
        }
    }

    $index = 0;
    $checkout_total = maca_menulist_order_payment_total($order);
    $balance = maca_menulist_get_order_payment_balance($order);
    $remaining = round((float) $balance['remaining'], 2);
    $paid_so_far = round((float) $balance['paid'], 2);

    if (
        !maca_menulist_order_bill_split_is_active($order)
        && $paid_so_far > 0.009
        && $remaining > 0.009
    ) {
        $body['line_items[0][quantity]'] = 1;
        $body['line_items[0][price_data][currency]'] = $currency;
        $body['line_items[0][price_data][unit_amount]'] = (int) round($remaining * 100);
        $body['line_items[0][price_data][product_data][name]'] = __('Additional order', 'maca-restu-pro');
        $body['metadata[supplemental_payment]'] = '1';
        $checkout_total = $remaining;
        $index = 1;
    } elseif (maca_menulist_order_bill_split_is_active($order)) {
        $checkout_total = maca_menulist_order_bill_split_next_amount($order);
        $share_number = maca_menulist_order_bill_split_paid_shares($order) + 1;
        $split_mode = maca_menulist_order_bill_split_mode($order);
        $body['metadata[bill_split_share]'] = (string) $share_number;
        $body['metadata[bill_split_mode]'] = $split_mode;
        $body['line_items[0][quantity]'] = 1;
        $body['line_items[0][price_data][currency]'] = $currency;
        $body['line_items[0][price_data][unit_amount]'] = (int) round($checkout_total * 100);

        if ($split_mode === 'fixed') {
            $body['line_items[0][price_data][product_data][name]'] = sprintf(
                /* translators: %d: payment number */
                __('Bill payment %d', 'maca-restu-pro'),
                $share_number
            );
        } else {
            $share_total = maca_menulist_order_bill_split_count($order);
            $body['line_items[0][price_data][product_data][name]'] = sprintf(
                /* translators: 1: share number, 2: total shares */
                __('Bill share %1$d of %2$d', 'maca-restu-pro'),
                $share_number,
                $share_total
            );
        }
        $index = 1;
    } elseif ($index === 0) {
        foreach ($order->lines as $line) {
            if ($line->price === null || $line->price === '') {
                continue;
            }

            $amount_ore = (int) round((float) $line->price * 100);
            if ($amount_ore <= 0) {
                continue;
            }

            $prefix = 'line_items[' . $index . ']';
            $body[$prefix . '[quantity]'] = max(1, (int) $line->qty);
            $body[$prefix . '[price_data][currency]'] = $currency;
            $body[$prefix . '[price_data][unit_amount]'] = $amount_ore;
            $body[$prefix . '[price_data][product_data][name]'] = mb_substr((string) $line->title, 0, 120);
            $index++;
        }

        if ($index === 0) {
            return new WP_Error('no_lines', __('No priced items for checkout.', 'maca-restu-pro'));
        }

        $tip_amount = maca_menulist_get_order_tip_amount($order);
        if ($tip_amount > 0) {
            $tip_ore = (int) round($tip_amount * 100);
            if ($tip_ore > 0) {
                $prefix = 'line_items[' . $index . ']';
                $body[$prefix . '[quantity]'] = 1;
                $body[$prefix . '[price_data][currency]'] = $currency;
                $body[$prefix . '[price_data][unit_amount]'] = $tip_ore;
                $body[$prefix . '[price_data][product_data][name]'] = __('Tip', 'maca-restu-pro');
                $index++;
            }
        }

        $checkout_total = maca_menulist_order_payment_total($order);
    }

    if ($checkout_total <= 0) {
        return new WP_Error('no_lines', __('No priced items for checkout.', 'maca-restu-pro'));
    }

    $session = maca_menulist_stripe_api_request('POST', 'checkout/sessions', $body);
    if (is_wp_error($session)) {
        return $session;
    }

    if (empty($session['id']) || empty($session['url'])) {
        return new WP_Error('stripe_session', __('Could not start checkout.', 'maca-restu-pro'));
    }

    maca_menulist_db_set_order_payment_pending(
        (int) $order->id,
        'stripe',
        (string) $session['id'],
        $checkout_total
    );

    return array(
        'url' => esc_url_raw($session['url']),
        'session_id' => (string) $session['id'],
    );
}

/**
 * Create Stripe Checkout Session for multiple table orders.
 *
 * @param array<int, object>   $orders     Orders with lines (sorted, primary first).
 * @param string               $return_url Base page URL for redirects.
 * @param array<string, mixed> $opts       Optional checkout options (confirmation email).
 * @return array{url: string, session_id: string}|WP_Error
 */
function maca_menulist_stripe_create_combined_table_order_checkout($orders, $return_url = '', $opts = array()) {
    if (!maca_menulist_stripe_guest_checkout_active()) {
        return new WP_Error('disabled', __('Online payment is not available.', 'maca-restu-pro'));
    }

    $orders = maca_menulist_validate_combined_payment_orders($orders);
    if (is_wp_error($orders)) {
        return $orders;
    }

    $primary = $orders[0];
    $guest_token = isset($primary->guest_token) ? (string) $primary->guest_token : '';
    if ($guest_token === '') {
        return new WP_Error('token', __('Invalid order.', 'maca-restu-pro'));
    }

    if ($return_url === '') {
        $return_url = maca_menulist_get_current_page_url_for_payments();
    }

    $success_url = add_query_arg(
        array(
            'maca_payment' => 'success',
            'maca_order' => $guest_token,
        ),
        $return_url
    );
    $cancel_url = add_query_arg(
        array(
            'maca_payment' => 'cancel',
            'maca_order' => $guest_token,
        ),
        $return_url
    );

    $currency = strtolower(maca_menulist_get_price_currency());
    if ($currency === '') {
        $currency = 'sek';
    }

    $order_ids = array_map(
        static function ($order) {
            return (string) (int) $order->id;
        },
        $orders
    );

    $body = array(
        'mode' => 'payment',
        'success_url' => $success_url,
        'cancel_url' => $cancel_url,
        'client_reference_id' => (string) (int) $primary->id,
        'metadata[order_id]' => (string) (int) $primary->id,
        'metadata[guest_token]' => $guest_token,
        'metadata[combined_payment]' => '1',
        'metadata[order_ids]' => implode(',', $order_ids),
    );

    if (!empty($opts['send_confirmation']) && !empty($opts['confirmation_email'])) {
        $confirmation_email = sanitize_email((string) $opts['confirmation_email']);
        if (is_email($confirmation_email)) {
            $body['customer_email'] = $confirmation_email;
            $body['metadata[send_confirmation]'] = '1';
        }
    }

    $index = 0;
    $checkout_total = 0.0;

    if (maca_menulist_order_bill_split_is_active($primary)) {
        $checkout_total = maca_menulist_combined_bill_split_next_amount($orders);
        if ($checkout_total <= 0) {
            return new WP_Error('paid', __('This order is already paid.', 'maca-restu-pro'));
        }

        $share_number = maca_menulist_order_bill_split_paid_shares($primary) + 1;
        $split_mode = maca_menulist_order_bill_split_mode($primary);
        $body['metadata[bill_split_share]'] = (string) $share_number;
        $body['metadata[bill_split_mode]'] = $split_mode;
        $body['line_items[0][quantity]'] = 1;
        $body['line_items[0][price_data][currency]'] = $currency;
        $body['line_items[0][price_data][unit_amount]'] = (int) round($checkout_total * 100);

        if ($split_mode === 'fixed') {
            $body['line_items[0][price_data][product_data][name]'] = sprintf(
                /* translators: %d: payment number */
                __('Bill payment %d', 'maca-restu-pro'),
                $share_number
            );
        } else {
            $share_total = maca_menulist_order_bill_split_count($primary);
            $body['line_items[0][price_data][product_data][name]'] = sprintf(
                /* translators: 1: share number, 2: total shares */
                __('Bill share %1$d of %2$d', 'maca-restu-pro'),
                $share_number,
                $share_total
            );
        }
        $index = 1;
    } else {
        foreach ($orders as $order_index => $order) {
            $prefix_label = '';
            if (count($orders) > 1 && $order_index > 0) {
                $prefix_label = sprintf(
                    /* translators: %d: add-on order number */
                    __('Add-on order #%d', 'maca-restu-pro'),
                    $order_index + 1
                ) . ': ';
            }

            foreach ($order->lines as $line) {
                if ($line->price === null || $line->price === '') {
                    continue;
                }

                $amount_ore = (int) round((float) $line->price * 100);
                if ($amount_ore <= 0) {
                    continue;
                }

                $prefix = 'line_items[' . $index . ']';
                $body[$prefix . '[quantity]'] = max(1, (int) $line->qty);
                $body[$prefix . '[price_data][currency]'] = $currency;
                $body[$prefix . '[price_data][unit_amount]'] = $amount_ore;
                $body[$prefix . '[price_data][product_data][name]'] = mb_substr($prefix_label . (string) $line->title, 0, 120);
                $index++;
            }
        }

        if ($index === 0) {
            return new WP_Error('no_lines', __('No priced items for checkout.', 'maca-restu-pro'));
        }

        $tip_amount = maca_menulist_get_order_tip_amount($primary);
        if ($tip_amount > 0) {
            $tip_ore = (int) round($tip_amount * 100);
            if ($tip_ore > 0) {
                $prefix = 'line_items[' . $index . ']';
                $body[$prefix . '[quantity]'] = 1;
                $body[$prefix . '[price_data][currency]'] = $currency;
                $body[$prefix . '[price_data][unit_amount]'] = $tip_ore;
                $body[$prefix . '[price_data][product_data][name]'] = __('Tip', 'maca-restu-pro');
                $index++;
            }
        }

        $checkout_total = maca_menulist_get_combined_order_payment_total($orders);
    }

    if ($checkout_total <= 0) {
        return new WP_Error('no_lines', __('No priced items for checkout.', 'maca-restu-pro'));
    }

    $session = maca_menulist_stripe_api_request('POST', 'checkout/sessions', $body);
    if (is_wp_error($session)) {
        return $session;
    }

    if (empty($session['id']) || empty($session['url'])) {
        return new WP_Error('stripe_session', __('Could not start checkout.', 'maca-restu-pro'));
    }

    foreach ($orders as $order) {
        $pending_amount = maca_menulist_order_bill_split_is_active($primary)
            ? maca_menulist_combined_bill_split_next_amount($orders)
            : maca_menulist_order_payment_total($order);

        maca_menulist_db_set_order_payment_pending(
            (int) $order->id,
            'stripe',
            (string) $session['id'],
            $pending_amount
        );
    }

    return array(
        'url' => esc_url_raw($session['url']),
        'session_id' => (string) $session['id'],
    );
}

/**
 * Verify Stripe webhook signature.
 *
 * @param string $payload    Raw body.
 * @param string $sig_header Stripe-Signature header.
 * @param string $secret     whsec_ secret.
 * @return bool
 */
function maca_menulist_stripe_verify_webhook_signature($payload, $sig_header, $secret) {
    if ($secret === '' || $sig_header === '') {
        return false;
    }

    $timestamp = null;
    $signatures = array();

    foreach (explode(',', $sig_header) as $element) {
        $parts = explode('=', trim($element), 2);
        if (count($parts) !== 2) {
            continue;
        }
        if ($parts[0] === 't') {
            $timestamp = $parts[1];
        }
        if ($parts[0] === 'v1') {
            $signatures[] = $parts[1];
        }
    }

    if ($timestamp === null || empty($signatures)) {
        return false;
    }

    $timestamp_int = (int) $timestamp;
    if ($timestamp_int <= 0) {
        return false;
    }

    // Reject old/replayed signatures outside Stripe's normal tolerance window.
    if (abs(time() - $timestamp_int) > (5 * MINUTE_IN_SECONDS)) {
        return false;
    }

    $signed = $timestamp . '.' . $payload;
    $expected = hash_hmac('sha256', $signed, $secret);

    foreach ($signatures as $signature) {
        if (hash_equals($expected, $signature)) {
            return true;
        }
    }

    return false;
}

/**
 * Handle Stripe webhook events for guest orders.
 *
 * @param mixed           $result  Prior result.
 * @param WP_REST_Request $request Request.
 * @return true|WP_Error|null
 */
function maca_menulist_stripe_handle_webhook($result, $request) {
    unset($result);

    $secret = maca_menulist_payments_get_setting('stripe', 'webhook_secret');
    if ($secret === '') {
        return new WP_Error('stripe_wh', __('Webhook secret not configured.', 'maca-restu-pro'), array('status' => 400));
    }

    $payload = $request->get_body();
    $sig = $request->get_header('stripe-signature');
    if ($sig === '') {
        $sig = isset($_SERVER['HTTP_STRIPE_SIGNATURE']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_STRIPE_SIGNATURE'])) : '';
    }

    if (!maca_menulist_stripe_verify_webhook_signature($payload, $sig, $secret)) {
        return new WP_Error('stripe_sig', __('Invalid webhook signature.', 'maca-restu-pro'), array('status' => 400));
    }

    $event = json_decode($payload, true);
    if (!is_array($event) || empty($event['type'])) {
        return new WP_Error('stripe_event', __('Invalid event payload.', 'maca-restu-pro'), array('status' => 400));
    }

    $event_id = isset($event['id']) ? sanitize_text_field((string) $event['id']) : '';
    if ($event_id !== '') {
        $dedupe_key = 'maca_stripe_event_' . md5($event_id);
        if (get_transient($dedupe_key)) {
            return true;
        }
        set_transient($dedupe_key, 1, DAY_IN_SECONDS);
    }

    $type = (string) $event['type'];
    $handled = array(
        'checkout.session.completed',
        'checkout.session.async_payment_succeeded',
    );

    if (!in_array($type, $handled, true)) {
        return true;
    }

    $object = isset($event['data']['object']) && is_array($event['data']['object'])
        ? $event['data']['object']
        : array();

    $order_id = isset($object['metadata']['order_id']) ? absint($object['metadata']['order_id']) : 0;
    if ($order_id <= 0 && !empty($object['client_reference_id'])) {
        $order_id = absint($object['client_reference_id']);
    }

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

    $payment_status = isset($object['payment_status']) ? (string) $object['payment_status'] : 'paid';
    if ($payment_status !== 'paid' && $payment_status !== 'no_payment_required') {
        return true;
    }

    $amount = isset($object['amount_total']) ? ((int) $object['amount_total']) / 100 : 0;
    $currency = isset($object['currency']) ? strtoupper((string) $object['currency']) : 'SEK';
    $session_id = isset($object['id']) ? (string) $object['id'] : '';

    $payment_args = array(
        'payment_provider' => 'stripe',
        'payment_method' => 'online',
        'payment_external_id' => $session_id,
        'payment_amount' => $amount,
        'payment_currency' => $currency,
        'transaction_amount' => $amount,
    );

    $metadata = isset($object['metadata']) && is_array($object['metadata']) ? $object['metadata'] : array();
    if (!empty($metadata['combined_payment']) && !empty($metadata['order_ids'])) {
        $combined_ids = array_filter(array_map('absint', explode(',', (string) $metadata['order_ids'])));
        if (count($combined_ids) >= 2) {
            $combined_orders = array();
            foreach ($combined_ids as $combined_order_id) {
                $combined_order = maca_menulist_db_get_table_order($combined_order_id);
                if (!$combined_order) {
                    continue;
                }
                $combined_orders[] = maca_menulist_db_attach_table_order_lines(array($combined_order))[0];
            }

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

            if (!empty($metadata['bill_split_share']) && !empty($combined_orders)) {
                $primary_order = $combined_orders[0];
                $order_payment_args = $payment_args;
                $order_payment_args['bill_split_share'] = true;
                $order_payment_args['payment_kind'] = 'bill_split';
                $order_payment_args['payment_amount'] = $amount;
                maca_menulist_db_apply_order_payment((int) $primary_order->id, $order_payment_args);

                $primary_order = maca_menulist_db_get_table_order((int) $primary_order->id);
                if ($primary_order) {
                    $primary_order = maca_menulist_db_attach_table_order_lines(array($primary_order))[0];
                    $combined_orders[0] = $primary_order;
                }

                if (maca_menulist_combined_bill_split_is_complete($combined_orders)) {
                    maca_menulist_apply_combined_group_payment_complete($combined_orders, $payment_args);
                    maca_menulist_maybe_send_combined_confirmation_email($object, $combined_ids);
                }

                return true;
            }

            foreach ($combined_ids as $combined_order_id) {
                if ($combined_order_id <= 0) {
                    continue;
                }

                $combined_order = maca_menulist_db_get_table_order($combined_order_id);
                if (!$combined_order) {
                    continue;
                }

                $combined_order = maca_menulist_db_attach_table_order_lines(array($combined_order))[0];
                $order_payment_args = $payment_args;
                $order_payment_args['payment_amount'] = maca_menulist_order_payment_total($combined_order);
                maca_menulist_db_apply_order_payment($combined_order_id, $order_payment_args);
            }

            maca_menulist_maybe_send_combined_confirmation_email($object, $combined_ids);

            return true;
        }
    }

    if (!empty($metadata['supplemental_payment'])) {
        $payment_args['supplemental_payment'] = true;
        $payment_args['payment_kind'] = 'supplemental';
    }
    if (!empty($metadata['bill_split_share'])) {
        $payment_args['bill_split_share'] = true;
        $payment_args['payment_kind'] = 'bill_split';
    }

    maca_menulist_db_apply_order_payment($order_id, $payment_args);

    $confirmation = maca_menulist_confirmation_email_from_stripe_session($object);
    if ($confirmation['send'] && $confirmation['email'] !== '') {
        $order = maca_menulist_db_get_table_order($order_id);
        if ($order) {
            $order = maca_menulist_db_attach_table_order_lines(array($order))[0];
            maca_menulist_send_order_confirmation_email($order, $confirmation['email'], $object);
        }
    }

    return true;
}
