<?php
/**
 * Astra Cache
 *
 * @package     Astra
 * @link        https://www.brainstormforce.com
 * @since       Astra 2.1.0
 */

/**
 * Class Astra_Cache_Base.
 */
// @codingStandardsIgnoreStart
class Astra_Cache_Base {
	// @codingStandardsIgnoreEnd

	/**
	 * Member Variable
	 *
	 * @var array instance
	 */
	private static $dynamic_css_files = array();

	/**
	 * Asset slug for filename.
	 *
	 * @since 2.1.0
	 * @var string
	 */
	private $asset_slug = '';

	/**
	 * Check if we are on a single or archive query page.
	 *
	 * @since 2.1.0
	 * @var string
	 */
	private $asset_query_var = '';

	/**
	 * Asset Type - archive/post
	 *
	 * @since 2.1.0
	 * @var string
	 */
	private $asset_type = '';

	/**
	 * Uploads directory.
	 *
	 * @since 2.1.0
	 * @var array
	 */
	private $uploads_dir = array();

	/**
	 * Cache directory from uploads folder.
	 *
	 * @since 2.1.0
	 * @var String
	 */
	private $cache_dir;

	/**
	 * Set priority for add_action call for action `wp_enqueue_scripts`
	 *
	 * @since 2.1.0
	 * @var string
	 */
	protected $asset_priority = '';

	/**
	 * Constructor
	 *
	 * @since 2.1.0
	 * @param String $cache_dir Base cache directory in the uploads directory.
	 */
	public function __construct( $cache_dir ) {
		if ( true === is_admin() ) {
			return false;
		}

		$this->cache_dir = $cache_dir;

		add_action( 'wp', array( $this, 'init_cache' ) );
		add_action( 'wp_enqueue_scripts', array( $this, 'setup_cache' ), $this->asset_priority );
	}

	/**
	 * Setup class variables.
	 *
	 * @since 2.1.0
	 * @return void
	 */
	public function init_cache() {
		$this->asset_type      = $this->asset_type();
		$this->asset_query_var = $this->asset_query_var();
		$this->asset_slug      = $this->asset_slug();
		$this->uploads_dir     = astra_addon_filesystem()->get_uploads_dir( $this->cache_dir );

		// Create uploads directory.
		astra_addon_filesystem()->maybe_create_uploads_dir( $this->uploads_dir['path'] );
	}

	/**
	 * Get Current query type. single|archive.
	 *
	 * @since 2.1.0
	 * @return String
	 */
	private function asset_query_var() {
		if ( 'post' === $this->asset_type || 'home' === $this->asset_type || 'frontpage' === $this->asset_type ) {
			$slug = 'single';
		} else {
			$slug = 'archive';
		}

		return apply_filters( 'astra_addon_cache_asset_query_var', $slug );
	}

	/**
	 * Get current asset slug.
	 *
	 * @since 2.1.0
	 * @return String
	 */
	private function asset_slug() {
		if ( 'home' === $this->asset_type || 'frontpage' === $this->asset_type ) {
			return $this->asset_type;
		}

		return $this->asset_type . $this->cache_key_suffix();
	}

	/**
	 * Append queried object ID to cache if it is not `0`
	 *
	 * @since 2.1.0
	 * @return Mixed queried object id if that is not 0; else false.
	 */
	private function cache_key_suffix() {
		return get_queried_object_id() !== 0 ? '-' . get_queried_object_id() : false;
	}

