/* Brainwerk SEO Suite admin JS — AJAX wiring for tools, redirects, 404s, analyzer. */
(function ($) {
    'use strict';
    if (typeof seoforgeAjax === 'undefined') { return; }

    function post(action, data) {
        return $.post(seoforgeAjax.url, $.extend({
            action: action,
            nonce: seoforgeAjax.nonce
        }, data || {}));
    }

    function flash(target, ok, message) {
        var $t = $(target);
        $t.text(message).css('color', ok ? '#16a34a' : '#dc2626');
        setTimeout(function () { $t.text(''); }, 4000);
    }

    // Tools
    $('#sf-test-mail').on('click', function () {
        var $btn = $(this).prop('disabled', true);
        post('seoforge_test_mail').done(function (res) {
            flash('#sf-test-mail-result', res && res.ok, res && res.ok ? 'Sent!' : 'Failed.');
        }).fail(function () {
            flash('#sf-test-mail-result', false, seoforgeAjax.i18n.genericError);
        }).always(function () { $btn.prop('disabled', false); });
    });

    $('#sf-cleanup-now').on('click', function () {
        var $btn = $(this).prop('disabled', true);
        post('seoforge_cleanup_now').done(function () {
            flash('#sf-cleanup-result', true, 'Done.');
        }).always(function () { $btn.prop('disabled', false); });
    });

    $('#sf-reset-visits').on('click', function () {
        if (!window.confirm(seoforgeAjax.i18n.confirmReset)) { return; }
        post('seoforge_reset_visits').done(function () { window.location.reload(); });
    });

    $('#sf-reset-404').on('click', function () {
        if (!window.confirm(seoforgeAjax.i18n.confirmReset)) { return; }
        post('seoforge_reset_404').done(function () { window.location.reload(); });
    });

    // Redirects
    $('.sf-toggle-redir').on('click', function () {
        var id = $(this).data('id');
        var $btn = $(this);
        post('seoforge_toggle_redir', { id: id }).done(function (res) {
            $btn.text(res.is_active ? 'On' : 'Off');
        });
    });
    $('.sf-delete-redir').on('click', function () {
        if (!window.confirm(seoforgeAjax.i18n.confirmDelete)) { return; }
        var id = $(this).data('id');
        var $row = $(this).closest('tr');
        post('seoforge_delete_redir', { id: id }).done(function () { $row.remove(); });
    });

    // 404 actions
    $('.sf-resolve-404').on('click', function () {
        var id = $(this).data('id');
        var $btn = $(this);
        post('seoforge_resolve_404', { id: id }).done(function () {
            $btn.closest('tr').fadeOut();
        });
    });

    $('.sf-quick-redirect').on('click', function () {
        var src = $(this).data('source');
        var url = (window.location.href.replace(/[&?]tab=[^&]*/g, '')) + '&tab=redirects&prefill_source=' + encodeURIComponent(src);
        window.location.href = url;
    });

    // Score class thresholds — mirror of Seoforge_Admin_Columns::score_class().
    function scoreClass(s) {
        return s >= 80 ? 'good' : (s >= 50 ? 'ok' : 'bad');
    }

    // SEO list table + native edit.php column: analyze one post, persist, and
    // update the score badge in place (no reload). Delegated so it also works
    // for rows added after load.
    $(document).on('click', '.sf-analyze-btn', function () {
        var $btn = $(this);
        var post_id = $btn.data('post');
        var label = $btn.text();
        var $badge = $btn.closest('td, .seoforge-score').find('.sf-score-badge');
        $btn.prop('disabled', true).text('…');
        post('seoforge_analyze_post', { post_id: post_id }).done(function (res) {
            var s = parseInt(res.score, 10) || 0;
            if ($badge.length) {
                $badge.text(s)
                    .removeClass('seoforge-pill-good seoforge-pill-ok seoforge-pill-bad seoforge-pill-unscored')
                    .addClass('seoforge-pill-' + scoreClass(s));
                $btn.text(label || 'Re-analyze').prop('disabled', false);
            } else {
                $btn.text(s + '/100').prop('disabled', false);
            }
        }).fail(function () {
            $btn.text(seoforgeAjax.i18n.genericError || 'Failed').prop('disabled', false);
        });
    });

    // Backfill: recompute every post that has no score yet, in server-side
    // batches, looping until the queue is drained.
    $('#sf-backfill-scores').on('click', function () {
        var $btn = $(this).prop('disabled', true);
        var $out = $('#sf-backfill-result');
        var total = 0;
        function runBatch() {
            post('seoforge_backfill_scores').done(function (res) {
                if (!res || !res.ok) {
                    $out.text(seoforgeAjax.i18n.genericError || 'Failed').css('color', '#dc2626');
                    $btn.prop('disabled', false);
                    return;
                }
                total += res.processed;
                $out.css('color', '#334155').text(
                    (seoforgeAjax.i18n.backfillProgress || 'Analyzed %d, %r remaining')
                        .replace('%d', total).replace('%r', res.remaining)
                );
                if (res.processed > 0 && res.remaining > 0) {
                    runBatch();
                } else {
                    $out.css('color', '#16a34a').text(
                        (seoforgeAjax.i18n.backfillDone || 'Done — %d posts analyzed.').replace('%d', total)
                    );
                    window.setTimeout(function () { window.location.reload(); }, 900);
                }
            }).fail(function () {
                $out.text(seoforgeAjax.i18n.genericError || 'Failed').css('color', '#dc2626');
                $btn.prop('disabled', false);
            });
        }
        runBatch();
    });

    // Verify tab: nginx log reconciliation, chunked/resumable so a wide
    // range never risks a PHP timeout — same offset+accumulator handshake
    // as Seoforge_Log_Reconciler::reconcile() itself (see its docblock).
    (function () {
        var $run = $('#sf-verify-run');
        if (!$run.length) { return; }
        var $cancel = $('#sf-verify-cancel');
        var $progress = $('#sf-verify-progress');
        var $results = $('#sf-verify-results');
        var cancelled = false;

        function fmt(str, n) { return String(str || '').replace('%d', n); }

        function toServerDatetime(val) {
            // <input type=datetime-local> gives "YYYY-MM-DDTHH:MM"; the
            // server expects a space, not a "T".
            return String(val || '').replace('T', ' ');
        }

        function renderDone(result) {
            var c = result.comparison || {};
            $('#sf-verify-total').text(result.total);
            $('#sf-verify-after-static').text(result.after_static);
            $('#sf-verify-after-admin').text(result.after_admin);
            $('#sf-verify-counted').text(result.counted);
            $('#sf-verify-plugin-count').text(c.plugin_count || 0);
            $('#sf-verify-malformed').text(result.malformed);
            var deviation = c.deviation || 0;
            var $dev = $('#sf-verify-deviation');
            $dev.text(fmt(seoforgeAjax.i18n.verifyDeviation, deviation));
            $dev.css('color', deviation === 0 ? '#16a34a' : '#dc2626');
            var $logOnly = $('#sf-verify-log-only').empty();
            (c.sample_log_only || []).forEach(function (b) { $logOnly.append($('<li>').text(b)); });
            var $pluginOnly = $('#sf-verify-plugin-only').empty();
            (c.sample_plugin_only || []).forEach(function (b) { $pluginOnly.append($('<li>').text(b)); });
            $results.show();
        }

        function runChunk(offset, accumulator) {
            if (cancelled) {
                $progress.text(seoforgeAjax.i18n.verifyCancelled || 'Cancelled.');
                $run.prop('disabled', false);
                $cancel.hide();
                return;
            }
            post('seoforge_verify_reconcile', {
                start: toServerDatetime($('#sf-verify-start').val()),
                end: toServerDatetime($('#sf-verify-end').val()),
                format: $('#sf-verify-run-format').val(),
                source: $('#sf-verify-source').val(),
                offset: offset,
                accumulator: accumulator ? JSON.stringify(accumulator) : ''
            }).done(function (res) {
                if (!res || !res.ok) {
                    $progress.text((res && res.data && res.data.message) || seoforgeAjax.i18n.genericError);
                    $run.prop('disabled', false);
                    $cancel.hide();
                    return;
                }
                var result = res.result;
                if (result.done) {
                    $progress.text(seoforgeAjax.i18n.verifyDone || 'Done.');
                    $run.prop('disabled', false);
                    $cancel.hide();
                    renderDone(result);
                } else {
                    $progress.text(fmt(seoforgeAjax.i18n.verifyProgress, result.total));
                    runChunk(result.resume_offset, result);
                }
            }).fail(function (xhr) {
                var msg = (xhr.responseJSON && xhr.responseJSON.data && xhr.responseJSON.data.message) || seoforgeAjax.i18n.genericError;
                $progress.text(msg);
                $run.prop('disabled', false);
                $cancel.hide();
            });
        }

        $run.on('click', function () {
            if (!$('#sf-verify-start').val() || !$('#sf-verify-end').val()) {
                $progress.text(seoforgeAjax.i18n.verifyBadRange || 'Please choose a start and end date/time.');
                return;
            }
            cancelled = false;
            $results.hide();
            $run.prop('disabled', true);
            $cancel.show();
            $progress.css('color', '#334155').text(seoforgeAjax.i18n.verifyRunning || 'Reconciling…');
            runChunk(0, null);
        });

        $cancel.on('click', function () {
            cancelled = true;
        });
    })();

    // Post meta box: re-analyze
    $('#seoforge-reanalyze').on('click', function () {
        var post_id = $(this).data('post');
        var $btn = $(this).prop('disabled', true).text('…');
        post('seoforge_analyze_post', { post_id: post_id }).done(function () {
            $btn.text('Saving…');
            window.setTimeout(function () { window.location.reload(); }, 400);
        });
    });

    // Prefill redirect source from 404 quick-redirect
    var qs = new URLSearchParams(window.location.search);
    if (qs.get('prefill_source')) {
        var $input = $('input[name="source_path"]');
        if ($input.length) {
            $input.val(qs.get('prefill_source')).focus();
        }
    }

    // Dashboard-widget sparkline tooltip — daily total + source breakdown.
    $('.seoforge-spark-wrap').each(function () {
        var $wrap = $(this);
        var raw   = $wrap.attr('data-days');
        var data  = [];
        try { data = JSON.parse(raw); } catch (e) { return; }
        var $tip = $wrap.find('.seoforge-spark-tooltip');

        var sourceLabels = {
            search:   'Search',
            social:   'Social',
            referral: 'Referral',
            direct:   'Direct',
            campaign: 'Campaign',
            self:     'Internal',
            outbound: 'Outbound'
        };

        function formatDate(iso) {
            var p = String(iso).split('-');
            if (p.length !== 3) { return iso; }
            return p[2] + '.' + p[1] + '.' + p[0];
        }

        $wrap.on('mouseenter', '.sf-spark-hover', function () {
            var i = parseInt(this.getAttribute('data-i'), 10);
            var d = data[i];
            if (!d) { return; }
            var sources = d.sources || {};
            var keys = Object.keys(sources).sort(function (a, b) {
                return (sources[b] || 0) - (sources[a] || 0);
            });
            var html = '<div class="sf-tip-head">' + formatDate(d.day) + '</div>'
                     + '<div class="sf-tip-total"><strong>' + (d.total || 0) + '</strong> Pageviews</div>';
            if (keys.length) {
                html += '<ul class="sf-tip-list">';
                keys.forEach(function (k) {
                    var label = sourceLabels[k] || k;
                    html += '<li><span class="sf-tip-pill sf-tip-pill-' + k + '"></span>'
                          + label + '<span class="sf-tip-num">' + sources[k] + '</span></li>';
                });
                html += '</ul>';
            } else {
                html += '<div class="sf-tip-empty">no visits</div>';
            }
            $tip.html(html).show();

            $wrap.find('.sf-spark-dot').attr('r', 3.5);
            $wrap.find('.sf-spark-dot[data-i="' + i + '"]').attr('r', 5.5);
        });

        $wrap.on('mousemove', '.sf-spark-hover', function (e) {
            var rect = $wrap[0].getBoundingClientRect();
            var tipW = $tip.outerWidth();
            var tipH = $tip.outerHeight();
            var left = e.clientX - rect.left + 14;
            // flip horizontally if would overflow the widget
            if (left + tipW > rect.width - 4) { left = e.clientX - rect.left - tipW - 14; }
            var top = e.clientY - rect.top - tipH - 8;
            if (top < 0) { top = e.clientY - rect.top + 14; }
            $tip.css({ left: left + 'px', top: top + 'px' });
        });

        $wrap.on('mouseleave', '.sf-spark-hover', function () {
            $tip.hide();
            $wrap.find('.sf-spark-dot').attr('r', 3.5);
        });
    });

    // Live visitors poller — only on dashboard tab.
    if ($('#seoforge-live-count').length) {
        var pollLive = function () {
            $.ajax({
                url: '/wp-json/seoforge/v1/live',
                headers: { 'X-WP-Nonce': window.wpApiSettings ? window.wpApiSettings.nonce : '' },
                cache: false
            }).done(function (data) {
                if (!data) return;
                $('#seoforge-live-count').text(data.visitors || 0);
                if (Array.isArray(data.pages)) {
                    var $list = $('#seoforge-live-pages').empty();
                    data.pages.forEach(function (p) {
                        var li = $('<li/>');
                        $('<a/>', {
                            href: p.url_path,
                            target: '_blank',
                            text: p.title || p.url_path
                        }).appendTo(li);
                        li.append(' — ' + p.hits);
                        $list.append(li);
                    });
                }
            });
        };
        // Refetch on a 15s cadence; respects WP REST nonce when present.
        setInterval(pollLive, 15000);
    }

    // Dashboard "pjax" — swap only #seoforge-dash-content on range/scope
    // changes so the page never does a full admin reload. Progressive
    // enhancement only: the range links stay real <a href>s and the scope
    // picker stays a real GET form, so no-JS / new-tab / back-forward all
    // keep working; any fetch problem falls back to a normal navigation.
    if ($('#seoforge-dash-content').length && window.fetch && window.DOMParser) {

        function dashSwap(url, pushHistory) {
            var $container = $('#seoforge-dash-content');
            if (!$container.length) { window.location = url; return; }
            $container.addClass('is-loading');
            fetch(url, { credentials: 'same-origin' }).then(function (res) {
                if (!res.ok) { throw new Error('seoforge: bad status ' + res.status); }
                return res.text();
            }).then(function (html) {
                var doc     = new DOMParser().parseFromString(html, 'text/html');
                var newBody = doc.getElementById('seoforge-dash-content');
                if (!newBody) { throw new Error('seoforge: no dash container in response'); }
                $container.html(newBody.innerHTML);

                // The range-picker and scope-picker are server-rendered with
                // the correct active/selected state on every full response —
                // just copy that markup over rather than re-deriving it in JS.
                var newRange = doc.querySelector('.seoforge-range-picker');
                if (newRange) { $('.seoforge-range-picker').html(newRange.innerHTML); }
                var newSite = doc.getElementById('seoforge-site-select');
                if (newSite) { $('#seoforge-site-select').val(newSite.value); }

                $container.removeClass('is-loading');
                if (pushHistory) {
                    window.history.pushState({ seoforgeDash: true }, '', url);
                }
            }).catch(function () {
                window.location = url;
            });
        }

        $(document).on('click', '.seoforge-range-picker a', function (e) {
            if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || e.button !== 0) { return; }
            e.preventDefault();
            dashSwap(this.href, true);
        });

        $(document).on('submit', '.seoforge-scope-picker', function (e) {
            e.preventDefault();
            var $form = $(this);
            var action = $form.attr('action') || window.location.href.split('?')[0];
            var url = action + (action.indexOf('?') > -1 ? '&' : '?') + $form.serialize();
            dashSwap(url, true);
        });

        window.addEventListener('popstate', function () {
            dashSwap(window.location.href, false);
        });
    }

    // AI: connection-test on the AI tab
    $('#sf-ai-test').on('click', function () {
        var $btn = $(this).prop('disabled', true);
        $('#sf-ai-test-result').text('…').css('color', '');
        post('seoforge_ai_test').done(function (r) {
            $('#sf-ai-test-result').text('OK — ' + (r.reply || '')).css('color', '#16a34a');
        }).fail(function (xhr) {
            var msg = (xhr.responseJSON && xhr.responseJSON.data && xhr.responseJSON.data.message) || 'failed';
            $('#sf-ai-test-result').text(msg).css('color', '#dc2626');
        }).always(function () { $btn.prop('disabled', false); });
    });

    // AI: per-post optimize buttons (in the post meta box)
    function aiModalOpen(html) {
        $('#sf-ai-modal-body').html(html);
        $('#sf-ai-suggestions').show();
    }
    $(document).on('click', '#sf-ai-modal-close', function () {
        $('#sf-ai-suggestions').hide();
    });
    $(document).on('click', '.sf-ai-suggest', function () {
        var $btn   = $(this);
        var kind   = $btn.data('kind');
        var pid    = $btn.data('post');
        var target = $btn.data('target');
        var inline = (kind === 'links');

        if (inline) {
            $(target).html('<p><span class="spinner is-active" style="float:none"></span> ' + 'Asking the AI…' + '</p>');
        } else {
            aiModalOpen('<p><span class="spinner is-active" style="float:none"></span> Asking the AI…</p>');
        }
        $btn.prop('disabled', true);

        post('seoforge_ai_suggest', { kind: kind, post_id: pid }).done(function (res) {
            var alts = (res.data && res.data.alternatives) || [];
            var note = (res.data && res.data.note) || '';
            if (!alts.length) {
                var msg = '<p><strong>No strong matches.</strong>'
                        + (note ? ' ' + $('<div/>').text(note).html() : '')
                        + '</p><p class="description">Tip: set a focus keyword first, then click Find again.</p>';
                if (inline) $(target).html(msg); else aiModalOpen(msg);
                return;
            }
            var html;
            if (kind === 'titles' || kind === 'descriptions') {
                var key = (kind === 'titles') ? 'title' : 'description';
                html = '<ul class="sf-ai-list">';
                alts.forEach(function (a) {
                    var val = a[key] || '';
                    html += '<li><div class="sf-ai-text">' + $('<div/>').text(val).html() + '</div>';
                    if (a.why) html += '<div class="sf-ai-why">' + $('<div/>').text(a.why).html() + '</div>';
                    html += '<button type="button" class="button button-small sf-ai-apply" data-target="' + target + '" data-value="' + encodeURIComponent(val) + '">Use this</button></li>';
                });
                html += '</ul>';
                aiModalOpen(html);
            } else if (kind === 'focus') {
                html = '<ul class="sf-ai-list">';
                alts.forEach(function (a) {
                    var val = a.keyword || '';
                    html += '<li><div class="sf-ai-text">' + $('<div/>').text(val).html() + '</div>';
                    if (a.intent) html += '<div class="sf-ai-why"><em>' + $('<div/>').text(a.intent).html() + '</em> · ' + $('<div/>').text(a.why || '').html() + '</div>';
                    html += '<button type="button" class="button button-small sf-ai-apply" data-target="' + target + '" data-value="' + encodeURIComponent(val) + '">Use this</button></li>';
                });
                html += '</ul>';
                aiModalOpen(html);
            } else if (kind === 'links') {
                html = '<ul class="sf-ai-list">';
                alts.forEach(function (a) {
                    var anchor = a.anchor || '';
                    var url    = a.url || '#';
                    var title  = a.title || '';
                    html += '<li><div class="sf-ai-text"><a href="' + $('<div/>').text(url).html() + '" target="_blank">' + $('<div/>').text(title).html() + '</a></div>';
                    html += '<div class="sf-ai-why">Anchor: <code>' + $('<div/>').text(anchor).html() + '</code> — ' + $('<div/>').text(a.why || '').html() + '</div>';
                    html += '<button type="button" class="button button-small sf-ai-copy-link" data-anchor="' + encodeURIComponent(anchor) + '" data-url="' + $('<div/>').text(url).html() + '">Copy link HTML</button>';
                    html += '</li>';
                });
                html += '</ul>';
                $(target).html(html);
            }
        }).fail(function (xhr) {
            var msg = (xhr.responseJSON && xhr.responseJSON.data && xhr.responseJSON.data.message) || 'Request failed.';
            var html = '<p style="color:#dc2626">' + $('<div/>').text(msg).html() + '</p>';
            if (inline) $(target).html(html); else aiModalOpen(html);
        }).always(function () { $btn.prop('disabled', false); });
    });
    // Apply a suggestion into the input it belongs to.
    $(document).on('click', '.sf-ai-apply', function () {
        var sel = $(this).data('target');
        var val = decodeURIComponent($(this).data('value'));
        $(sel).val(val).trigger('change').focus();
        $('#sf-ai-suggestions').hide();
    });
    $(document).on('click', '.sf-ai-copy-link', function () {
        var anchor = decodeURIComponent($(this).data('anchor'));
        var url    = $(this).data('url');
        var html   = '<a href="' + url + '">' + anchor + '</a>';
        try {
            navigator.clipboard.writeText(html).then(function () {});
            $(this).text('Copied!');
        } catch (e) {}
    });

    // Migration wizard
    var migrateTimer = null;
    function refreshMigrateProgress(state) {
        if (!state) return;
        var total = parseInt(state.total_posts, 10) || 0;
        var done  = parseInt(state.done_posts, 10) || 0;
        var pct   = total > 0 ? Math.min(100, Math.round((done / total) * 100)) : 0;
        $('#sf-progress-bar').css('width', pct + '%');
        $('#sf-progress-pct').text(pct);
        $('#sf-progress-done').text(done);
        $('#sf-progress-total').text(total);
        $('#sf-progress-meta').text(state.imported_meta || 0);
        $('#sf-progress-red').text(state.imported_red || 0);
        var msg = '';
        if (state.status === 'running') { msg = 'Working in the background…'; }
        else if (state.status === 'done') { msg = 'Done. You can deactivate the old plugin now.'; }
        else if (state.status === 'error') { msg = 'Error: ' + (state.last_error || '?'); }
        $('#sf-progress-status').text(msg);
        $('#sf-import-progress').show();

        if (state.status === 'running' && !migrateTimer) {
            migrateTimer = setInterval(function () {
                post('seoforge_migrate_status').done(function (r) {
                    refreshMigrateProgress(r.state);
                    if (r.state && r.state.status !== 'running') {
                        clearInterval(migrateTimer);
                        migrateTimer = null;
                    }
                });
            }, 3000);
        }
    }
    $('.sf-import-start').on('click', function () {
        var source = $(this).data('source');
        if (!window.confirm('Start importing from ' + source + '? This is safe — the original plugin is not modified.')) { return; }
        $('.sf-import-start').prop('disabled', true);
        post('seoforge_migrate_start', { source: source }).done(function (r) {
            refreshMigrateProgress(r.state);
        }).fail(function (xhr) {
            alert((xhr.responseJSON && xhr.responseJSON.data && xhr.responseJSON.data.message) || 'Failed to start.');
            $('.sf-import-start').prop('disabled', false);
        });
    });
    // If the page loads with a running migration, attach the poller.
    if ($('#sf-import-progress').is(':visible') && $('#sf-progress-status').text().toLowerCase().indexOf('working') !== -1) {
        post('seoforge_migrate_status').done(function (r) { refreshMigrateProgress(r.state); });
    }

    // Reports tab: quick-range links prefill the date inputs + tag select.
    $(document).on('click', '.seoforge-quick-range', function (e) {
        e.preventDefault();
        var $a = $(this);
        $('input[name="range_start"]').val($a.data('start'));
        $('input[name="range_end"]').val($a.data('end'));
        $('select[name="tag"]').val($a.data('tag'));
    });

    // Geo: upgrade ISO codes to full country names using the browser's own
    // Intl data — no country-name table needs to ship in the plugin.
    (function () {
        var dn = null;
        try {
            if (window.Intl && Intl.DisplayNames) {
                dn = new Intl.DisplayNames([document.documentElement.lang || 'en'], { type: 'region' });
            }
        } catch (e) { dn = null; }
        if (!dn) { return; }
        $('.sf-geo-name[data-cc]').each(function () {
            var cc = $(this).data('cc');
            if (!cc) { return; }
            try {
                var name = dn.of(String(cc).toUpperCase());
                if (name && name !== cc) { $(this).text(name); }
            } catch (e) {}
        });
    }());

    // Geo: drill-down a country into its top regions + cities.
    function geoRange() {
        // Mirror the active quick-range if present; default to 7 days (168h).
        var h = parseInt($('.sf-geo-list').data('hours'), 10);
        return (h && h > 0) ? h : 168;
    }
    function geoName(cc) {
        try {
            if (window.Intl && Intl.DisplayNames) {
                var n = new Intl.DisplayNames([document.documentElement.lang || 'en'], { type: 'region' }).of(cc);
                if (n) { return n; }
            }
        } catch (e) {}
        return cc;
    }
    function geoList(title, rows) {
        var html = '<div class="sf-geo-col"><h4>' + title + '</h4>';
        if (!rows || !rows.length) {
            html += '<p class="seoforge-empty sf-geo-empty">' + (seoforgeAjax.i18n.geoNoDetail || '—') + '</p></div>';
            return html;
        }
        var total = rows.reduce(function (s, r) { return s + (parseInt(r.hits, 10) || 0); }, 0) || 1;
        html += '<ul class="seoforge-bar-list">';
        rows.forEach(function (r) {
            var hits = parseInt(r.hits, 10) || 0;
            var pct = Math.round((hits / total) * 1000) / 10;
            var label = $('<span>').text(r.label || '—').html();
            html += '<li><span class="seoforge-bar-list-label">' + label + '</span>' +
                '<span class="seoforge-bar-list-track"><span style="width:' + pct + '%"></span></span>' +
                '<span class="seoforge-bar-list-value">' + pct + '%</span></li>';
        });
        html += '</ul></div>';
        return html;
    }
    $(document).on('click', '.sf-geo-country', function () {
        var $btn = $(this);
        var $panel = $btn.next('.sf-geo-drill');
        var open = $btn.attr('aria-expanded') === 'true';
        // Collapse any other open panel.
        $('.sf-geo-country[aria-expanded="true"]').not($btn).attr('aria-expanded', 'false')
            .next('.sf-geo-drill').attr('hidden', true).empty();
        if (open) {
            $btn.attr('aria-expanded', 'false');
            $panel.attr('hidden', true).empty();
            return;
        }
        var cc = $btn.data('country');
        $btn.attr('aria-expanded', 'true');
        $panel.removeAttr('hidden').html('<p class="sf-geo-loading">…</p>');
        post('seoforge_geo_drill', { country: cc, hours: geoRange() }).done(function (r) {
            if (!r || !r.ok) {
                $panel.html('<p class="seoforge-empty">' + seoforgeAjax.i18n.genericError + '</p>');
                return;
            }
            var head = '<div class="sf-geo-drill-head">' + geoName(cc) + '</div>';
            $panel.html(head + '<div class="sf-geo-cols">' +
                geoList(seoforgeAjax.i18n.geoRegions || 'Regions', r.regions) +
                geoList(seoforgeAjax.i18n.geoCities || 'Cities', r.cities) + '</div>');
        }).fail(function () {
            $panel.html('<p class="seoforge-empty">' + seoforgeAjax.i18n.genericError + '</p>');
        });
    });

    // FAQ repeater in the post meta box (FAQPage schema / GEO).
    (function () {
        var $list = $('#sf-faq-list');
        if (!$list.length) { return; }
        var seq = 1000; // start high so new-row indices never collide with existing ones
        $(document).on('click', '.sf-faq-add', function (e) {
            e.preventDefault();
            var tmpl = $('#sf-faq-tmpl').html() || '';
            $list.append(tmpl.replace(/__i__/g, String(seq++)));
        });
        $(document).on('click', '.sf-faq-remove', function (e) {
            e.preventDefault();
            $(this).closest('.sf-faq-row').remove();
        });
    }());

    // License activation lives in Freemius's own admin pages now.
}(jQuery));
