<?php
/**
 * Admin analytics helpers
 *
 * Provides lightweight aggregate statistics for display in the admin header.
 *
 * @package Simple Points and Rewards
 */

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

/**
 * Get a snapshot of key analytics values for admin UI.
 *
 * Returns array keys:
 * - earned              (int) Lifetime points earned (sum of all positive awards)
 * - redeemed            (int) Lifetime points redeemed
 * - net                 (int) Current total balance across all users
 * - in_circulation      (int) Same as net
 * - users_with_points   (int) Users who have ever earned points (> 0 lifetime)
 * - active_customers_30 (int) Distinct users with any points activity in last 30 days
 *
 *
 * Filters:
 * - spar_admin_analytics_snapshot_cache_ttl (int) TTL in seconds; default 300 (5 min)
 * - spar_admin_analytics_snapshot           (array) Final snapshot array
 */
function spar_admin_analytics_cache_key() {
	return 'spar_admin_analytics_snapshot_v4';
}

function spar_admin_analytics_build_lock_key() {
	return 'spar_admin_analytics_build_lock';
}

function spar_admin_analytics_stale_flag_key() {
	return 'spar_admin_analytics_stale_flag';
}

function spar_get_admin_analytics_snapshot() {
	// Static cache: free on any repeated call within the same PHP request.
	static $request_cache = null;
	if ( null !== $request_cache ) {
		return $request_cache;
	}

	$cache_key = spar_admin_analytics_cache_key();
	$lock_key  = spar_admin_analytics_build_lock_key();
	$stale_key = spar_admin_analytics_stale_flag_key();
	$ttl       = (int) apply_filters( 'spar_admin_analytics_snapshot_cache_ttl', 300 );
	$lock_ttl  = (int) apply_filters( 'spar_admin_analytics_build_lock_ttl', 20 );

	// Check the stale flag + transient together. Both go through the object cache on
	// sites that have one (Memcached/Redis), so this is a single fast lookup.
	$is_stale = (bool) get_transient( $stale_key );
	$cached   = get_transient( $cache_key );
	if ( is_array( $cached ) && ! $is_stale ) {
		$request_cache = $cached;
		return $request_cache;
	}

	global $wpdb;

	$logs_table   = $wpdb->prefix . 'spar_points_logs';
	$usermeta_tbl = $wpdb->usermeta;

	// --- Table-existence check (cached in object cache, not re-run on every page load) ---
	$exists_cache_key  = 'spar_logs_table_exists';
	$logs_table_exists = wp_cache_get( $exists_cache_key, 'spar' );
	if ( false === $logs_table_exists ) {
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$logs_table_exists = ( $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $logs_table ) ) === $logs_table ) ? 'yes' : 'no';
		wp_cache_set( $exists_cache_key, $logs_table_exists, 'spar', 300 );
	}
	$logs_table_exists = ( 'yes' === $logs_table_exists );

	// Acquire a short build lock to prevent query stampedes on cache miss.
	// If another process holds the lock and we still have a stale snapshot, return it.
	$acquired_lock = false;
	if ( $logs_table_exists ) {
		if ( get_transient( $lock_key ) ) {
			if ( is_array( $cached ) ) {
				$request_cache = $cached;
				return $request_cache;
			}
			// No stale snapshot available — fall through and compute without touching the lock.
		} else {
			set_transient( $lock_key, 1, max( 5, $lock_ttl ) );
			$acquired_lock = true;
		}
	}

	$earned_raw   = 0;
	$redeemed_raw = 0;
	$active_30    = 0;

	if ( $logs_table_exists ) {
		// Single-pass: earned + redeemed totals AND active-30 count in one query.
		$since_str = date_i18n( 'Y-m-d H:i:s', current_time( 'timestamp' ) - 30 * DAY_IN_SECONDS );
		// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
		$row = $wpdb->get_row(
			$wpdb->prepare(
				"SELECT
					COALESCE(SUM(CASE WHEN (points > 0 OR type = 'add')    THEN points    ELSE 0 END), 0) AS earned,
					COALESCE(SUM(CASE WHEN (points < 0 OR type = 'remove') THEN ABS(points) ELSE 0 END), 0) AS redeemed,
					COUNT(DISTINCT CASE WHEN date >= %s THEN user_id END) AS active_30
				FROM %i",
				$since_str,
				$logs_table
			),
			ARRAY_A
		);
		$earned_raw   = isset( $row['earned'] )    ? (int) $row['earned']    : 0;
		$redeemed_raw = isset( $row['redeemed'] )  ? (int) $row['redeemed']  : 0;
		$active_30    = isset( $row['active_30'] ) ? (int) $row['active_30'] : 0;
	}

	// Single-pass usermeta query: in-circulation balance + users-with-earned-points.
	// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
	$um_row = $wpdb->get_row(
		$wpdb->prepare(
			"SELECT
				COALESCE(SUM(CASE WHEN meta_key = %s THEN CAST(meta_value AS SIGNED) ELSE 0 END), 0) AS in_circ,
				SUM(CASE WHEN meta_key = %s AND CAST(meta_value AS SIGNED) > 0 THEN 1 ELSE 0 END) AS users_with_earned
			FROM {$usermeta_tbl}
			WHERE meta_key IN (%s, %s)",
			'_spar_points',
			'_spar_total_earned_points',
			'_spar_points',
			'_spar_total_earned_points'
		),
		ARRAY_A
	);
	$in_circ              = isset( $um_row['in_circ'] )         ? (int) $um_row['in_circ']         : 0;
	$users_with_earned    = isset( $um_row['users_with_earned'] ) ? (int) $um_row['users_with_earned'] : 0;

	$snapshot = array(
		'earned'              => max( 0, $earned_raw ),
		'redeemed'            => max( 0, $redeemed_raw ),
		'net'                 => max( 0, $in_circ ),
		'in_circulation'      => max( 0, $in_circ ),
		'users_with_points'   => max( 0, $users_with_earned ),
		'active_customers_30' => max( 0, $active_30 ),
	);

	$snapshot = apply_filters( 'spar_admin_analytics_snapshot', $snapshot );
	set_transient( $cache_key, $snapshot, max( 0, $ttl ) );
	delete_transient( $stale_key );
	if ( $acquired_lock ) {
		delete_transient( $lock_key );
	}

	$request_cache = $snapshot;
	return $request_cache;
}

/**
 * Helper to format large numbers compactly (e.g., 12.3k, 4.5M) while keeping i18n
 * for decimals. Falls back to number_format_i18n() for small values.
 *
 * @param int|float $number
 * @return string
 */
function spar_format_compact_number( $number ) {
	$number = (float) $number;
	if ( $number >= 1000000 ) {
		return sprintf( _x( '%sM', 'millions short format', 'simple-points-and-rewards' ), number_format_i18n( $number / 1000000, 1 ) );
	}
	if ( $number >= 1000 ) {
		return sprintf( _x( '%sk', 'thousands short format', 'simple-points-and-rewards' ), number_format_i18n( $number / 1000, 1 ) );
	}
	return number_format_i18n( $number );
}

/**
 * Invalidate cached analytics snapshot whenever points change.
 */
function spar_admin_analytics_invalidate() {
	// Mark snapshot stale; keep existing until next rebuild to avoid stampede.
	set_transient( spar_admin_analytics_stale_flag_key(), 1, 120 );
}
add_action( 'spar_after_points_update', 'spar_admin_analytics_invalidate' );
add_action( 'spar_points_added', 'spar_admin_analytics_invalidate' );
add_action( 'spar_points_removed', 'spar_admin_analytics_invalidate' );
