<?php
/**
 * Schema Cache Handler for BW AI Schema Pro
 *
 * @package BW_AI_Schema_Pro
 */

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

class BW_Schema_Cache {

	/**
	 * Cache group
	 */
	const CACHE_GROUP = 'bw_schema_cache';

	/**
	 * Cache expiration (24 hours)
	 */
	const CACHE_EXPIRATION = DAY_IN_SECONDS;

	/**
	 * Transient key prefix used as a fallback when no persistent object cache is
	 * available (since 2.1.5).
	 *
	 * Why this exists: `wp_cache_*` defaults to a single-request in-memory store
	 * on hosts without Redis/Memcached (Flywheel, most shared hosts, etc.). On
	 * those hosts the cache was effectively a no-op — schema regenerated on
	 * every page load. We fall back to transients (DB-backed but persistent
	 * across requests) when `wp_using_ext_object_cache()` is false. When a real
	 * object cache IS available we keep using `wp_cache_*` (faster, no DB hit).
	 */
	const TRANSIENT_PREFIX = 'bw_schema_';

	/**
	 * Whether to use the transient fallback instead of wp_cache_*.
	 *
	 * `wp_using_ext_object_cache()` returns true on hosts that have a drop-in
	 * persistent object cache. Otherwise wp_cache_* is request-scoped, so we
	 * route through transients instead.
	 */
	private static function use_transient_fallback() {
		return ! wp_using_ext_object_cache();
	}

	/**
	 * Get cached schema
	 */
	public static function get( $key ) {
		// Check if caching is enabled
		if ( ! self::is_enabled() ) {
			return false;
		}

		if ( self::use_transient_fallback() ) {
			return get_transient( self::TRANSIENT_PREFIX . $key );
		}

		return wp_cache_get( $key, self::CACHE_GROUP );
	}

	/**
	 * Set cached schema
	 */
	public static function set( $key, $data ) {
		// Check if caching is enabled
		if ( ! self::is_enabled() ) {
			return false;
		}

		if ( self::use_transient_fallback() ) {
			return set_transient( self::TRANSIENT_PREFIX . $key, $data, self::CACHE_EXPIRATION );
		}

		return wp_cache_set( $key, $data, self::CACHE_GROUP, self::CACHE_EXPIRATION );
	}

	/**
	 * Delete cached schema
	 */
	public static function delete( $key ) {
		if ( self::use_transient_fallback() ) {
			return delete_transient( self::TRANSIENT_PREFIX . $key );
		}

		return wp_cache_delete( $key, self::CACHE_GROUP );
	}

	/**
	 * Clear all schema cache
	 */
	public static function clear_all() {
		if ( self::use_transient_fallback() ) {
			// Nuke every transient we own. There's no first-class WP API for this,
			// so we run one direct DELETE against the options table. Limited to
			// keys with our own TRANSIENT_PREFIX (safe — no other plugin's data).
			global $wpdb;
			$like_value   = $wpdb->esc_like( '_transient_' . self::TRANSIENT_PREFIX ) . '%';
			$like_timeout = $wpdb->esc_like( '_transient_timeout_' . self::TRANSIENT_PREFIX ) . '%';
			$wpdb->query( $wpdb->prepare(
				"DELETE FROM {$wpdb->options} WHERE option_name LIKE %s OR option_name LIKE %s",
				$like_value,
				$like_timeout
			) );
			return;
		}

		wp_cache_flush();
	}

	/**
	 * Clear post cache
	 */
	public static function clear_post_cache( $post_id ) {
		// Clear specific post cache
		self::delete( 'post_' . $post_id );

		// Clear author cache
		$post = get_post( $post_id );
		if ( $post ) {
			self::delete( 'author_' . $post->post_author );
		}

		// Clear organization cache
		self::delete( 'organization' );
		self::delete( 'website' );
	}
	
	/**
	 * Generate cache key
	 */
	public static function get_cache_key( $type, $id = null ) {
		if ( $id ) {
			return $type . '_' . $id;
		}
		return $type;
	}
	
	/**
	 * Check if caching is enabled
	 */
	public static function is_enabled() {
		// Check if caching is disabled in settings
		if ( get_option( 'bw_schema_enable_cache', 'yes' ) === 'no' ) {
			return false;
		}
		
		// Don't cache in development environments
		if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
			return false;
		}
		
		// Don't cache for logged-in users (admins)
		if ( is_user_logged_in() && current_user_can( 'manage_options' ) ) {
			return false;
		}
		
		return true;
	}
	
	/**
	 * Hook into WordPress actions
	 */
	public static function init() {
		// Clear cache when posts are saved
		add_action( 'save_post', array( __CLASS__, 'clear_post_cache' ) );
		add_action( 'delete_post', array( __CLASS__, 'clear_post_cache' ) );
		
		// Clear cache when plugin settings are updated
		add_action( 'update_option_bw_schema_organization', array( __CLASS__, 'clear_all' ) );
		add_action( 'update_option_bw_schema_enable_schema', array( __CLASS__, 'clear_all' ) );
		add_action( 'update_option_bw_schema_enable_breadcrumbs', array( __CLASS__, 'clear_all' ) );
		add_action( 'update_option_bw_schema_post_type_defaults', array( __CLASS__, 'clear_all' ) );
		
		// Clear cache when user meta is updated
		add_action( 'updated_user_meta', array( __CLASS__, 'clear_author_cache' ), 10, 3 );
	}
	
	/**
	 * Clear author cache when user meta is updated
	 */
	public static function clear_author_cache( $meta_id, $user_id, $meta_key ) {
		if ( $meta_key === '_bw_schema_author_data' ) {
			self::delete( 'author_' . $user_id );
		}
	}
}