<?php

/**
 * Plugin Name: QuantumCache
 * Description: Deterministic, tag-based query & fragment caching for anonymous traffic. Redis preferred with MySQL fallback. Prevents wp_options/wp_postmeta bloat.
 * Version: 1.0.0
 * Author: Aaron DeLong
 * License: GPLv2 or later
 * License URI: https://www.gnu.org/licenses/gpl-2.0.html
 * Text Domain: quantumcache
 * Domain Path: /languages
 * Requires at least: 6.0
 * Tested up to: 7.0
 * Requires PHP: 8.0
 */


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


// Constants (allow overrides via wp-config or env)

if ( ! defined( 'QUANTUMCACHE_REDIS_HOST' ) ) {
	define( 'QUANTUMCACHE_REDIS_HOST', '127.0.0.1' );
}
if ( ! defined( 'QUANTUMCACHE_REDIS_PORT' ) ) {
	define( 'QUANTUMCACHE_REDIS_PORT', 6379 );
}
if ( ! defined( 'QUANTUMCACHE_REDIS_DB' ) ) {
	define( 'QUANTUMCACHE_REDIS_DB', 0 );
}
if ( ! defined( 'QUANTUMCACHE_KEY_PREFIX' ) ) {
	define( 'QUANTUMCACHE_KEY_PREFIX', 'qc:v1:' );
}
if ( ! defined( 'QUANTUMCACHE_CACHE_LOGGED_IN' ) ) {
	define( 'QUANTUMCACHE_CACHE_LOGGED_IN', false );
} // Free: anon only
if ( ! defined( 'QUANTUMCACHE_VERSION' ) ) {
	define( 'QUANTUMCACHE_VERSION', '1.0.0' );
}


if ( ! defined( 'QUANTUMCACHE_FILE' ) ) {
	define( 'QUANTUMCACHE_FILE', __FILE__ );
}


// Autoload (Composer if available, else lightweight PSR-4)
$quantumcache_composer = __DIR__ . '/vendor/autoload.php';
if ( file_exists( $quantumcache_composer ) ) {
	require_once $quantumcache_composer;
} else {
	spl_autoload_register(
		static function ( $fqcn ) {
			// Ensure class is in our namespace. Use constant-first comparison for PHPCS.
			if ( 0 !== strpos( $fqcn, 'QuantumCache\\' ) ) {
				return;
			}
			$rel  = substr( $fqcn, strlen( 'QuantumCache\\' ) );
			$path = __DIR__ . '/src/' . str_replace( '\\', '/', $rel ) . '.php';
			if ( file_exists( $path ) ) {
				require_once $path;
			}
		}
	);

}


register_activation_hook(
	__FILE__,
	function ( $network_wide ) {
		if ( function_exists( 'is_multisite' ) && is_multisite() && $network_wide ) {
			$sites = get_sites( array( 'fields' => 'ids' ) );
			foreach ( $sites as $blog_id ) {
				switch_to_blog( $blog_id );
				\QuantumCache\Store\MySQLStore::activate();
				\QuantumCache\Support\Metrics::activate();
				restore_current_blog();
			}
		} else {
			\QuantumCache\Store\MySQLStore::activate();
			\QuantumCache\Support\Metrics::activate();
		}
	}
);


add_action(
	'plugins_loaded',
	function () {
		QuantumCache\Plugin::init();
		do_action( 'quantumcache_loaded', QUANTUMCACHE_VERSION );
	}
);

// Create QC tables when a new site is created (multisite)
add_action(
	'wpmu_new_blog',
	function ( $blog_id ) {
		switch_to_blog( $blog_id );
		\QuantumCache\Store\MySQLStore::activate();
		restore_current_blog();
	}
);


if ( ! function_exists( 'QuantumCache\\quantumcache_should_debug_headers' ) ) {
	function quantumcache_should_debug_headers(): bool {
		// Allow WP_DEBUG to enable in dev, and an option to toggle in prod
		$opt = function_exists( 'get_option' ) ? (bool) get_option( 'quantumcache_debug_headers', false ) : false;
		$env = defined( 'WP_DEBUG' ) && WP_DEBUG;
		return apply_filters( 'quantumcache_debug_headers_enabled', $env || $opt );
	}
}

if ( ! function_exists( 'quantumcache_purge_tags' ) ) {
	/**
	 * Public API: request purge by tags. Store layer / integrations listen to this action.
	 *
	 * @param string[] $tags
	 */
	function quantumcache_purge_tags( array $tags ): void {
		$tags = array_values(
			array_unique(
				array_filter( $tags, 'is_string' )
			)
		);

		if ( ! $tags ) {
			return;
		}

		do_action( 'quantumcache_purge_tags', $tags );
	}
}

if ( ! function_exists( 'qc_schedule_cdn_purge' ) ) {
	/**
	 * Accumulate tags for a deferred CDN purge fired once at shutdown.
	 *
	 * Store::purgeTags() may be called dozens of times per request (e.g. from
	 * WooCommerce cascading save_post hooks). Batching all tags into a single
	 * do_action('quantumcache_purge_tags') call at shutdown prevents blocking
	 * the current request on N sequential HTTP purge calls.
	 *
	 * @param string[] $tags
	 */
	function qc_schedule_cdn_purge( array $tags ): void {
		global $qc_pending_cdn_tags;

		if ( ! isset( $qc_pending_cdn_tags ) ) {
			$qc_pending_cdn_tags = array();
			add_action( 'shutdown', 'qc_flush_pending_cdn_purge', PHP_INT_MAX - 1 );
		}

		$qc_pending_cdn_tags = array_values(
			array_unique(
				array_merge( $qc_pending_cdn_tags, $tags )
			)
		);
	}
}

if ( ! function_exists( 'qc_flush_pending_cdn_purge' ) ) {
	/**
	 * Fire the batched CDN purge action at shutdown.
	 */
	function qc_flush_pending_cdn_purge(): void {
		global $qc_pending_cdn_tags;

		if ( ! empty( $qc_pending_cdn_tags ) ) {
			do_action( 'quantumcache_purge_tags', $qc_pending_cdn_tags );
			$qc_pending_cdn_tags = array();
		}
	}
}

if ( ! function_exists( 'quantumcache_allow_logged_in_cache' ) ) {
	/**
	 * Decide whether QuantumCache is allowed to cache logged-in requests.
	 *
	 * Default:
	 *   - true  if QUANTUMCACHE_CACHE_LOGGED_IN is explicitly true
	 *   - false otherwise
	 *
	 * Extensions (e.g., QC Pro) can override via the 'quantumcache_allow_logged_in_cache' filter.
	 */
	function quantumcache_allow_logged_in_cache(): bool {
		$base = defined( 'QUANTUMCACHE_CACHE_LOGGED_IN' ) && true === QUANTUMCACHE_CACHE_LOGGED_IN;

		// Is there *any* filter hooked?
		$has = function_exists( 'has_filter' )
			? has_filter( 'quantumcache_allow_logged_in_cache' )
			: false;

		$filtered = apply_filters( 'quantumcache_allow_logged_in_cache', $base );

		return (bool) $filtered;
	}
}
