<?php

/**
 * Referral Tracking Functions
 */
if ( !defined( 'ABSPATH' ) ) {
    exit;
}
/**
 * Track referral when page loads
 */
add_action( 'init', 'spar_track_referral', 5 );
// Priority 5 to run early
function spar_track_referral() {
    // Only run if referral feature is enabled
    $options = get_option( 'spar_options', [] );
    if ( empty( $options['earn']['referral']['enabled'] ) ) {
        return;
    }
    // Read-only referral parameter
    $referral_code = filter_input( INPUT_GET, 'ref', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
    if ( !empty( $referral_code ) ) {
        // Validate that the referral code exists
        $referrer_id = spar_get_user_by_referral_code( $referral_code );
        if ( !$referrer_id ) {
            return;
            // Invalid referral code
        }
        // Track the click FIRST before any blocking logic
        spar_increment_referral_clicks( $referrer_id );
        // Insert detailed click log row (always log the visit)
        $click_id = spar_log_referral_click( $referral_code, $referrer_id );
        // Get attribution model setting and self-referral blocking setting
        $options = get_option( 'spar_options', [] );
        $attribution_model = $options['earn']['referral']['gift_attribution_model'] ?? 'first_click';
        $block_self_referral = !empty( $options['earn']['referral']['block_self_referral'] );
        if ( !isset( $options['earn']['referral']['block_self_referral'] ) ) {
            $block_self_referral = true;
            // Default to blocking self-referrals for security
        }
        // Don't track if user is referring themselves and self-referral blocking is enabled
        $current_user_id = get_current_user_id();
        // CRITICAL: Block self-referrals BEFORE setting any cookies or creating coupons
        if ( $block_self_referral && $current_user_id && $current_user_id == $referrer_id ) {
            return;
            // Silently block self-referrals
        }
        // Check if we should honor existing referral (first-click attribution)
        if ( $attribution_model === 'first_click' && isset( $_COOKIE['spar_referrer'] ) ) {
            return;
        }
        // Set or update cookie (30 days)
        setcookie(
            'spar_referrer',
            $referral_code,
            time() + 30 * 24 * 60 * 60,
            '/'
        );
        if ( $click_id ) {
            setcookie(
                'spar_ref_click_id',
                (string) absint( $click_id ),
                time() + 30 * 24 * 60 * 60,
                '/'
            );
        }
    }
}

/**
 * Insert a detailed referral click log row
 */
function spar_log_referral_click(  $referral_code, $referrer_id  ) {
    global $wpdb;
    // Ensure table exists (best-effort)
    $table = $wpdb->prefix . 'spar_referral_clicks';
    // Ensure table exists (cached check).
    if ( !spar_table_exists( $table ) ) {
        if ( function_exists( 'spar_create_referral_clicks_table' ) ) {
            spar_create_referral_clicks_table();
        }
    }
    $landing_url = '';
    $referral_link = '';
    $referring_domain = null;
    if ( isset( $_SERVER['HTTP_HOST'], $_SERVER['REQUEST_URI'] ) ) {
        // Best-effort current URL
        $scheme = ( is_ssl() ? 'https://' : 'http://' );
        $referral_link = $scheme . wp_unslash( $_SERVER['HTTP_HOST'] ) . wp_unslash( $_SERVER['REQUEST_URI'] );
        $landing_url = $referral_link;
        $referral_link = esc_url_raw( $referral_link );
        $landing_url = esc_url_raw( $landing_url );
    }
    // Extract referring domain from HTTP referrer header if present
    $raw_ref = wp_get_raw_referer();
    if ( $raw_ref ) {
        $parts = wp_parse_url( $raw_ref );
        if ( is_array( $parts ) && !empty( $parts['host'] ) ) {
            $host = strtolower( (string) $parts['host'] );
            // Normalize: strip leading www.
            $host = preg_replace( '/^www\\./i', '', $host );
            $referring_domain = $host;
        }
    }
    $current_user_id = get_current_user_id();
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
    $inserted = $wpdb->insert( $table, [
        'created_at'       => current_time( 'mysql' ),
        'referral_code'    => sanitize_text_field( $referral_code ),
        'referrer_user_id' => (int) $referrer_id,
        'referree_user_id' => ( $current_user_id ? (int) $current_user_id : null ),
        'landing_url'      => esc_url_raw( $landing_url ),
        'referral_link'    => esc_url_raw( $referral_link ),
        'referring_domain' => ( $referring_domain ? sanitize_text_field( $referring_domain ) : null ),
        'converted'        => 0,
    ], [
        '%s',
        '%s',
        '%d',
        '%d',
        '%s',
        '%s',
        '%s',
        '%d'
    ] );
    if ( false === $inserted ) {
        return 0;
    }
    return (int) $wpdb->insert_id;
}

/**
 * Store referrer information on thank you page
 */
add_action( 'woocommerce_thankyou', 'spar_store_referrer_in_order', 10 );
function spar_store_referrer_in_order(  $order_id  ) {
    $options = get_option( 'spar_options', [] );
    if ( empty( $options['earn']['referral']['enabled'] ) ) {
        return;
    }
    $referrer_code = null;
    if ( isset( $_COOKIE['spar_referrer'] ) ) {
        $referrer_code = sanitize_text_field( wp_unslash( $_COOKIE['spar_referrer'] ) );
        setcookie(
            'spar_referrer',
            '',
            time() - 3600,
            '/'
        );
        // Clear click id cookie after thank you page capture
        if ( isset( $_COOKIE['spar_ref_click_id'] ) ) {
            setcookie(
                'spar_ref_click_id',
                '',
                time() - 3600,
                '/'
            );
        }
    } else {
        // If no cookie exists, check for referral coupon usage when always_track is enabled
        $tracking_mode = $options['earn']['referral']['coupon_tracking_mode'] ?? 'cookie_required';
        if ( $tracking_mode === 'always_track' ) {
            $order = wc_get_order( $order_id );
            if ( $order ) {
                $referrer_id = spar_check_referral_coupon_usage( $order );
                if ( $referrer_id ) {
                    $referrer_code = spar_get_user_referral_code( $referrer_id );
                }
            }
        }
    }
    setcookie(
        'spar_referral_coupon',
        '',
        time() - 3600,
        '/'
    );
    if ( $referrer_code ) {
        $referrer_id = spar_get_user_by_referral_code( $referrer_code );
        $order = wc_get_order( $order_id );
        if ( $order ) {
            $order->update_meta_data( 'referrer_code', $referrer_code );
            if ( $referrer_id ) {
                $customer_id = (int) $order->get_user_id();
                if ( $customer_id && $customer_id !== (int) $referrer_id ) {
                    update_user_meta( $customer_id, 'spar_referrer_user_id', (int) $referrer_id );
                }
            }
            $order->save();
            // Mark conversion against the click log if possible
            spar_update_referral_click_conversion( $order, $referrer_code );
        }
    }
}

/**
 * Update referral click log to mark conversion
 */
function spar_update_referral_click_conversion(  $order, $referrer_code  ) {
    if ( !$order ) {
        return;
    }
    global $wpdb;
    $table = $wpdb->prefix . 'spar_referral_clicks';
    $order_id = $order->get_id();
    $customer_id = (int) $order->get_user_id();
    // Determine gift coupon used, if any
    $gift_coupon_code = null;
    $coupon_codes = $order->get_coupon_codes();
    foreach ( $coupon_codes as $coupon_code ) {
        if ( preg_match( '/^gift-([A-Z0-9]+)$/i', $coupon_code ) ) {
            $gift_coupon_code = $coupon_code;
            break;
        }
    }
    $now = current_time( 'mysql' );
    // Prefer matching by click ID cookie
    $click_id_cookie = ( isset( $_COOKIE['spar_ref_click_id'] ) ? absint( $_COOKIE['spar_ref_click_id'] ) : 0 );
    if ( $click_id_cookie ) {
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
        $wpdb->update(
            $table,
            [
                'converted'        => 1,
                'order_id'         => $order_id,
                'gift_coupon_code' => ( $gift_coupon_code ? sanitize_text_field( $gift_coupon_code ) : null ),
                'conversion_at'    => $now,
                'referree_user_id' => ( $customer_id ?: null ),
            ],
            [
                'id' => $click_id_cookie,
            ],
            [
                '%d',
                '%d',
                '%s',
                '%s',
                '%d'
            ],
            ['%d']
        );
        return;
    }
    // Fallback: match most recent unconverted click for this referral code
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.PreparedSQL.NotPrepared
    $row = $wpdb->get_row( $wpdb->prepare( 'SELECT id FROM %i WHERE referral_code = %s AND converted = 0 ORDER BY created_at DESC LIMIT 1', $table, $referrer_code ), ARRAY_A );
    if ( $row && isset( $row['id'] ) ) {
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery
        $wpdb->update(
            $table,
            [
                'converted'        => 1,
                'order_id'         => $order_id,
                'gift_coupon_code' => ( $gift_coupon_code ? sanitize_text_field( $gift_coupon_code ) : null ),
                'conversion_at'    => $now,
                'referree_user_id' => ( $customer_id ?: null ),
            ],
            [
                'id' => (int) $row['id'],
            ],
            [
                '%d',
                '%d',
                '%s',
                '%s',
                '%d'
            ],
            ['%d']
        );
    }
}

/**
 * Award referral bonus when order is completed
 */
add_action( 'woocommerce_order_status_completed', 'spar_award_referral_bonus', 20 );
add_action( 'woocommerce_order_status_processing', 'spar_award_referral_bonus', 20 );
function spar_award_referral_bonus(  $order_id  ) {
    $options = get_option( 'spar_options', [] );
    if ( empty( $options['earn']['referral']['enabled'] ) ) {
        return;
    }
    // Respect configured award timing (completed or processing)
    $award_timing = $options['earn']['referral']['award_timing'] ?? 'completed';
    $current_hook = current_action();
    if ( $award_timing === 'completed' && $current_hook !== 'woocommerce_order_status_completed' || $award_timing === 'processing' && $current_hook !== 'woocommerce_order_status_processing' ) {
        return;
    }
    $order = wc_get_order( $order_id );
    if ( !$order ) {
        return;
    }
    // Check if referral bonus has already been awarded
    $referral_bonus_awarded = $order->get_meta( 'referral_points_awarded' );
    if ( $referral_bonus_awarded ) {
        return;
    }
    $referrer_code = $order->get_meta( 'referrer_code' );
    $referrer_id = null;
    // If no referrer code from cookie, check for referral coupon usage
    if ( !$referrer_code ) {
        $referrer_id = spar_check_referral_coupon_usage( $order );
        if ( $referrer_id ) {
            $referrer_code = spar_get_user_referral_code( $referrer_id );
            // Store referrer code for this order
            $order->update_meta_data( 'referrer_code', $referrer_code );
            $order->update_meta_data( 'referral_source', 'coupon' );
            $order->save();
        }
    } else {
        // Get referrer user ID from existing referrer code
        $referrer_id = spar_get_user_by_referral_code( $referrer_code );
    }
    if ( !$referrer_id ) {
        return;
    }
    // Get self-referral blocking setting
    $block_self_referral = !empty( $options['earn']['referral']['block_self_referral'] );
    // Don't award points if customer is referring themselves and blocking is enabled
    $customer_id = $order->get_user_id();
    if ( $block_self_referral && $customer_id && $customer_id === $referrer_id ) {
        return;
    }
    // Calculate referral bonus
    $earning_type = $options['earn']['referral']['earning_type'] ?? 'fixed';
    if ( $earning_type === 'fixed' ) {
        // Fixed amount per referral
        $referral_points = (int) ($options['earn']['referral']['fixed_points'] ?? 100);
    } else {
        // Percentage of order total
        $percentage = (float) ($options['earn']['referral']['percentage'] ?? 10);
        $points_per_pound = (float) ($options['earn']['referral']['points_per'] ?? 5);
        $order_total = $order->get_total();
        $referral_value = $order_total * $percentage / 100;
        $referral_points = floor( $referral_value * $points_per_pound );
    }
    if ( $referral_points > 0 ) {
        $action_name = $options['earn']['referral']['name'] ?? esc_html__( 'Referral Bonus', 'simple-points-and-rewards' );
        if ( $earning_type === 'fixed' ) {
            $action_description = sprintf( '%s: Fixed amount for referral (Order #%d)', $action_name, $order->get_id() );
        } else {
            // Display the order total with currency (prefer WooCommerce formatter when available)
            $order_currency = ( method_exists( $order, 'get_currency' ) ? $order->get_currency() : '' );
            if ( function_exists( 'wc_price' ) ) {
                $display_total = wc_price( $order_total, array(
                    'currency' => $order_currency,
                ) );
            } else {
                $display_total = (( $order_currency ? $order_currency . ' ' : '' )) . number_format( (float) $order_total, 2 );
            }
            $action_description = sprintf(
                '%s: %s%% of %s (Order #%d)',
                $action_name,
                $percentage,
                $display_total,
                $order->get_id()
            );
        }
        spar_update_user_points(
            $referrer_id,
            $referral_points,
            'add',
            $action_description,
            'referral'
        );
        // Mark that referral bonus has been awarded
        $order->update_meta_data( 'referral_points_awarded', $referral_points );
        $order->save();
        // Update referrer stats
        spar_update_user_referral_stats( $referrer_id, $referral_points );
    }
}

/**
 * Deduct previously awarded referral points if the referred order is refunded/cancelled/failed (configurable).
 */
add_action( 'woocommerce_order_status_refunded', 'spar_deduct_refunded_referral_points' );
add_action( 'woocommerce_order_status_cancelled', 'spar_deduct_refunded_referral_points' );
add_action( 'woocommerce_order_status_failed', 'spar_deduct_refunded_referral_points' );
// Also handle refund events where the order status does not change (partial/full refunds)
add_action(
    'woocommerce_order_refunded',
    'spar_maybe_deduct_referral_points_on_refund_event',
    10,
    1
);
add_action(
    'woocommerce_order_fully_refunded',
    'spar_maybe_deduct_referral_points_on_refund_event',
    10,
    1
);
function spar_deduct_refunded_referral_points(  $order_id  ) {
    $options = get_option( 'spar_options', [] );
    // Only run if referral feature is enabled
    if ( empty( $options['earn']['referral']['enabled'] ) ) {
        return;
    }
    // Default to true when unset so deductions still occur even if settings haven't been saved yet
    $deduct_on_refund = $options['earn']['referral']['deduct_on_refund'] ?? true;
    if ( !$deduct_on_refund ) {
        return;
    }
    $order = wc_get_order( $order_id );
    if ( !$order ) {
        return;
    }
    // Only proceed if referral points were actually awarded for this order
    $awarded = (int) $order->get_meta( 'referral_points_awarded' );
    if ( $awarded <= 0 ) {
        return;
    }
    // Prevent double-deduction
    $already_deducted = (int) $order->get_meta( 'referral_points_deducted' );
    if ( $already_deducted > 0 ) {
        return;
    }
    // Determine the referrer user ID
    $referrer_id = null;
    $referrer_code = $order->get_meta( 'referrer_code' );
    if ( $referrer_code ) {
        $referrer_id = spar_get_user_by_referral_code( $referrer_code );
    }
    if ( !$referrer_id ) {
        $referrer_id = spar_check_referral_coupon_usage( $order );
    }
    if ( !$referrer_id ) {
        return;
    }
    // Build action text
    $points_label = ( spar_get_option( '', 'points_label' ) ?: esc_html__( 'Points', 'simple-points-and-rewards' ) );
    $status = $order->get_status();
    $status_text = '';
    switch ( $status ) {
        case 'refunded':
            $status_text = esc_html__( 'Refunded', 'simple-points-and-rewards' );
            $action_id = 'referral_refund';
            break;
        case 'cancelled':
            $status_text = esc_html__( 'Cancelled', 'simple-points-and-rewards' );
            $action_id = 'referral_cancelled';
            break;
        case 'failed':
            $status_text = esc_html__( 'Failed', 'simple-points-and-rewards' );
            $action_id = 'referral_failed';
            break;
        default:
            $status_text = esc_html__( 'Refunded/Cancelled', 'simple-points-and-rewards' );
            $action_id = 'referral_refund';
    }
    $action_text = sprintf(
        esc_html__( '%1$s Deducted for %2$s Referred Order: #%3$d', 'simple-points-and-rewards' ),
        $points_label,
        $status_text,
        (int) $order_id
    );
    $action_text = sanitize_text_field( (string) $action_text );
    $action_id = sanitize_key( (string) $action_id );
    // Perform the deduction
    spar_update_user_points(
        $referrer_id,
        (int) $awarded,
        'remove',
        $action_text,
        $action_id
    );
    // Mark as deducted to avoid repeats
    $order->update_meta_data( 'referral_points_deducted', (int) $awarded );
    $order->save();
}

/**
 * On refund events that may not change the status (e.g., partial refunds),
 * only deduct referral points if the order is now fully refunded or moved
 * to a terminal status (refunded/cancelled/failed).
 *
 * @param int $order_id Order ID.
 */
function spar_maybe_deduct_referral_points_on_refund_event(  $order_id  ) {
    // Only run if referral feature is enabled and deductions are allowed
    $options = get_option( 'spar_options', [] );
    if ( empty( $options['earn']['referral']['enabled'] ) ) {
        return;
    }
    $deduct_on_refund = $options['earn']['referral']['deduct_on_refund'] ?? true;
    if ( !$deduct_on_refund ) {
        return;
    }
    $order = wc_get_order( $order_id );
    if ( !$order ) {
        return;
    }
    // If status already indicates a refund/cancellation/failure, run the standard deduction.
    if ( $order->has_status( array('refunded', 'cancelled', 'failed') ) ) {
        spar_deduct_refunded_referral_points( $order_id );
        return;
    }
    // Otherwise, for refund events, only deduct if the order is fully refunded by totals.
    $order_total = (float) $order->get_total();
    $total_refunded = (float) (( method_exists( $order, 'get_total_refunded' ) ? $order->get_total_refunded() : 0 ));
    // Use a small epsilon to account for floating point rounding.
    if ( $order_total > 0 && $order_total - $total_refunded <= 0.01 ) {
        spar_deduct_refunded_referral_points( $order_id );
    }
}

/**
 * Check if order used a referral coupon and return referrer ID
 */
function spar_check_referral_coupon_usage(  $order  ) {
    $options = get_option( 'spar_options', [] );
    // Only run if referral feature is enabled
    if ( empty( $options['earn']['referral']['enabled'] ) ) {
        return null;
    }
    $tracking_mode = $options['earn']['referral']['coupon_tracking_mode'] ?? 'cookie_required';
    // Only check coupon usage in 'always_track' mode
    if ( $tracking_mode !== 'always_track' ) {
        return null;
    }
    // Check all coupons used in the order
    $coupon_codes = $order->get_coupon_codes();
    foreach ( $coupon_codes as $coupon_code ) {
        // Check if this is a referral gift coupon
        if ( preg_match( '/^gift-([A-Z0-9]+)$/i', $coupon_code, $matches ) ) {
            $referral_code = $matches[1];
            $referrer_id = spar_get_user_by_referral_code( $referral_code );
            if ( $referrer_id ) {
                // Intentionally no logging in production to avoid noise
                // Update referrer stats for coupon-based referral
                spar_increment_referral_success( $referrer_id );
                return $referrer_id;
            }
        }
    }
    return null;
}

/**
 * Increment successful referrals (used for coupon-based tracking)
 */
function spar_increment_referral_success(  $referrer_id  ) {
    $stats = spar_get_user_referral_stats( $referrer_id );
    $stats['successful_referrals']++;
    update_user_meta( $referrer_id, 'spar_referral_stats', $stats );
}
