<?php

class ESSB_Cached_Share_Counters
{

    /**
     * Returns the optimized storage configuration for share counters.
     *
     * This method provides an optimized way to store and retrieve share counter data,
     * potentially improving performance and reducing database load.
     *
     * @return bool The optimized storage configuration or data.
     */
    public static function optimized_storage()
    {
        return essb_options_bool_value('plugin_meta_counters');
    }

    /**
     * Increments the internal share counter for a specific post and social network.
     *
     * @param mixed|int $post_id The ID of the post for which to increment the share counter.
     * @param string $network Optional. The name of the social network. Default is an empty string.
     * @return int Returns the updated integer value of the counter.
     */
    public static function tick_internal_share_counter($post_id, $network = '')
    {
        $current_value = self::get_network_internal_counter($post_id, $network);
        $current_value = intval($current_value) + 1;

        self::update_network_internal_counter($post_id, $network, $current_value);

        if (self::network_with_internal_counter($network)) {
            self::save_single_network_post_counter($post_id, $network, $current_value);

            // Update total value if optimized storage
            if (self::optimized_storage()) {
                $total_value = essb_get_post_meta($post_id, 'share_total');
                $total_value = intval($total_value) + 1;
                essb_update_post_meta($post_id, 'share_total', $total_value);
            }
        }

        return $current_value;
    }

    /**
     * Retrieves the internal share counter for a specific social network and post.
     *
     * This method fetches the cached share count for a given post ID and social network.
     *
     * @param int    $post_id The ID of the post for which to retrieve the share count.
     * @param string $network Optional. The social network identifier (e.g., 'facebook', 'twitter').
     * @return int The internal share count for the specified network and post.
     */
    public static function get_network_internal_counter($post_id, $network = '')
    {
        $value = self::get_post_meta($post_id, 'essb_pc_' . $network);
        return intval($value);
    }

    /**
     * Updates the internal share counter for a specific social network on a given post.
     *
     * @param int    $post_id The ID of the post for which the share counter is being updated.
     * @param string $network The name of the social network (e.g., 'facebook', 'twitter').
     * @param int    $value   The new value to set for the share counter.
     */
    public static function update_network_internal_counter($post_id, $network, $value)
    {
        self::update_post_meta($post_id, 'essb_pc_' . $network, $value);
    }

