<?php

class PeepSoConfigSectionNavigation extends PeepSoConfigSectionAbstract
{
    // Builds the groups array
    public function register_config_groups()
    {
        // Primary navigation + login/logout live in the LEFT column; the #8037 content-ranking
        // weights sit on their own in the RIGHT column.
        $this->set_context('left');
        $this->navigation();
        $this->login_logout();

        // #8037 per-family ranking weights — only meaningful when the ranking engine is present.
        if (class_exists('PeepSoActivityRankingEngine')) {
            $this->set_context('right');
            $this->content_ranking();
        }
    }

    private function navigation() {

        delete_post_meta_by_key('peepso_shortcode');

        $shortcodes = PeepSo::get_instance()->all_shortcodes();

        foreach($shortcodes as $sc => $method) {

            $options=array();
            $description='';

            if(is_callable($method)) {
                $method=explode('::', $method);
                $method = $method[0].'::description';
            } else {
                $method .='::description';
            }

            $post_state_method = explode('::',$method);
            $post_state_method = $post_state_method[0].='::post_state';

            if(is_callable($method)) {
                $this->args('descript', call_user_func($method));
            }


            if(is_callable($post_state_method)) {
                $post_state = call_user_func($post_state_method);
            } else {
                $post_state = $sc;
            }

            $page = str_ireplace('peepso_','', $sc);
            $page_key = 'page_'.$page;

            $pages = PeepSo::get_instance()->pages_with_shortcode($sc);

            foreach($pages as $key => $value) {
                $options[$key] = "{$value['label']}";

                if($key == PeepSo::get_option($page_key)) {
                    add_post_meta($value['id'], 'peepso_shortcode', $post_state, 1);
                }
            }

            $error = PeepSo::get_instance()->check_shortcode($sc, $options);
            $error = strip_tags($error,'<b><br>');

            $label = ''
                . str_ireplace(_x('PeepSo', 'Page listing', 'peepso-core'). ' - ','',$post_state)
                . '<a name="' . $sc . '" href="' . PeepSo::get_page($page) . '" target="_blank"> '
                .   '<i  class="fa fa-external-link"></i>'
                . '</a>'
                . '<br/><small style="color:#aaaaaa">[' . $sc . ']</small>';

            if(strlen($error)) {

                $options = array_merge(array($page_key=> __('Error: page not found!','peepso-core')), $options);

                $label = "<span style=color:red>$label<br/><small>$error</small></span>";
            }

            $this->args('options', $options);

            $this->set_field(
                $page_key,
                $label ,
                'select'
            );


            if('peepso_activity' == $sc) {

                // Default stream type
                $stream_id_list = apply_filters( 'peepso_stream_id_list', array() );

                if ( count( $stream_id_list ) > 1 ) {

                    if(isset($stream_id_list['core_scheduled'])) {
                        unset($stream_id_list['core_scheduled']);
                    }

                    if(isset($stream_id_list['core_saved'])) {
                        unset($stream_id_list['core_saved']);
                    }

                    $desc    = array();
                    $options = array();

                    foreach ( $stream_id_list as $stream_id => $stream_meta ) {
                        $options[ $stream_id ] = $stream_meta['label'];
                        $desc[]                = $stream_meta['label'] . ': ' . $stream_meta['desc'];
                    }

                    $this->args( 'descript', implode( '<br/>', $desc ) );

                    $this->args( 'options', $options );
                    $this->set_field(
                        'stream_id_default',
                        __( 'Default stream filter', 'peepso-core' ),
                        'select'
                    );
                }

                // Default stream sort — #8037: the two-level family/variant sorts are flattened
                // into one "Family — Variant" select (the config select can't do optgroups). The
                // value stays the leaf key; the frontend reconciles it back to family + period.
                // Labels/descs are reused from the filters, never re-declared here.
                $sort_posts_list = apply_filters( 'peepso_stream_sort_list', [] );
                $sort_families   = apply_filters( 'peepso_stream_sort_families', [] );

                // #8117 Mirror the frontend template: a leaf with no 'group' is its own family.
                // With a single family the "Family — " option prefix and the per-family help text
                // say nothing, so drop both and match the collapsed picker users actually see.
                $sort_groups = array();
                foreach ( $sort_posts_list as $sort_key => $sort_meta ) {
                    $g = ( isset($sort_meta['group']) && strlen($sort_meta['group']) ) ? $sort_meta['group'] : $sort_key;
                    $sort_groups[ $g ] = TRUE;
                }
                $solo_family = ( count($sort_groups) <= 1 );

                if ( count($sort_posts_list) > 1 ) {

                    $options = array();

                    foreach ( $sort_posts_list as $sort_key => $sort_meta ) {
                        $group = ( isset($sort_meta['group']) && strlen($sort_meta['group']) ) ? $sort_meta['group'] : '';
                        if ( !$solo_family && '' !== $group && isset($sort_families[ $group ]) ) {
                            $options[ $sort_key ] = $sort_families[ $group ]['label'] . ' — ' . $sort_meta['label'];
                        } else {
                            $options[ $sort_key ] = $sort_meta['label'];
                        }
                    }

                    // #8117 If the stored default now belongs to a hidden family it is no longer in
                    // $options, so no <option> is marked selected: the browser shows the first entry
                    // while the DB still holds the old key, and the next save of this tab silently
                    // rewrites it. Surface it instead of losing it.
                    $sort_stored = PeepSo::get_option_new('stream_sort_default');
                    if ( '' !== (string) $sort_stored && !isset($options[ $sort_stored ]) ) {
                        $options = array( $sort_stored => sprintf( __('%s (hidden from users)', 'peepso-core'), $sort_stored ) ) + $options;
                    }

                    // Help text: one line per family (skip empties), reusing the family descriptions.
                    // #8117 Pointless with a single family — it would read "Recent: Latest activity".
                    $desc = array();
                    if ( !$solo_family ) {
                        foreach ( $sort_families as $family ) {
                            if ( isset($family['desc']) && strlen($family['desc']) ) {
                                $desc[] = $family['label'] . ': ' . $family['desc'];
                            }
                        }
                    }

                    $this->args( 'descript', implode( '<br/>', $desc ) );

                    $this->args( 'options', $options );
                    $this->set_field(
                        'stream_sort_default',
                        __( 'Default stream order', 'peepso-core' ),
                        'select'
                    );
                }

                // #8051 Moved here from the Postbox tab. order_post_asc only flips the direction of
                // the "Recent" chronological sort, so it belongs beside "Default stream order"; the
                // "Top" / "Hot" ranking sorts always order by score and ignore it. Registered outside
                // the sort-list gate above so it still shows when ranking sorts are unavailable.
                $this->args( 'default', 0 );
                $this->args( 'descript', __( 'Applies only to the "Recent" stream sort. Enabled: posts are ordered oldest-first, so the newest post appears at the bottom. The "Top" and "Hot" rankings always sort by score and ignore this setting.', 'peepso-core' ) );
                $this->set_field(
                    'order_post_asc',
                    __( 'Recent sort: show oldest first', 'peepso-core' ),
                    'yesno_switch'
                );

                $options = [
                    0 => __('Disable', 'peepso-core'),
                    1 => __('Mobile', 'peepso-core'),
                    2 => __('Desktop', 'peepso-core'),
                    3 => __('Always', 'peepso-core'),
                ];

                $this->args( 'options', $options );
                $this->set_field(
                    'stream_filters_compact',
                    __( 'Compact stream filters', 'peepso-core' ),
                    'select'
                );

                // # Sticky stream ID
                $this->args( 'default', 0 );
                $this->set_field(
                    'stream_id_sticky',
                    __( 'Remember the last selected stream filter', 'peepso-core' ),
                    'yesno_switch'
                );

                $this->set_field(
                    'extra_activity_items',
                    __('Extra menu items can be enabled if you set the Default filter to "Community" and disable "Remember last filter"','peepso-core'),
                    'message'
                );

                $this->set_field(
                    'peepso_navigation_following',
                    'Show "Following"',
                    'yesno_switch'
                );

                if(PeepSo::get_option_new('post_save_enable')) {
                    $this->set_field(
                        'peepso_navigation_saved',
                        'Show "Saved"',
                        'yesno_switch'
                    );
                }

                $this->set_field(
                    'peepso_navigation_separator',
                    '',
                    'separator'
                );
            }
        }

        // Build Group
        $this->set_group(
            'profiles',
            __('Primary Navigation', 'peepso-core')
        );
    }