	/**
	 * Get the archive title.
	 *
	 * @since  2.1.0
	 * @return $title Returns the archive title.
	 */
	private function asset_type() {
		$title = 'post';

		if ( is_category() ) {
			$title = 'category';
		} elseif ( is_tag() ) {
			$title = 'tag';
		} elseif ( is_author() ) {
			$title = 'author';
		} elseif ( is_year() ) {
			$title = 'year-' . get_query_var( 'year' );
		} elseif ( is_month() ) {
			$title = 'month-' . get_query_var( 'monthnum' );
		} elseif ( is_day() ) {
			$title = 'day-' . get_query_var( 'day' );
		} elseif ( is_tax( 'post_format' ) ) {
			if ( is_tax( 'post_format', 'post-format-aside' ) ) {
				$title = 'asides';
			} elseif ( is_tax( 'post_format', 'post-format-gallery' ) ) {
				$title = 'galleries';
			} elseif ( is_tax( 'post_format', 'post-format-image' ) ) {
				$title = 'images';
			} elseif ( is_tax( 'post_format', 'post-format-video' ) ) {
				$title = 'videos';
			} elseif ( is_tax( 'post_format', 'post-format-quote' ) ) {
				$title = 'quotes';
			} elseif ( is_tax( 'post_format', 'post-format-link' ) ) {
				$title = 'links';
			} elseif ( is_tax( 'post_format', 'post-format-status' ) ) {
				$title = 'statuses';
			} elseif ( is_tax( 'post_format', 'post-format-audio' ) ) {
				$title = 'audio';
			} elseif ( is_tax( 'post_format', 'post-format-chat' ) ) {
				$title = 'chats';
			}
		} elseif ( is_post_type_archive() ) {
			// Allowing paths for custom post type archives.
			// get_query_var( 'post_type' ) returns an array when multiple CPTs share an archive rewrite slug.
			$post_type = get_query_var( 'post_type' );
			if ( is_array( $post_type ) ) {
				$post_type = ! empty( $post_type ) ? reset( $post_type ) : '';
			}
			$title = $post_type . '-archives';
		} elseif ( is_tax() ) {
			$queried_object = get_queried_object();
			if ( $queried_object && is_a( $queried_object, 'WP_Term' ) ) {
				$tax   = get_taxonomy( $queried_object->taxonomy );
				$title = sanitize_key( $tax->name );
			}
		}

		if ( is_search() ) {
			$title = 'search';
		}

		if ( is_404() ) {
			$title = '404';
		}

		if ( is_front_page() ) {
			$title = 'post';
		}

		if ( is_home() ) {
			$title = 'home';
		}

		return apply_filters( 'astra_addon_cache_asset_type', $title );
	}

	/**
	 * Create an array of all the files that needs to be merged in dynamic CSS file.
	 *
	 * @since 2.1.0
	 * @param array $file file path.
	 * @return void
	 */
	public static function add_css_file( $file ) {
	}

	/**
	 * Append CSS style to the theme dynamic css.
	 *
	 * @since 2.1.0
	 * @param Array $dynamic_css_files Array of file paths to be to be added to minify cache.
	 * @return String CSS from the CSS files passed.
	 */
	public function get_css_from_files( $dynamic_css_files ) {
		$dynamic_css_data = '';

		foreach ( $dynamic_css_files as $value ) {
			// Get file contents.
			$get_contents = astra_addon_filesystem()->get_contents( $value );
			if ( $get_contents ) {
				$dynamic_css_data .= $get_contents;
			}
		}

		return $dynamic_css_data;
	}

	/**
	 * Refresh Assets, called through ajax
	 *
	 * @since 2.1.0
	 * @param String $cache_dir dirname of the cache.
	 * @return void
	 */
	public function ajax_refresh_assets( $cache_dir ) {
		check_ajax_referer( 'astra_addon_update_admin_setting', 'security' );

		if ( ! current_user_can( 'edit_theme_options' ) ) {
			wp_send_json_error(
				array(
					'code'    => 'permission_denied',
					'message' => __( 'You do not have permission to regenerate Astra Pro assets.', 'astra-addon' ),
				)
			);
		}

		// Guard filesystem before init_cache(): init_cache() calls
		// maybe_create_uploads_dir() → get_filesystem() internally, so a broken
		// FTP object would fatal before the check below if we ran init_cache() first.
		if ( ! astra_addon_filesystem()->get_filesystem() ) {
			astra_addon_filesystem()->update_filesystem_access_status( false );
			wp_send_json_error(
				array(
					'code'    => 'filesystem_unavailable',
					'message' => __( 'Could not regenerate Astra Pro assets because WordPress cannot write to the filesystem on this site. This usually happens when the host requires FTP credentials. Please contact your hosting provider to enable direct filesystem access.', 'astra-addon' ),
				)
			);
		}

		$this->init_cache();
		astra_addon_filesystem()->reset_filesystem_access_status();

		$this->delete_cache_files( $cache_dir );

		wp_send_json_success();
	}