    /**
     * Retrieves the name of a social network that uses an internal counter for share counts.
     *
     * @param string $network Optional. The name of the social network to check.
     * @return bool Returns the true if it uses an internal counter, or false otherwise.
     */
    public static function network_with_internal_counter($network = '')
    {
        $api_counters = array();
        $api_counters[] = 'facebook';

        if (essb_option_value('twitter_counters') != 'self') {
            $api_counters[] = 'twitter';
        }

        $api_counters[] = 'pinterest';
        $api_counters[] = 'vk';
        $api_counters[] = 'reddit';
        $api_counters[] = 'buffer';
        $api_counters[] = 'ok';
        $api_counters[] = 'xing';
        $api_counters[] = 'yummly';
        $api_counters[] = 'comments'; // added 7.0.4

        if (in_array($network, $api_counters)) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * Prepares a list of social networks that have share counters enabled.
     *
     * This static method filters and returns the list of networks that should display share counters,
     * based on the provided networks and the list of active networks.
     *
     * @param array $networks            An array of all available social networks.
     * @param array $active_networks_list An array of active social networks to be considered.
     *
     * @return array Returns an array of networks with share counters enabled.
     */
    public static function prepare_list_of_networks_with_counter($networks = array(), $active_networks_list = array())
    {
        $basic_network_list = 'twitter,linkedin,facebook,pinterest,google,stumbleupon,vk,reddit,buffer,love,ok,xing,mail,print,comments,yummly,tumblr';

        // updated in version 4.2 - now we have only avoid with counter networks
        $avoid_network_list = 'more,share,subscribe,copy,mwp';

        $internal_counters = essb_option_bool_value('active_internal_counters');
        $no_mail_print_counter = essb_option_bool_value('deactive_internal_counters_mail');
        $twitter_counter = essb_option_value('twitter_counters');

        if ($twitter_counter == '') {
            $twitter_counter = 'api';
        }

        $linkedin_counter_type = essb_option_value('linkedin_counter_type');

        $basic_array = explode(',', $basic_network_list);
        $avoid_array = explode(',', $avoid_network_list);    

        $count_networks = array();

        foreach ($networks as $k) {

            if (!in_array($k, $active_networks_list)) {
                continue;
            }

            if ($k == 'linkedin' && $linkedin_counter_type == 'no') {
                continue;
            }


            if ($k == 'twitter' && $twitter_counter == 'no') {
                continue;
            }

            if (in_array($k, $basic_array)) {
                if ($k == 'print' || $k == 'mail') {
                    if (!$no_mail_print_counter) {
                        $count_networks[] = $k;
                    }
                } else {
                    $count_networks[] = $k;
                }
            }

            if (!in_array($k, $basic_array) && $internal_counters && !in_array($k, $avoid_array)) {
                $custom_network_avoid = false;
                $custom_network_avoid = apply_filters("essb4_no_counter_for_{$k}", $custom_network_avoid);
                if (!$custom_network_avoid) {
                    $count_networks[] = $k;
                }
            }
        }


        return $count_networks;
    }

    /**
     * Retrieves the meta value for a given post and meta key.
     *
     * @param int    $post_id The ID of the post from which to retrieve the meta value.
     * @param string $key The meta key to retrieve. Default is an empty string.
     *
     * @return mixed The value of the specified post meta field, or false if not found.
     */
    public static function get_post_meta($post_id, $key = '')
    {
        $value = '';
        if (self::optimized_storage()) {
            $value = essb_get_post_meta($post_id, $key);
        } else {
            $value = get_post_meta($post_id, $key, true);
        }

        return $value;
    }

    public static function update_post_meta($post_id, $meta_key, $meta_value)
    {
        if (self::optimized_storage()) {
            essb_update_post_meta($post_id, $meta_key, $meta_value);
        } else {
            update_post_meta($post_id, $meta_key, $meta_value);
        }
    }

    public static function is_fresh_cache($post_id)
    {
        $is_fresh = true;

        // Bail early if it's a crawl bot. If so, ONLY SERVE CACHED RESULTS FOR MAXIMUM SPEED.
        if (isset($_SERVER['HTTP_USER_AGENT']) && preg_match('/bot|crawl|slurp|spider/i',  wp_unslash($_SERVER['HTTP_USER_AGENT']))) {
            $is_fresh = true;
        } else {
            $expire_time = self::get_post_meta($post_id, 'essb_cache_expire');
            $now = time();

            $is_alive = ($expire_time > $now);

            if (true == $is_alive) {
                $is_fresh = true;
            } else {
                $is_fresh = false;
            }
        }

        // avoid update of share counters when we do not browse on single post
        if (! is_singular()) {
            $is_fresh = true;
        }

        // additional check if the mixed loading is set to Yes
        if (essb_option_bool_value('cache_counter_narrow') && !$is_fresh) {
            $updated_time = self::get_post_meta($post_id, 'essb_cache_updated');

            // if we does not have update time it is useless to do any further checks because
            // expiration will not be properly set
            if ($updated_time != '') {
                $update = essb_option_value('counter_mode');
                if ($expire_time == '') {
                    $update = 60;
                }

                $update = (intval($update) * 2 * 60) + intval($updated_time);

                if ($update > $now) {
                    $is_fresh = true;
                }
            }
        }

        $user_call_refresh = isset($_REQUEST['essb_counter_update']) ? $_REQUEST['essb_counter_update'] : '';
        if ($user_call_refresh == 'true') {
            $is_fresh = false;
        }

        if (essb_internal_cache_get('updatedcounter-' . $post_id) == 'true') {
            $is_fresh = true;
        }

        /** 
         * @since 8.0 don't update share counters when in preview
         */
        if (is_preview()) {
            $is_fresh = true;
        }


        return $is_fresh;
    }

    public static function all_socaial_networks()
    {
        return ESSB_Share_Networks_Helper::get_networks_as_list();
    }

    /**
     * Reads the stored share counters for a given post and specified social networks.
     *
     * @param int   $post_id  The ID of the post for which to retrieve share counters.
     * @param array $networks Optional. An array of social network identifiers to filter the counters. Default is an empty array (all networks).
     *
     * @return array An associative array of share counters for the specified post and networks.
     */
    public static function read_post_counters($post_id, $networks = array())
    {
        $cached_counters = array('total' => 0);

        if (self::optimized_storage()) {
            $stored_counters = essb_get_post_meta($post_id, 'share_counters');
            if (!is_array($stored_counters)) {
                $stored_counters = array();
            }

            foreach ($networks as $k) {
                $cached_counters[$k] = isset($stored_counters[$k]) ? $stored_counters[$k] : 0;
                $cached_counters['total'] += intval($cached_counters[$k]);
            }
        } else {
            foreach ($networks as $k) {
                $cached_counters[$k] = get_post_meta($post_id, 'essb_c_' . $k, true);
                $cached_counters['total'] += intval($cached_counters[$k]);
            }
        }

        return $cached_counters;
    }

    /**
     * Reads all social share counters for a given post from the database.
     *
     * @param int $post_id The ID of the post to retrieve share counters for.
     * @return array An associative array of social share networks and their corresponding share values.
     */
    public static function read_all_post_counters($post_id)
    {
        $r = array();

        if (self::optimized_storage()) {
            $r = essb_get_post_meta($post_id, 'share_counters');
        } else {
            $networks = self::all_socaial_networks();

            foreach ($networks as $k) {
                $r[$k] =  get_post_meta($post_id, 'essb_c_' . $k, true);
            }
        }

        return $r;
    }

    public static function get_single_network_post_counter($post_id, $network)
    {
        $value = 0;

        if (self::optimized_storage()) {
            $stored_counters = essb_get_post_meta($post_id, 'share_counters');
            if (!is_array($stored_counters)) {
                $stored_counters = array();
            }

            $value = isset($stored_counters[$network]) ? $stored_counters[$network] : 0;
        } else {
            $value = get_post_meta($post_id, 'essb_c_' . $network, true);
        }

        return intval($value);
    }

    public static function save_single_network_post_counter($post_id, $network, $value)
    {
        if (self::optimized_storage()) {
            $stored_counters = essb_get_post_meta($post_id, 'share_counters');
            if (!is_array($stored_counters)) {
                $stored_counters = array();
            }

            $stored_counters[$network] = $value;

            essb_update_post_meta($post_id, 'share_counters', $stored_counters);
        } else {
            update_post_meta($post_id, 'essb_c_' . $network, $value);
        }
    }

    /**
     * Saves the share counters for a specific post.
     *
     * @param int   $post_id         The ID of the post for which to save share counters.
     * @param array $networks        The list of sharing networks being parsed for counters.
     * @param array $cached_counters The current updated and read counters from the database.
     *
     * @return array
     */
    public static function save_post_counters($post_id, $networks = array(), $cached_counters = array())
    {
        $calculated_total = 0;

        if (self::optimized_storage()) {
            $stored_counters = essb_get_post_meta($post_id, 'share_counters');
            if (!is_array($stored_counters)) {
                $stored_counters = array();
            }

            $has_change = false;

            foreach ($networks as $k) {
                if ($k == 'total') {
                    continue;
                }

                $saved_value = intval(isset($stored_counters[$k]) ? $stored_counters[$k] : 0);
                $new_value = intval(isset($cached_counters[$k]) ? $cached_counters[$k] : 0);

                if ($new_value > -1) {
                    if (!essb_option_bool_value('cache_counter_force')) {
                        if ($saved_value < $new_value) {
                            $stored_counters[$k] = $new_value;
                            $has_change = true;
                            $calculated_total += $new_value;
                        } else {
                            $cached_counters[$k] = $saved_value;
                            $calculated_total += $saved_value;
                        }
                    } else {
                        $stored_counters[$k] = $new_value;
                        $has_change = true;
                        $calculated_total += $new_value;
                    }
                } else {
                    $cached_counters[$k] = $saved_value;
                    $calculated_total += $saved_value;
                }
            }

            if ($has_change) {
                essb_update_post_meta($post_id, 'share_counters', $cached_counters);
                essb_update_post_meta($post_id, 'share_total', $calculated_total);
                update_post_meta($post_id, 'essb_c_total', $calculated_total);
            }
        } else {
            $has_change = false;
            foreach ($networks as $k) {
                if ($k == 'total') {
                    continue;
                }

                $single = isset($cached_counters[$k]) ? $cached_counters[$k] : '0';
                if (intval($single) > -1) {
                    //cache_counter_force
                    $past_shares = intval(get_post_meta($post_id, 'essb_c_' . $k, true));

                    // since version 5 we prevent save of lower than past saved shares
                    if (!essb_option_bool_value('cache_counter_force')) {
                        if (intval($past_shares) < intval($single)) {
                            update_post_meta($post_id, 'essb_c_' . $k, $single);
                            $calculated_total += intval($single);
                            $has_change = true;
                        } else {
                            $cached_counters[$k] = $past_shares;
                            $calculated_total += intval($past_shares);
                        }
                    } else {
                        update_post_meta($post_id, 'essb_c_' . $k, $single);
                        $calculated_total += intval($single);
                        $has_change = true;
                    }
                } else {
                    $cached_counters[$k] =  intval(get_post_meta($post_id, 'essb_c_' . $k, true));
                    $calculated_total += intval($cached_counters[$k]);
                }
            }

            if ($has_change) {
                update_post_meta($post_id, 'essb_c_total', $calculated_total);
            }
        }

        $cached_counters['total'] = $calculated_total;
        return $cached_counters;
    }

    /**
     * Returns the number of shares for selected post by ID
     * @updated 8.0.2 PHP 8
     * 
     * @param int $post_id
     * @param array $share
     * @param array $networks
     * @return array
     */
    public static function get_counters($post_id, $share = array(), $networks = array())
    {

        $cached_counters = array();
        $cached_counters['total'] = 0;

        // since 4.2 we give option to display each time total counter based on all
        // social networks
        if (essb_option_bool_value('total_counter_all')) {
            $networks = self::all_socaial_networks();
        }

        // @since 6.0 we support importing of AddThis local share counters
        if (essb_option_bool_value('cache_counter_addthis')) {
            $networks[] = 'addthis';
        }

        /**
         * @since 8.0 read previously stored MashShare values
         */
        if (essb_option_bool_value('activate_ms_fbcount_bridge')) {
            if (!function_exists('essb_mashshare_integrate_facebook_counter')) {
                include_once(ESSB3_HELPERS_PATH . 'share-counters/helpers-mashshare-facebook.php');
            }
        }

        if (!self::is_fresh_cache($post_id)) {
            $cached_counters = self::update_counters($post_id, $share['url'], $share['full_url'], $networks);


            if (defined('ESSB3_SHARED_COUNTER_RECOVERY')) {

                $recovery_till_date = essb_option_value('counter_recover_date');
                $is_applying_for_recovery = true;

                // @since 3.4 - apply recovery till provided date only
                if (!empty($recovery_till_date)) {
                    $is_applying_for_recovery = essb_recovery_is_matching_recovery_date($post_id, $recovery_till_date);
                }

                if ($is_applying_for_recovery) {
                    $current_url = $share['full_url'];
                    $post_essb_activate_sharerecovery = get_post_meta($post_id, 'essb_activate_sharerecovery', true);
                    if (!empty($post_essb_activate_sharerecovery)) {
                        $current_url = $post_essb_activate_sharerecovery;
                    } else {
                        $current_url = essb_recovery_get_alt_permalink($current_url, $post_id);
                    }

                    // to avoid duplicating same data we will execute update call only if those 2 URLs are different in some form
                    if ($share['full_url'] != $current_url) {
                        $recovery_counters = self::update_counters($post_id, $current_url, $current_url, $networks, true);
                        $cached_counters = essb_recovery_consolidate_results($cached_counters, $recovery_counters, $networks);
                    }
                }
            }

            // mutli-step share counter recovery option - since 5.2
            if (function_exists('essb_advnaced_recovery_url_list')) {

                // include the required for share recovery functions if does not exists
                if (!function_exists('essb_recovery_consolidate_results')) {
                    include_once(ESSB3_PLUGIN_ROOT . 'lib/core/share-counters/essb-sharecounter-recovery.php');
                }

                $alternative_url_list = essb_advnaced_recovery_url_list($share['full_url'], $post_id);

                foreach ($alternative_url_list as $current_url) {
                    if ($share['full_url'] != $current_url) {
                        $recovery_counters = self::update_counters($post_id, $current_url, $current_url, $networks, true);
                        $cached_counters = essb_recovery_consolidate_results($cached_counters, $recovery_counters, $networks);
                    }
                }
            }

            $cached_counters = self::save_post_counters($post_id, $networks, $cached_counters);


            if (defined('ESSB3_ESML_ACTIVE') && function_exists('essb_sm_store_data')) {
                essb_sm_store_data($post_id, $cached_counters);
            }
        } else {
            $cached_counters = self::read_post_counters(intval($post_id), $networks);
        }


        if (essb_option_bool_value('homepage_total_allposts') && has_filter('essb_homepage_get_cached_counters')) {
            $cached_counters = apply_filters('essb_homepage_get_cached_counters', $cached_counters);
            self::rebuild_totals($cached_counters, $networks);
        }

        /**
         * @since 8.0 Additional share counter integration filter
         */
        if (has_filter('essb_get_post_cached_counters')) {
            $cached_counters = apply_filters('essb_get_post_cached_counters', $post_id, $cached_counters);
            self::rebuild_totals($cached_counters, $networks);
        }

        if (has_filter('essb4_get_cached_counters')) {
            $cached_counters = apply_filters('essb4_get_cached_counters', $cached_counters);
            self::rebuild_totals($cached_counters, $networks);
        }

        return $cached_counters;
    }

    /**
     * Recalculate the total share values
     * 
     * @param array $cached_counters
     * @param array $networks
     */
    public static function rebuild_totals($cached_counters = array(), $networks = array())
    {
        $cached_counters['total'] = 0;

        foreach ($networks as $k) {
            $cached_counters['total'] += isset($cached_counters[$k]) ? intval($cached_counters[$k]) : 0;
        }

        return $cached_counters;
    }

    public static function update_counters($post_id, $url, $full_url, $networks = array(), $recover_mode = false)
    {

        $twitter_counter = essb_options_value('twitter_counters');

        // changed in 4.2 to use internal counter when nothing is selected
        if ($twitter_counter == '') {
            $twitter_counter = 'self';
        }

        $async_update_mode = essb_option_bool_value('cache_counter_refresh_async');

        if (essb_option_bool_value('counter_remove_query_string')) {
            $url = preg_replace('/\?.*/', '', $url);
            $full_url = preg_replace('/\?.*/', '', $full_url);
        }

        if (!$async_update_mode) {
            essb_depend_load_function('essb_counter_request', 'lib/core/share-counters/essb-counter-update.php');
            $cached_counters = essb_counter_update_simple($post_id, $url, $full_url, $networks, $recover_mode, $twitter_counter);
        } else {
            essb_depend_load_class('ESSBAsyncShareCounters', 'lib/core/share-counters/essb-counter-update-async.php');
            $counter_parser = new ESSBAsyncShareCounters($post_id, $url, $full_url, $networks, $recover_mode, $twitter_counter);
            $cached_counters = $counter_parser->get_counters();
        }

        if (!$recover_mode) {
            $expire_time = essb_option_value('counter_mode');
            if ($expire_time == '') {
                $expire_time = 60;
            }

            $default_expire_time = $expire_time;

            // @since version 5.0 - support for progressive counter update mode
            if (essb_option_bool_value('cache_counter_increase')) {
                $post_age = floor(date('U') - get_post_time('U', false, $post_id));
                $post_age = $post_age / 86400;

                if (intval($post_age) >= 7 && intval($post_age) < 14) {
                    $expire_time = intval($expire_time) * 2;
                }

                if (intval($post_age) >= 14 && intval($post_age) < 21) {
                    $expire_time = intval($expire_time) * 3;
                }

                if (intval($post_age) >= 21 && intval($post_age) < 30) {
                    $expire_time = intval($expire_time) * 4;
                }

                if (intval($post_age) >= 30) {
                    $expire_time = intval($expire_time) * 5;
                }

                if (has_filter('essb_get_advanced_counter_expiration')) {
                    $opts = array('post_age' => $post_age, 'expire' => $default_expire_time);
                    $expire_time = apply_filters('essb_get_advanced_counter_expiration', $opts);
                }
            }

            self::update_post_meta($post_id, 'essb_cache_expire', (time() + ($expire_time * 60)));

            // keep a track of last share counter update period if we will shuffle the update periods
            if (essb_option_bool_value('cache_counter_narrow')) {
                self::update_post_meta($post_id, 'essb_cache_updated', time());
            }

            essb_internal_cache_set('updatedcounter-' . $post_id, 'true');
        }

        return $cached_counters;
    }

    public static function clear_cache_expire()
    {
        delete_post_meta_by_key('essb_cache_expire');
        essb_delete_post_meta_by_key('essb_cache_expire');

        delete_post_meta_by_key('essb_cache_updated');
        essb_delete_post_meta_by_key('essb_cache_updated');
    }

    public static function clear_single_post_cache_expire($post_id = 0)
    {
        delete_post_meta($post_id, 'essb_cache_expire');
        essb_delete_post_meta($post_id, 'essb_cache_expire');

        delete_post_meta($post_id, 'essb_cache_updated');
        essb_delete_post_meta($post_id, 'essb_cache_updated');
    }

    public static function clear_interal_counters()
    {
        $networks = essb_available_social_networks(true);
        foreach ($networks as $key => $data) {
            delete_post_meta_by_key('essb_pc_' . $key);
        }

        essb_delete_post_meta_by_matching_keys('essb_pc_');
    }

    public static function clear_single_internal_counters($post_id = 0)
    {
        $networks = essb_available_social_networks(true);
        foreach ($networks as $key => $data) {
            delete_post_meta($post_id, 'essb_pc_' . $key);
        }

        essb_delete_post_meta_by_matching_keys('essb_pc_', $post_id);
    }

    public static function clear_share_counters()
    {
        $networks = essb_available_social_networks(true);
        foreach ($networks as $key => $data) {
            delete_post_meta_by_key('essb_c_' . $key);
        }

        delete_post_meta_by_key('essb_c_total');

        essb_delete_post_meta_by_key('share_counters');
        essb_delete_post_meta_by_key('share_total');
    }

    public static function clear_single_post_share_counters($post_id = 0)
    {
        $networks = essb_available_social_networks(true);
        foreach ($networks as $key => $data) {
            delete_post_meta($post_id, 'essb_c_' . $key);
        }

        delete_post_meta($post_id, 'essb_c_total');

        essb_delete_post_meta($post_id, 'share_counters');
        essb_delete_post_meta($post_id, 'share_total');
    }
}
