<?php

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

// Tracking params are dropped from the URL by third party redirects. The most
// common case is WooCommerce Subscriptions: paying for a renewal order while
// logged out sends the visitor to /my-account/?wcs_redirect=pay_for_order&...
// (see WCS_Cart_Renewal::maybe_setup_cart()), and every hop after that rebuilds
// the URL from scratch. So the params are captured on the first request that
// carries them and kept in a cookie + the WC session until an order is stamped.
define('CHURNSOLUTION_TRACKING_COOKIE', 'cs_tracking_params');
define('CHURNSOLUTION_TRACKING_SESSION_KEY', 'churnsolution_tracking_params');
define('CHURNSOLUTION_TRACKING_TTL', 3600);

// WordPress defines these in wp_cookie_constants() before plugins load, but
// guard anyway so the file is safe to include from any bootstrap.
function churnsolution_tracking_cookie_path()
{
    return defined('COOKIEPATH') && COOKIEPATH ? COOKIEPATH : '/';
}

function churnsolution_tracking_cookie_domain()
{
    return defined('COOKIE_DOMAIN') && COOKIE_DOMAIN ? COOKIE_DOMAIN : '';
}

function churnsolution_tracking_is_tracking_key($key)
{
    return strpos($key, 'churnsolution_') === 0 || strpos($key, 'cs_') === 0;
}

function churnsolution_tracking_sanitize_params($params)
{
    if (!is_array($params)) {
        return array();
    }

    $sanitized = array();

    foreach ($params as $key => $value) {
        if (!is_scalar($value)) {
            continue;
        }

        $key = sanitize_key($key);

        if (!churnsolution_tracking_is_tracking_key($key)) {
            continue;
        }

        $sanitized[$key] = substr(sanitize_text_field($value), 0, 255);
    }

    return $sanitized;
}

/**
 * Tracking params carried by the current request, either on the query string or
 * on the referer of the page that linked here.
 */
function churnsolution_tracking_params_from_request()
{
    $tracking_vars = array();

    foreach ($_GET as $key => $value) {
        if (churnsolution_tracking_is_tracking_key($key)) {
            $tracking_vars[$key] = wp_unslash($value);
        }
    }

    if (!empty($_SERVER['HTTP_REFERER'])) {
        $ref_query = parse_url(wp_unslash($_SERVER['HTTP_REFERER']), PHP_URL_QUERY);
        if ($ref_query) {
            parse_str($ref_query, $ref_vars);
            foreach ($ref_vars as $key => $value) {
                if (churnsolution_tracking_is_tracking_key($key) && !isset($tracking_vars[$key])) {
                    $tracking_vars[$key] = $value;
                }
            }
        }
    }

    return churnsolution_tracking_sanitize_params($tracking_vars);
}

/**
 * Tracking params persisted from an earlier request in this visit.
 *
 * The cookie is the source that survives the logged-out -> logged-in hop: the WC
 * session is re-keyed on login, which is exactly the redirect that loses the
 * param today.
 */
function churnsolution_tracking_get_stored_params()
{
    $stored = array();

    if (!empty($_COOKIE[CHURNSOLUTION_TRACKING_COOKIE])) {
        $decoded = json_decode(wp_unslash($_COOKIE[CHURNSOLUTION_TRACKING_COOKIE]), true);
        if (is_array($decoded)) {
            $stored = $decoded;
        }
    }

    if (function_exists('WC') && WC()->session) {
        $from_session = WC()->session->get(CHURNSOLUTION_TRACKING_SESSION_KEY);
        if (is_array($from_session)) {
            $stored = array_merge($stored, $from_session);
        }
    }

    return churnsolution_tracking_sanitize_params($stored);
}

function churnsolution_tracking_store_params($params)
{
    $params = churnsolution_tracking_sanitize_params($params);

    if (empty($params)) {
        return;
    }

    $merged = array_merge(churnsolution_tracking_get_stored_params(), $params);

    if (!headers_sent()) {
        setcookie(
            CHURNSOLUTION_TRACKING_COOKIE,
            wp_json_encode($merged),
            time() + CHURNSOLUTION_TRACKING_TTL,
            churnsolution_tracking_cookie_path(),
            churnsolution_tracking_cookie_domain(),
            is_ssl(),
            true
        );
    }

    // Keep the current request consistent with what the browser was just told.
    $_COOKIE[CHURNSOLUTION_TRACKING_COOKIE] = wp_json_encode($merged);

    if (function_exists('WC') && WC()->session) {
        WC()->session->set(CHURNSOLUTION_TRACKING_SESSION_KEY, $merged);
    }
}

