<?php

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

/**
 * Manages the local screenshot cache stored in wp-content/uploads/dynamic-og-images/.
 */
class DOIG_Cache {

	/** Cache subdirectory name inside wp uploads. */
	const SUBDIR = 'dynamic-og-images';

	/** @var string|null Cached absolute path to the cache directory. */
	private static $dir = null;

	/** @var string|null Cached base URL to the cache directory. */
	private static $url = null;

	/**
	 * Hook registration (none required — all methods called directly).
	 */
	public static function init() {}

	// -------------------------------------------------------------------------
	// Directory helpers
	// -------------------------------------------------------------------------

	/**
	 * Returns the absolute path to the cache directory (with trailing slash).
	 *
	 * @return string
	 */
	public static function get_dir() {
		if ( null === self::$dir ) {
			$upload       = wp_upload_dir();
			self::$dir    = trailingslashit( $upload['basedir'] ) . self::SUBDIR . '/';
		}
		return self::$dir;
	}

	/**
	 * Returns the base URL of the cache directory (with trailing slash).
	 *
	 * @return string
	 */
	public static function get_url() {
		if ( null === self::$url ) {
			$upload    = wp_upload_dir();
			self::$url = trailingslashit( $upload['baseurl'] ) . self::SUBDIR . '/';
		}
		return self::$url;
	}

	/**
	 * Creates the cache directory and drops in security files.
	 * Called on plugin activation.
	 */
	public static function create_directory() {
		$dir = self::get_dir();

		if ( ! is_dir( $dir ) ) {
			wp_mkdir_p( $dir );
			// DOIG_Logger may not be available yet during activation — guard defensively.
			if ( class_exists( 'DOIG_Logger' ) ) {
				DOIG_Logger::debug( 'Cache directory created: ' . $dir );
			}
		}

		// Silence direct directory access
		$index = $dir . 'index.php';
		if ( ! file_exists( $index ) ) {
			file_put_contents( $index, '<?php // Silence is golden.' );
		}

		// Block directory listing on Apache
		$htaccess = $dir . '.htaccess';
		if ( ! file_exists( $htaccess ) ) {
			file_put_contents( $htaccess, 'Options -Indexes' . PHP_EOL );
		}
	}

	// -------------------------------------------------------------------------
	// File path / URL helpers
	// -------------------------------------------------------------------------

	/**
	 * Returns the absolute file path for a post's cached screenshot.
	 *
	 * @param int $post_id
	 * @return string
	 */
	public static function get_file_path( $post_id ) {
		return self::get_dir() . absint( $post_id ) . '.webp';
	}

	/**
	 * Returns the public URL for a post's cached screenshot.
	 *
	 * @param int $post_id
	 * @return string
	 */
	public static function get_file_url( $post_id ) {
		return self::get_url() . absint( $post_id ) . '.webp';
	}

	// -------------------------------------------------------------------------
	// State checks
	// -------------------------------------------------------------------------

	/**
	 * Returns true if a cached file exists for the given post.
	 *
	 * @param int $post_id
	 * @return bool
	 */
	public static function exists( $post_id ) {
		return file_exists( self::get_file_path( $post_id ) );
	}

	/**
	 * Returns true if the cached file exists AND is within the refresh interval.
	 *
	 * @param int $post_id
	 * @return bool
	 */
	public static function is_fresh( $post_id ) {
		$path = self::get_file_path( $post_id );

		if ( ! file_exists( $path ) ) {
			return false;
		}

		// When the user has disabled scheduled refresh, an existing cached
		// file is NEVER considered stale by time. Post-update auto-regen
		// and the explicit Regenerate Now / Regenerate All buttons still
		// work; only the schedule-driven sweep stops queueing this post.
		// The daily cron still runs (it also handles quota_exceeded
		// requeues, which are independent of the refresh schedule).
		if ( ! DOIG_Settings::get( 'refresh_enabled', true ) ) {
			return true;
		}

		$interval_days    = (int) DOIG_Settings::get( 'refresh_interval', 7 );
		$interval_seconds = $interval_days * DAY_IN_SECONDS;
		$cached_at        = (int) DOIG_Post_Meta::get( $post_id, 'cached_at' );

		if ( 0 === $cached_at ) {
			// Fall back to file mtime if meta is missing
			$cached_at = filemtime( $path );
		}

		return ( time() - $cached_at ) < $interval_seconds;
	}

	// -------------------------------------------------------------------------
	// Purge
	// -------------------------------------------------------------------------

	/**
	 * Deletes the cached file for a single post and resets its meta.
	 *
	 * @param int $post_id
	 * @return bool
	 */
	public static function purge( $post_id ) {
		$path = self::get_file_path( $post_id );

		if ( file_exists( $path ) ) {
			wp_delete_file( $path );
		}

		DOIG_Post_Meta::set( $post_id, 'status',       '' );
		DOIG_Post_Meta::set( $post_id, 'cached_url',   '' );
		DOIG_Post_Meta::set( $post_id, 'cached_at',    '' );
		DOIG_Post_Meta::set( $post_id, 'scheduled_at', '' );
		// 2.4.0 M5: clear the orphan-sweep tracker so a future
		// fresh capture starts from a clean slate. Set to '' rather
		// than deleting the row so the meta-key shape stays stable
		// across the codebase.
		DOIG_Post_Meta::set( $post_id, 'orphaned_at',  '' );

		DOIG_Logger::info( 'Cache purged.', array( 'post_id' => $post_id ) );

		return true;
	}

	/**
	 * Deletes all cached files and resets all post meta.
	 *
	 * @return int Number of files deleted.
	 */
	public static function purge_all() {
		$dir   = self::get_dir();
		$files = glob( $dir . '*.webp' );
		$count = 0;

		if ( $files ) {
			foreach ( $files as $file ) {
				wp_delete_file( $file );
				if ( ! file_exists( $file ) ) {
					$post_id = (int) basename( $file, '.webp' );
					if ( $post_id > 0 ) {
						DOIG_Post_Meta::set( $post_id, 'status',       '' );
						DOIG_Post_Meta::set( $post_id, 'cached_url',   '' );
						DOIG_Post_Meta::set( $post_id, 'cached_at',    '' );
						DOIG_Post_Meta::set( $post_id, 'scheduled_at', '' );
					}
					$count++;
				}
			}
		}

		DOIG_Logger::info( "All cache purged — {$count} file(s) deleted." );

		return $count;
	}

	// -------------------------------------------------------------------------
	// Stats
	// -------------------------------------------------------------------------

	/**
	 * Returns cache statistics for display in the admin.
	 *
	 * @return array { count: int, size_bytes: int, size_human: string }
	 */
	public static function stats() {
		$dir   = self::get_dir();
		$files = is_dir( $dir ) ? glob( $dir . '*.webp' ) : array();
		$files = $files ?: array();

		$size = 0;
		foreach ( $files as $file ) {
			$size += filesize( $file );
		}

		return array(
			'count'      => count( $files ),
			'size_bytes' => $size,
			'size_human' => size_format( $size, 1 ),
		);
	}
}