    /**
     * #8037 "Content ranking" group: per-family engagement weights for the Top and Hot
     * stream sorts. TOP = "top voted" (reactions outweigh comments); HOT = "most discussed"
     * (comments outweigh reactions — Hot's read-time decay already favours recency). Six
     * admin-tunable weights (0-10), read by PeepSoActivityRankingEngine::weights_top() /
     * weights_hot(). Changing any weight bumps the weights-generation pointer, which lazily
     * marks every already-scored post for re-scoring by the cron (rank_rev < current) — so
     * the new order rolls in over the next while rather than in one blocking pass.
     */
    private function content_ranking() {

        // Simple per-signal labels — the Top / Hot header above each block gives the context.
        $label_comment = __('Comments', 'peepso-core');
        $label_react   = __('Reactions', 'peepso-core');
        $label_repost  = __('Repost', 'peepso-core');

        // ---- Top family (reactions should outweigh comments) --------------------------
        $this->set_field('ranking_header_top', __('Top', 'peepso-core'), 'separator');
        $this->set_field('ranking_top_desc', __('The "Top" ranking counts engagement within the selected time window and does not fade with time. For example, if one post gathered a lot of engagement earlier in the window and another has less engagement today, the post with more total engagement ranks higher.', 'peepso-core'), 'message');

        $this->args('options', $this->_weight_scale(4));
        $this->args('default', 4);
        $this->set_field('ranking_weight_top_comment', $label_comment, 'select');

        $this->args('options', $this->_weight_scale(8));
        $this->args('default', 8);
        $this->set_field('ranking_weight_top_react', $label_react, 'select');

        $this->args('options', $this->_weight_scale(2));
        $this->args('default', 2);
        $this->set_field('ranking_weight_top_repost', $label_repost, 'select');

        // ---- Hot family (comments should outweigh reactions) --------------------------
        $this->set_field('ranking_header_hot', __('Hot', 'peepso-core'), 'separator');
        $this->set_field('ranking_hot_desc', __('The "Hot" ranking weights engagement by how recent it is and fades as it ages, representing content that is trending right now. For example, given an older post with more total engagement and a fresher one with less, the more recently engaged post ranks higher.', 'peepso-core'), 'message');

        $this->args('options', $this->_weight_scale(8));
        $this->args('default', 8);
        $this->set_field('ranking_weight_hot_comment', $label_comment, 'select');

        $this->args('options', $this->_weight_scale(4));
        $this->args('default', 4);
        $this->set_field('ranking_weight_hot_react', $label_react, 'select');

        $this->args('options', $this->_weight_scale(2));
        $this->args('default', 2);
        $this->set_field('ranking_weight_hot_repost', $label_repost, 'select');

        // Live "posts waiting re-count" gauge, appended right under the group description.
        // The markup rides in the (wp_kses_post'd) description; the polling JS is printed in the
        // admin footer (see _recount_indicator_script). Weight changes / new posts are re-scored
        // by the maintenance cron, so this shows the drain progress.
        $desc = __('By default, Top ranks higher for posts with more reactions, while Hot ranks higher for posts with more comments. You can adjust the weights to change the relative impact of each action. Your changes will be reflected over time, as the maintenance cron recalculates all posts.', 'peepso-core');
        $desc .= $this->_recount_indicator_markup();
        add_action('admin_print_footer_scripts', array($this, '_recount_indicator_script'));

        $this->set_group(
            'content_ranking',
            __('Content ranking', 'peepso-core'),
            $desc
        );
    }