	/**
	 * Refresh Assets with smart cache clearing logic.
	 *
	 * This method handles selective cache clearing:
	 * - For post updates: Only clears cache for that specific post
	 * - For customizer/manual refresh: Clears all cache
	 *
	 * @since 2.1.0
	 * @param String    $cache_dir dirname of the cache.
	 * @param int|mixed $post_id Optional. Post ID for selective clearing, or other object types (e.g., WP_Customize_Manager).
	 * @return void
	 */
	public function refresh_assets( $cache_dir, $post_id = null ) {
		if ( ! current_user_can( 'edit_theme_options' ) ) {
			return;
		}

		// Don't clear cache for autosaves or revisions.
		if ( $this->is_post_autosave_or_revision( $post_id ) ) {
			return;
		}

		$this->init_cache();
		astra_addon_filesystem()->reset_filesystem_access_status();

		$this->delete_cache_files( $cache_dir, $post_id );
	}

	/**
	 * Check if the given post ID is an autosave or revision.
	 *
	 * @since 4.12.2
	 * @param int|mixed $post_id Post ID to check.
	 * @return bool True if autosave or revision, false otherwise.
	 */
	public function is_post_autosave_or_revision( $post_id ) {
		// If not a valid integer post ID, do full cache clear.
		if ( null !== $post_id && is_int( $post_id ) && $post_id !== 0 ) {
			// Don't clear cache for autosaves or revisions.
			if ( wp_is_post_autosave( $post_id ) || wp_is_post_revision( $post_id ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Deletes cache files
	 *
	 * @since 2.1.0
	 * @param String $cache_dir dirname of the cache.
	 * @param int    $post_id Optional. If provided, only delete cache for this specific post.
	 * @return void
	 */
	private function delete_cache_files( $cache_dir, $post_id = null ) {
		$cache_dir  = astra_addon_filesystem()->get_uploads_dir( $cache_dir );
		$filesystem = astra_addon_filesystem()->get_filesystem();
		if ( ! $filesystem ) {
			return;
		}
		$cache_files = $filesystem->dirlist( $cache_dir['path'], false, true );

		if ( is_array( $cache_files ) ) {

			foreach ( $cache_files as $file ) {
				// don't delete index.php file.
				if ( 'index.php' === $file['name'] ) {
					continue;
				}

				// Delete only dynamic CSS files.
				if ( false === strpos( $file['name'], 'dynamic-css' ) ) {
					continue;
				}

				// If post_id is specified, only delete files for that specific post.
				if ( null !== $post_id && is_int( $post_id ) && $post_id > 0 ) {
					// Check if filename contains the post ID (e.g., astra-theme-dynamic-css-post-123.css).
					if ( false === strpos( $file['name'], '-post-' . $post_id . '.css' ) ) {
						continue;
					}
				}

				astra_addon_filesystem()->delete( trailingslashit( $cache_dir['path'] ) . $file['name'], true, 'f' );
			}
		}
	}

	/**
	 * Fetch theme CSS data to be added in the dynamic CSS file.
	 *
	 * @since 2.1.0
	 * @return void
	 */
	public function setup_cache() {
	}

	/**
	 * Write dynamic asset files.
	 *
	 * @param String $style_data Dynamic CSS.
	 * @param String $type Asset type.
	 * @return void
	 */
	protected function write_assets( $style_data, $type ) {
		$allow_file_generation = get_option( '_astra_file_generation', 'disable' );

		if ( 'disable' === $allow_file_generation || is_customize_preview() ) {
			return;
		}

		$assets_info    = $this->get_asset_info( $type );
		$post_timestamp = $this->get_post_timestamp( $assets_info );

		// Check if we need to create a new file or override the current file.
		if ( ! empty( $style_data ) && true === $post_timestamp['create_new_file'] ) {
			$this->file_write( $style_data, $post_timestamp['timestamp'], $assets_info );
		}
	}

	/**
	 * Get Dynamic CSS.
	 *
	 * @since 2.1.0
	 * @return void
	 */
	protected function get_dynamic_css() {
	}

	/**
	 * Enqueue CSS files.
	 *
	 * @param  string $type         Gets the type theme/addon.
	 * @since  2.1.0
	 * @return void
	 */
	public function enqueue_styles( $type ) {

		if ( self::inline_assets() ) {
			wp_add_inline_style( 'astra-' . $type . '-css', $this->get_dynamic_css() );
		} else {
			$assets_info    = $this->get_asset_info( $type );
			$post_timestamp = $this->get_post_timestamp( $assets_info );
			if ( isset( $this->uploads_dir['url'] ) ) {
				wp_enqueue_style( 'astra-' . $type . '-dynamic', $this->uploads_dir['url'] . 'astra-' . $type . '-dynamic-css-' . $this->asset_slug . '.css', array( 'astra-' . $type . '-css' ), $post_timestamp['timestamp'] );
			}
		}
	}

	/**
	 * Enqueue the assets inline.
	 *
	 * @since 2.1.0
	 * @return bool
	 */
	public static function inline_assets() {
		$inline                = false;
		$allow_file_generation = get_option( '_astra_file_generation', 'disable' );

		if ( ( defined( 'SCRIPT_DEBUG' ) && true === SCRIPT_DEBUG ) || ! astra_addon_filesystem()->can_access_filesystem()
			|| 'disable' === $allow_file_generation || is_customize_preview() ) {
			$inline = true;
		}

		return apply_filters( 'astra_addon_load_dynamic_css_inline', $inline );
	}

	/**
	 * Returns the current Post Meta/ Option Timestamp.
	 *
	 * @since  2.1.0
	 * @param  string $assets_info  Gets the assets path info.
	 * @return array $timestamp_data.
	 */
	public function get_post_timestamp( $assets_info ) {
		// Check if current page is a post/ archive page. false states that the current page is a post.
		if ( 'single' === $this->asset_query_var ) {
			$post_timestamp = get_post_meta( get_the_ID(), 'astra_style_timestamp_css', true );
		} else {
			$post_timestamp = get_option( 'astra_get_dynamic_css' );
		}

		return $this->maybe_get_new_timestamp( $post_timestamp, $assets_info );
	}

	/**
	 * Gets the current timestamp.
	 *
	 * @since 2.1.0
	 * @return string $timestamp Timestamp.
	 */
	private function get_current_timestamp() {
		$date = new DateTime();
		return $date->getTimestamp();
	}

	/**
	 * Returns the current Post Meta/ Option Timestamp or creates a new timestamp.
	 *
	 * @since  2.1.0
	 * @param  string $post_timestamp Timestamp of the post meta/ option.
	 * @param  string $assets_info  Gets the assets path info.
	 * @return array $data.
	 */
	public function maybe_get_new_timestamp( $post_timestamp, $assets_info ) {
		// Creates a new timestamp if the file does not exists or the timestamp is empty.
		// If post_timestamp is empty that means it is an new post or the post is updated and a new file needs to be created.
		// If a file does not exists then we need to create a new file.
		if ( '' == $post_timestamp || ( isset( $assets_info['path'] ) && ! empty( $assets_info['path'] ) && ! file_exists( $assets_info['path'] ) ) ) {
			$timestamp = $this->get_current_timestamp();

			$data = array(
				'create_new_file' => true,
				'timestamp'       => $timestamp,
			);
		} else {
			$timestamp = $post_timestamp;
			$data      = array(
				'create_new_file' => false,
				'timestamp'       => $timestamp,
			);
		}

		return $data;
	}

	/**
	 * Returns an array of paths for the CSS assets
	 * of the current post.
	 *
	 * @param  string $type         Gets the type theme/addon.
	 * @since 2.1.0
	 * @return array
	 */
	public function get_asset_info( $type ) {
		$css_suffix = 'astra-' . $type . '-dynamic-css';
		$info       = array();

		if ( ! isset( $this->uploads_dir['path'] ) || ! isset( $this->uploads_dir['url'] ) ) {
			return $info;
		}

		$info['path']    = $this->uploads_dir['path'] . $css_suffix . '-' . $this->asset_slug . '.css';
		$info['css_url'] = $this->uploads_dir['url'] . $css_suffix . '-' . $this->asset_slug . '.css';

		return $info;
	}

	/**
	 * Updates the Post Meta/ Option Timestamp.
	 *
	 * @param  string $timestamp    Gets the current timestamp.
	 * @since  2.1.0
	 * @return void
	 */
	public function update_timestamp( $timestamp ) {
		// Check if current page is a post/ archive page. false states that the current page is a post.
		if ( 'single' === $this->asset_query_var ) {
			update_post_meta( get_the_ID(), 'astra_style_timestamp_css', $timestamp );
		} else {
			update_option( 'astra_get_dynamic_css', $timestamp );
		}
	}

	/**
	 * Creates CSS files.
	 *
	 * @param  string $style_data   Gets the CSS for the current Page.
	 * @param  string $timestamp    Gets the current timestamp.
	 * @param  string $assets_info  Gets the assets path info.
	 * @since  2.1.0
	 * @return void
	 */
	public function file_write( $style_data, $timestamp, $assets_info ) {
		astra_addon_filesystem()->put_contents( $assets_info['path'], $style_data );
		$this->update_timestamp( $timestamp );
	}
}