function churnsolution_tracking_clear_stored_params()
{
    if (!headers_sent()) {
        setcookie(
            CHURNSOLUTION_TRACKING_COOKIE,
            '',
            time() - CHURNSOLUTION_TRACKING_TTL,
            churnsolution_tracking_cookie_path(),
            churnsolution_tracking_cookie_domain(),
            is_ssl(),
            true
        );
    }

    unset($_COOKIE[CHURNSOLUTION_TRACKING_COOKIE]);

    if (function_exists('WC') && WC()->session) {
        WC()->session->set(CHURNSOLUTION_TRACKING_SESSION_KEY, null);
    }
}

/**
 * Every tracking param known for this visit. Params on the current request win
 * over anything persisted earlier.
 */
function churnsolution_tracking_collect_query_params()
{
    return array_merge(
        churnsolution_tracking_get_stored_params(),
        churnsolution_tracking_params_from_request()
    );
}

// Runs before WCS_Cart_Renewal::maybe_setup_cart() (template_redirect, priority
// 100) so the params are banked before it redirects them away.
add_action('template_redirect', 'churnsolution_tracking_capture_params', 1);
function churnsolution_tracking_capture_params()
{
    churnsolution_tracking_store_params(churnsolution_tracking_params_from_request());
}

function churnsolution_tracking_append_params_to_url($checkout_url, $params)
{
    if (empty($params) || !is_string($params)) {
        return $checkout_url;
    }

    parse_str($params, $parsed_params);

    if (empty($parsed_params) || !is_array($parsed_params)) {
        return $checkout_url;
    }

    $churnsolution_params = array_filter(
        $parsed_params,
        function ($key) {
            return strpos($key, 'cs_') === 0;
        },
        ARRAY_FILTER_USE_KEY
    );

    if (empty($churnsolution_params)) {
        return $checkout_url;
    }

    $query = http_build_query($churnsolution_params);

    return $checkout_url . (strpos($checkout_url, '?') === false ? '?' : '&') . $query;
}

add_filter('wp_redirect', 'churnsolution_update_wp_redirect', 999, 2);
function churnsolution_update_wp_redirect($location)
{
    if (strpos($location, wc_get_checkout_url()) === 0) {
        $location = churnsolution_tracking_append_params_to_url($location, $_SERVER['QUERY_STRING']);
    }

    return $location;
}

add_action('woocommerce_checkout_create_subscription', 'churnsolution_update_tracking_after_subscription_create', 10, 4);
function churnsolution_update_tracking_after_subscription_create($subscription, $posted_data, $order, $cart)
{

    $tracking_vars = churnsolution_tracking_collect_query_params();

    if (empty($tracking_vars) || !is_array($tracking_vars)) {
        return;
    }

    foreach ($tracking_vars as $key => $value) {
        $sanitized_value = sanitize_text_field($value);

        // Save to subscription
        if (is_object($subscription)) {
            $subscription->update_meta_data($key, $sanitized_value);
        }

        // Save to order
        if (is_object($order)) {
            $order->update_meta_data($key, $sanitized_value);
        }
    }
}

/**
 * Write the visit's tracking params onto an order and forget them, so the next
 * order in this browser isn't attributed to the same notification.
 */
function churnsolution_tracking_stamp_order($order_id)
{
    $order = wc_get_order($order_id);

    if (!$order) {
        return;
    }

    $tracking_vars = churnsolution_tracking_collect_query_params();

    if (empty($tracking_vars)) {
        return;
    }

    foreach ($tracking_vars as $key => $value) {
        $order->update_meta_data($key, $value);
    }

    $order->save();

    churnsolution_tracking_clear_stored_params();
}

// Covers gateways that never fire woocommerce_payment_complete (offline
// methods, orders left on-hold pending review).
add_action('woocommerce_checkout_order_processed', 'churnsolution_update_tracking_after_checkout', 10, 1);
function churnsolution_update_tracking_after_checkout($order_id)
{
    churnsolution_tracking_stamp_order($order_id);
}

add_action('woocommerce_payment_complete', 'churnsolution_update_tracking_after_order_pay', 10, 4);
function churnsolution_update_tracking_after_order_pay($order_id)
{
    churnsolution_tracking_stamp_order($order_id);
}