    /**
     * 0-10 weight scale for a Content-ranking select, with the shipped default value marked "N *".
     * @param  int $default the default weight to flag
     * @return array value => label
     */
    private function _weight_scale($default) {
        $default = intval($default);
        $opts = array();
        for ($i = 1; $i <= 10; $i++) {
            $opts[$i] = "x ". (($i === $default) ? ($i . ' (').__('default','peepso-core').')' : (string) $i);
        }
        return $opts;
    }

    /**
     * #8037 "posts waiting re-count" gauge markup (text line + progress bar). Built entirely from
     * block-display <span>s so it survives wp_kses_post AND stays valid inside the description <p>.
     * The JS (_recount_indicator_script) fills it in and polls every 15s.
     * @return string
     */
    private function _recount_indicator_markup() {
        return '<span id="pso-recount" style="display:block;margin-top:12px;font-size:12px;color:#50575e">'
             . '<span id="pso-recount-text">' . esc_html__('Checking re-count status…', 'peepso-core') . '</span>'
             . '<span style="display:block;height:8px;margin-top:5px;width:100%;background:#dcdcde;border-radius:4px;overflow:hidden">'
             . '<span id="pso-recount-bar" style="display:block;height:100%;width:0;background:#46b450;transition:width .4s ease"></span>'
             . '</span></span>';
    }

