<?php
/**
 * Readabler
 * Web accessibility for Your WordPress site.
 * Exclusively on https://1.envato.market/readabler
 *
 * @encoding        UTF-8
 * @version         2.0.5
 * @copyright       (C) 2018-2025 Merkulove ( https://merkulov.design/ ). All rights reserved.
 * @license         Envato License https://1.envato.market/KYbje
 * @contributors    Dmytro Merkulov (dmitry@merkulov.design)
 * @support         help@merkulov.design
 **/

namespace Merkulove\Readabler\Unity;

/** Exit if accessed directly. */
if (!defined('ABSPATH')) {
    header('Status: 403 Forbidden');
    header('HTTP/1.1 403 Forbidden');
    exit;
}

use stdClass;
use WP_Error;

/**
 * PluginUpdaterV2 class used to control plugin updates.
 */
final class PluginUpdaterV2
{

    /**
     * The one true PluginUpdater.
     *
     * @since 1.0.0
     * @access private
     * @var PluginUpdaterV2
     **/
    private static $instance;

    private function __construct()
    {

        /** Disable if plugin don't have Envato ID. */
        if (!defined('READABLER') || !isset(READABLER['envato_id'])) {
            return;
        }

        // Check for updates
        add_filter('site_transient_update_plugins', [$this, 'update_plugins']);
        add_filter('transient_update_plugins', [$this, 'update_plugins']);

        /** Show a custom update message. */
	    add_action( 'in_plugin_update_message-readabler/readabler.php', [$this, 'show_plugin_update_message' ], 10, 2 );

        /** Add plugin info to View details popup.  */
        add_filter('plugins_api', [$this, 'plugin_info'], 20, 3);

    }

    /**
     * Check do we have a new version?
     * @param $update_plugins
     * @return mixed
     */
    public function update_plugins($update_plugins)
    {
        // Reset temporary cache
        $this->force_check();

        // Do nothing if this is not about getting plugin information.
        if (!is_object($update_plugins)) {
            return $update_plugins;
        }

        // Do nothing if it is not our plugin.
        if (!isset($update_plugins->response) || !is_array($update_plugins->response)) {
            $update_plugins->response = [];
        }

        /** This method runs multiple times, so we use short time cache.  */

        // Get plugin information from cache or remote.
        $plugin_info = $this->get_plugin_information();
        if (empty($plugin_info)) {
            return $update_plugins;
        }

        /** Show plugin update if the latest version is newer. */
        return $this->compare_with_new($plugin_info, $update_plugins);
    }

    /**
     * Show plugin update if the latest version is newer.
     * @param $plugin_info
     * @param $update_plugins
     * @return mixed
     */
    private function compare_with_new($plugin_info, $update_plugins)
    {
        global $wp_version;

        $latest_version = $plugin_info['version'];
        $package = $plugin_info['package'] ?? null;

        // If the latest version is newer, show an update version. */
        if (version_compare($latest_version, Plugin::get_version()) > 0) {

	        $update_plugins->response['readabler/readabler.php'] = (object)[
		        'slug' => 'readabler',
		        'plugin' => 'readabler/readabler.php',
                'new_version' => $latest_version, // The newest version
                'url' => $plugin_info['homepage'],
                'package' => $package,
                'requires' => Plugin::requires_wp(),
                'tested' => $wp_version,
                'requires_php' => Plugin::requires_php(),
                'icons' => [
                    '2x' => Plugin::get_url() . 'images/logo-color.svg',
                    '1x' => Plugin::get_url() . 'images/logo-color.svg',
                ],

            ];

        }

        return $update_plugins;
    }

    /**
     * Prepare plugin info.
     *
     * @param $res
     * @param $action
     * @param $args
     * @return bool|stdClass
     **@since 1.0.0
     * @access public
     *
     */
    public function plugin_info($res, $action, $args)
    {
        global $wp_version;

        // Do nothing if this is not about getting plugin information
        if ($action !== 'plugin_information') {
            return false;
        }

        // Do nothing if it is not our plugin
        if (Plugin::get_slug() !== $args->slug) {
            return false;
        }

        // Reset temporary cache
        $this->force_check();

        // Trying to get from cache first.
        $plugin_information = $this->get_plugin_information();

        if (!empty($plugin_information)) {

            $res = (object) $plugin_information;

            $res->tested = $wp_version; // Tested up to WordPress version.
            $res->requires = Plugin::requires_wp(); // Requires an at least WordPress version.
            $res->requires_php = Plugin::requires_php(); // Requires at least PHP version.

            return $res;

        }

        return false;

    }

    /**
     * Get plugin information
     * @return array
     */
    private function get_plugin_information()
    {
        // Get plugin information from transient.
        $transient_key = 'mdp_' . Plugin::get_slug() . '_plugin_info';

        // Trying to get from cache first.
        $transient_plugin_info = get_transient($transient_key);
        if ($transient_plugin_info) {
            return $transient_plugin_info;
        }

        // Fresh update links only on the Plugins List page and Updates Page
	    if (!is_admin() || !function_exists('get_current_screen')) {
			return [];
		}
        $screen = get_current_screen();
        $re_fetch = isset($screen->base) && in_array($screen->base, ['plugins', 'update-core']);

        // Get plugin information from remote server.
        if ($re_fetch) {
            $plugin_info = $this->get_remote_plugin_info();
            if ($plugin_info) {
                set_transient($transient_key, $plugin_info, count($plugin_info) > 0 ? DAY_IN_SECONDS : HOUR_IN_SECONDS);
            }
            return $plugin_info;
        } else {
            return [];
        }
    }