    /**
     * #8037 admin-footer JS for the re-count gauge: POSTs to the peepso_ranking_pending ajax action
     * and updates the text + bar, immediately and then every 15s. No-ops if the gauge is absent, so
     * it is safe even though admin_print_footer_scripts fires on the whole page.
     */
    public function _recount_indicator_script() {
        $nonce = wp_create_nonce('peepso_ranking_pending');
        // Singular/plural forms of the pending label — the live count comes from ajax, so both
        // forms are handed to JS (via _n at 1 and 2) and JS picks by count. The "N%" is appended
        // in JS with a single literal '%'.
        $t_one  = _n('%s post pending', '%s posts pending', 1, 'peepso-core');
        $t_many = _n('%s post pending', '%s posts pending', 2, 'peepso-core');
        $t_done = __('All posts up to date', 'peepso-core');
        $t_err  = __('Re-count status unavailable', 'peepso-core');
        ?>
<script>
(function(){
	var box = document.getElementById('pso-recount');
	if (!box || typeof ajaxurl === 'undefined') { return; }
	var text = document.getElementById('pso-recount-text'),
		bar  = document.getElementById('pso-recount-bar'),
		nonce = <?php echo json_encode($nonce); ?>;
	function tick(){
		var xhr = new XMLHttpRequest();
		xhr.open('POST', ajaxurl, true);
		xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
		xhr.onreadystatechange = function(){
			if (xhr.readyState !== 4) { return; }
			try {
				var r = JSON.parse(xhr.responseText);
				if (!r || !r.success) { text.textContent = <?php echo json_encode($t_err); ?>; return; }
				var total = r.data.total|0, pending = r.data.pending|0, done = total - pending,
					pct = total > 0 ? Math.round(done / total * 100) : 100;
				bar.style.width = pct + '%';
				bar.style.background = pending > 0 ? '#dba617' : '#46b450';
				if (pending > 0) {
					var tpl = (pending === 1) ? <?php echo json_encode($t_one); ?> : <?php echo json_encode($t_many); ?>;
					text.textContent = tpl.replace('%s', pending.toLocaleString()) + ' · ' + pct + '%';
				} else {
					text.textContent = <?php echo json_encode($t_done); ?>;
				}
			} catch(e){ text.textContent = <?php echo json_encode($t_err); ?>; }
		};
		xhr.send('action=peepso_ranking_pending&nonce=' + encodeURIComponent(nonce));
	}
	tick();
	setInterval(tick, 15000);
})();
</script>
		<?php
    }

    public function login_logout() {
        // # Redirect Successful Logins
        $args = array(
            'sort_order' => 'asc',
            'sort_column' => 'post_title',
            'hierarchical' => 1,
            'exclude' => '',
            'include' => '',
            'meta_key' => '',
            'meta_value' => '',
            'authors' => '',
            'child_of' => 0,
            'parent' => -1,
            'exclude_tree' => '',
            'number' => '',
            'offset' => 0,
            'post_type' => 'page',
            'post_status' => 'publish'
        );
        $pages = get_pages($args);
        $options = array(
            -1 => __('Front Page', 'peepso-core'),
            0 => __('No redirect', 'peepso-core'),
        );

        $pageredirect = PeepSo::get_option('site_frontpage_redirectlogin');
        $settings = PeepSoConfigSettings::get_instance();
        foreach ($pages as $page) {
            // handling selected old value (activity/profile)
            if($page->post_name === $pageredirect) {
                //$this->args('default', $page->ID);
                // update option to selected ID
                $settings->set_option('site_frontpage_redirectlogin', $page->ID);
            }

            $options[$page->ID] = __('Page:','peepso-core') . ' ' . ($page->post_parent > 0 ? '&nbsp;&nbsp;' : '') . $page->post_title;
        }

        $this->args('options', $options);

        $this->set_field(
            'site_frontpage_redirectlogin',
            __('Log-in redirect', 'peepso-core'),
            'select'
        );


        // # Redirect Logout

        $args = array(
            'sort_order' => 'asc',
            'sort_column' => 'post_title',
            'hierarchical' => 1,
            'exclude' => '',
            'include' => '',
            'meta_key' => '',
            'meta_value' => '',
            'authors' => '',
            'child_of' => 0,
            'parent' => -1,
            'exclude_tree' => '',
            'number' => '',
            'offset' => 0,
            'post_type' => 'page',
            'post_status' => 'publish'
        );
        $pages = get_pages($args);
        $options = array(
            -1 => __('Front Page', 'peepso-core'),
            0 => __('No redirect', 'peepso-core'),
        );

        $pageredirect = PeepSo::get_option('logout_redirect');
        $settings = PeepSoConfigSettings::get_instance();
        foreach ($pages as $page) {
            // handling selected old value (activity/profile)
            if($page->post_name === $pageredirect) {
                //$this->args('default', $page->ID);
                // update option to selected ID
                $settings->set_option('logout_redirect', $page->ID);
            }

            $options[$page->ID] = __('Page:','peepso-core') . ' ' . ($page->post_parent > 0 ? '&nbsp;&nbsp;' : '') . $page->post_title;
        }

        $this->args('options', $options);

        $this->set_field(
            'logout_redirect',
            __('Log-out redirect', 'peepso-core'),
            'select'
        );

        // Build Group
        $this->set_group(
            'login_logout',
            __('Login & Logout', 'peepso-core')
        );

    }

}
?>