    /**
     * Reset temporary options on Force update check.
     * @return void
     */
    public function force_check(): void
    {
        /** Reset cache only once per session. */
        static $called = false;
        if ($called) {
            return;
        }
        $called = true;

        if (isset($_GET['force-check']) && $_GET['force-check'] === '1') {

            /** Clear plugin cache. */
            $cache = new Cache();
            $cache->drop_cache_table();

        }
    }

    /**
     * Show custom update messages on plugins page.
     *
     * @param $plugin_data - An array of plugin metadata.
     * @param $r - An array of metadata about the available plugin update.
     *
     * @return void
     **@since 1.0.0
     * @access public
     *
     */
    public function show_plugin_update_message($plugin_data, $r)
    {
        /** Message for non-activated users. */
        if (!Activator::status()) {
            echo '<br /><span class="mdp-line">&nbsp;</span>';
            esc_html_e('Your plugin license is ', 'readabler');
            echo '<strong style="color: #ff495c">';
            esc_html_e('not activated', 'readabler');
            echo '.</strong> ';
            esc_html_e('Please activate the license to receive automatic updates in one click, or visit the plugin page on the Envato market to ', 'readabler');
	        echo ' <a href="https://1.envato.market/cc-downloads" target="_blank">';
            esc_html_e('download ', 'readabler');
            echo '</a> ';
            esc_html_e('the latest version.', 'readabler');
        }

    }

    /**
     * Get plugin information from remote server.
     * @return array|mixed|WP_Error
     */
    private function get_remote_plugin_info()
    {
        $flag_key = 'mdp_' . Plugin::get_slug() . '_remote_plugin_info_flag';

        // Check a flag for remote request to get plugin info
        $flag = get_transient($flag_key);
        if (!$flag) {
            set_transient($flag_key, true, 60);
        } else {
            return [];
        }

        // Get plugin information from remote server.
        $response = $this->remote_plugin_request_v2();

        // Check for errors
        if (is_wp_error($response)) {
            add_action('admin_notices', function () use ($response) {
                foreach ($response->get_error_messages() as $error) {
                    echo '<div class="notice notice-error is-dismissible"><p>' . Plugin::get_name() . ' ' . esc_html__('can\'t get plugin info.', 'readabler') . ' ' . esc_html($error) . '</p></div>';
                }
            });
            return [];
        }

        // Check response status code
        if (wp_remote_retrieve_response_code($response) !== 200) {
            add_action('admin_notices', function () use ($response) {
                echo '<div class="notice notice-error is-dismissible"><p>' . Plugin::get_name() . ' ' . esc_html__('can\'t get plugin info.', 'readabler') . ' ' . esc_html__('Response code is: ', 'readabler') . wp_remote_retrieve_response_code($response) . '</p></div>';
            });
            return [];
        }

        // Decode the response
        $body = wp_remote_retrieve_body($response);
        $response_body = json_decode($body, true);
        if (!$response_body) {
            add_action('admin_notices', function () {
                echo '<div class="notice notice-error is-dismissible"><p>' . Plugin::get_name() . ' ' . esc_html__('can\'t get plugin info.', 'readabler') . ' ' . esc_html__('Response is not JSON.', 'readabler') . '</p></div>';
            });
            return [];
        }

        // Check if the response is successful
        if (!$response_body['success']) {
            add_action('admin_notices', function () use ($response_body) {
                echo '<div class="notice notice-error is-dismissible"><p>' . Plugin::get_name() . ' ' . esc_html__('can\'t get plugin info.', 'readabler') . ' ' . esc_html($response_body['message'] ?? '') . '</p></div>';
            });
            return [];
        }

        // Delete flag
        delete_transient($flag_key);

        return $response_body['data'] ?? [];
    }

    /**
     * Get plugin information from remote server.
     * @return array|WP_Error
     */
    private function remote_plugin_request_v2()
    {
        $route = '/api/v2/product_info';

        return wp_remote_post(
            EnvatoItem::$host . $route . '?t=' . time(),
            [
                'headers' => EnvatoItem::get_headers($route),
                'body' => [
                    'id' => READABLER['envato_id'] ?? '',
                    'plugin' => Plugin::get_slug(),
                    'version' => Plugin::get_version(),
                    'pid' => Plugin::get_purchase_code(),
                    'domain' => site_url(),
                ],
                'sslverify' => (Settings::get_instance()->options['check_ssl'] ?? 'off') === 'on'
            ]
        );
    }

    /**
     * Retrieve the singleton instance of the class.
     * If the instance does not exist, it will create and return a new instance of the class.
     * @return self The single instance of the class.
     */
    public static function get_instance()
    {
        if (!isset(self::$instance) && !(self::$instance instanceof self)) {
            self::$instance = new self;
        }
        return self::$instance;
    }

}
