<?php

namespace WPSN\Modules\Sitemap\Admin;

use WPSN\Core\Capabilities;
use WPSN\Core\Container;
use WPSN\Core\Http\HttpClient;
use WPSN\Core\ModuleRegistry;
use WPSN\Core\Settings;
use WPSN\Modules\IndexNow\IndexNowJobs;
use WPSN\Modules\Sitemap\SitemapModule;

defined('ABSPATH') || exit;

/**
 * Admin UI for sitemap endpoints.
 *
 * Users often expect a "button" to find the sitemap URL.
 * This page provides:
 * - Open sitemap links
 * - Flush rewrite rules
 * - Bust sitemap cache
 * - Manual ping (Google/Bing) via queue
 */
final class SitemapAdmin
{
    public const PAGE_SLUG = 'wpsn-sitemaps';

    private const SELFTEST_TTL = 600; // 10 minutes

    private Container $container;
    private Settings $settings;

    public function __construct(Container $container)
    {
        $this->container = $container;
        $this->settings = $container->get('settings');
    }

    public function register(): void
    {
        add_action('admin_menu', [$this, 'addMenu']);

        add_action('admin_post_wpsn_sitemaps_flush_rewrites', [$this, 'handleFlushRewrites']);
        add_action('admin_post_wpsn_sitemaps_bust_cache', [$this, 'handleBustCache']);
        add_action('admin_post_wpsn_sitemaps_ping_now', [$this, 'handlePingNow']);
        add_action('admin_post_wpsn_sitemaps_self_test', [$this, 'handleSelfTest']);
        add_action('admin_post_wpsn_sitemaps_self_test_clear', [$this, 'handleSelfTestClear']);
    }

    public function addMenu(): void
    {
        add_management_page(
            __('Sitemaps', 'wp-seo-next'),
            __('Sitemaps', 'wp-seo-next'),
            Capabilities::CAP_TOOLS,
            self::PAGE_SLUG,
            [$this, 'render']
        );
    }

    public function handleFlushRewrites(): void
    {
        if (!Capabilities::canManageTools()) {
            wp_die(esc_html__('You do not have permission to perform this action.', 'wp-seo-next'));
        }
        check_admin_referer('wpsn_sitemaps_flush_rewrites');

        $modules = $this->container->get('modules');
        if ($modules instanceof ModuleRegistry && !$modules->isEnabled('sitemap')) {
            wp_safe_redirect(admin_url('tools.php?page=' . self::PAGE_SLUG . '&wpsn_notice=sitemap_disabled'));
            exit;
        }

        // Ensure our sitemap rewrite rules exist before flushing.
        if ($modules instanceof ModuleRegistry) {
            $all = $modules->all();
            if (isset($all['sitemap']) && $all['sitemap'] instanceof SitemapModule) {
                $all['sitemap']->registerRewriteRules();
            }
        }

        flush_rewrite_rules(false);

        wp_safe_redirect(admin_url('tools.php?page=' . self::PAGE_SLUG . '&wpsn_notice=rewrites_flushed'));
        exit;
    }

    public function handleBustCache(): void
    {
        if (!Capabilities::canManageTools()) {
            wp_die(esc_html__('You do not have permission to perform this action.', 'wp-seo-next'));
        }
        check_admin_referer('wpsn_sitemaps_bust_cache');

        $modules = $this->container->get('modules');
        if ($modules instanceof ModuleRegistry && !$modules->isEnabled('sitemap')) {
            wp_safe_redirect(admin_url('tools.php?page=' . self::PAGE_SLUG . '&wpsn_notice=sitemap_disabled'));
            exit;
        }

        if ($modules instanceof ModuleRegistry) {
            $all = $modules->all();
            if (isset($all['sitemap']) && $all['sitemap'] instanceof SitemapModule) {
                $all['sitemap']->bustCache('manual');
            }
        }

        wp_safe_redirect(admin_url('tools.php?page=' . self::PAGE_SLUG . '&wpsn_notice=cache_busted'));
        exit;
    }

    public function handlePingNow(): void
    {
        if (!Capabilities::canManageTools()) {
            wp_die(esc_html__('You do not have permission to perform this action.', 'wp-seo-next'));
        }
        check_admin_referer('wpsn_sitemaps_ping_now');

        $modules = $this->container->get('modules');
        if ($modules instanceof ModuleRegistry && !$modules->isEnabled('sitemap')) {
            wp_safe_redirect(admin_url('tools.php?page=' . self::PAGE_SLUG . '&wpsn_notice=sitemap_disabled'));
            exit;
        }

        // Respect privacy modes.
        if ((bool) $this->settings->get('privacy.local_only', false)) {
            wp_safe_redirect(admin_url('tools.php?page=' . self::PAGE_SLUG . '&wpsn_notice=ping_blocked_local_only'));
            exit;
        }
        if ((string) get_option('blog_public') === '0') {
            wp_safe_redirect(admin_url('tools.php?page=' . self::PAGE_SLUG . '&wpsn_notice=ping_blocked_discourage'));
            exit;
        }

        $sp = $this->settings->get('indexnow.sitemap_pings', []);
        $sp = is_array($sp) ? $sp : [];
        $prov = isset($sp['providers']) && is_array($sp['providers']) ? $sp['providers'] : [];
        $providers = [];
        foreach (['bing', 'google'] as $p) {
            if (!empty($prov[$p])) {
                $providers[] = $p;
            }
        }
        if ($providers === []) {
            // Manual ping default.
            $providers = ['bing'];
        }

        $sitemapUrls = [
            home_url('/sitemap_index.xml'),
        ];
        if ((bool) $this->settings->get('sitemap.news.enabled', false)) {
            $sitemapUrls[] = home_url('/news-sitemap.xml');
        }
        if ((bool) $this->settings->get('sitemap.video.enabled', false)) {
            $sitemapUrls[] = home_url('/video-sitemap.xml');
        }

        /** @var \WPSN\Core\Queue\QueueInterface $queue */
        $queue = $this->container->get('queue');

        foreach ($sitemapUrls as $url) {
            $url = is_string($url) ? trim($url) : '';
            if ($url === '') {
                continue;
            }

            foreach ($providers as $provider) {
                $provider = sanitize_key((string) $provider);
                if ($provider === '') {
                    continue;
                }

                $queue->enqueue(IndexNowJobs::JOB_PING_SITEMAP, [
                    'provider' => $provider,
                    'sitemap_url' => $url,
                    'reason' => 'manual',
                    'token' => 'manual',
                ]);
            }
        }

        wp_safe_redirect(admin_url('tools.php?page=' . self::PAGE_SLUG . '&wpsn_notice=ping_queued'));
        exit;
    }

    public function handleSelfTest(): void
    {
        if (!Capabilities::canManageTools()) {
            wp_die(esc_html__('You do not have permission to perform this action.', 'wp-seo-next'));
        }
        check_admin_referer('wpsn_sitemaps_self_test');

        $results = $this->runSelfTest();
        set_transient($this->selfTestKey(), $results, self::SELFTEST_TTL);

        wp_safe_redirect(admin_url('tools.php?page=' . self::PAGE_SLUG . '&wpsn_notice=selftest_done'));
        exit;
    }

    public function handleSelfTestClear(): void
    {
        if (!Capabilities::canManageTools()) {
            wp_die(esc_html__('You do not have permission to perform this action.', 'wp-seo-next'));
        }
        check_admin_referer('wpsn_sitemaps_self_test_clear');

        delete_transient($this->selfTestKey());

        wp_safe_redirect(admin_url('tools.php?page=' . self::PAGE_SLUG . '&wpsn_notice=selftest_cleared'));
        exit;
    }

    public function render(): void
    {
        if (!Capabilities::canManageTools()) {
            wp_die(esc_html__('You do not have permission to access this page.', 'wp-seo-next'));
        }

        $modules = $this->container->get('modules');
        $sitemapEnabled = $modules instanceof ModuleRegistry ? $modules->isEnabled('sitemap') : true;

        $indexUrl = home_url('/sitemap.xml');
        $indexUrlAlt = home_url('/sitemap_index.xml');
        $htmlUrl = home_url('/html-sitemap/');
        $newsUrl = home_url('/news-sitemap.xml');
        $videoUrl = home_url('/video-sitemap.xml');

        $robotsAppend = (bool) $this->settings->get('robots_txt.append_sitemap', true);
        $pingsEnabled = (bool) $this->settings->get('indexnow.sitemap_pings.enabled', false);
        $localOnly = (bool) $this->settings->get('privacy.local_only', false);
        $discourage = (string) get_option('blog_public') === '0';

        // Best-effort check: do sitemap rewrite rules exist in DB?
        $rewriteRulesOk = $this->rewriteRulesPresent();

        $notice = isset($_GET['wpsn_notice']) ? sanitize_key((string) wp_unslash($_GET['wpsn_notice'])) : '';

        echo '<div class="wrap">';
        echo '<h1>' . esc_html__('Sitemaps', 'wp-seo-next') . '</h1>';
        echo '<p class="description">' . esc_html__('Your XML sitemap is generated dynamically. There is no "build" step — just open the URL.', 'wp-seo-next') . '</p>';

        if ($notice !== '') {
            $msg = '';
            $type = 'info';
            switch ($notice) {
                case 'sitemap_disabled':
                    $type = 'warning';
                    $msg = __('Sitemap module is currently disabled. Enable it in WP SEO Next → Settings → Modules (or disable Compatibility mode).', 'wp-seo-next');
                    break;
                case 'rewrites_flushed':
                    $type = 'success';
                    $msg = __('Rewrite rules flushed. Try opening the sitemap again.', 'wp-seo-next');
                    break;
                case 'cache_busted':
                    $type = 'success';
                    $msg = __('Sitemap cache busted.', 'wp-seo-next');
                    break;
                case 'ping_queued':
                    $type = 'success';
                    $msg = __('Sitemap ping jobs were queued. Run the queue worker if needed.', 'wp-seo-next');
                    break;
                case 'ping_blocked_local_only':
                    $type = 'warning';
                    $msg = __('Ping is blocked in Local-only mode.', 'wp-seo-next');
                    break;
                case 'ping_blocked_discourage':
                    $type = 'warning';
                    $msg = __('Ping is blocked because the site discourages search engines (blog_public=0).', 'wp-seo-next');
                    break;
                case 'selftest_done':
                    $type = 'success';
                    $msg = __('Sitemap self-test completed. See results below.', 'wp-seo-next');
                    break;
                case 'selftest_cleared':
                    $type = 'info';
                    $msg = __('Sitemap self-test results cleared.', 'wp-seo-next');
                    break;
            }

            if ($msg !== '') {
                echo '<div class="notice notice-' . esc_attr($type) . '"><p>' . esc_html($msg) . '</p></div>';
            }
        }

        echo '<h2>' . esc_html__('Sitemap URLs', 'wp-seo-next') . '</h2>';
        echo '<table class="widefat striped" style="max-width: 980px;">';
        echo '<tbody>';

        $this->renderUrlRow(__('Sitemap index', 'wp-seo-next'), $indexUrl, __('Main URL (alias).', 'wp-seo-next'));
        $this->renderUrlRow(__('Sitemap index (alt)', 'wp-seo-next'), $indexUrlAlt, __('Same content as /sitemap.xml.', 'wp-seo-next'));
        $this->renderUrlRow(__('HTML sitemap', 'wp-seo-next'), $htmlUrl, __('Human-readable sitemap for users (optional).', 'wp-seo-next'));

        if ((bool) $this->settings->get('sitemap.news.enabled', false)) {
            $this->renderUrlRow(__('News sitemap', 'wp-seo-next'), $newsUrl, __('Optional Google News sitemap.', 'wp-seo-next'));
        }
        if ((bool) $this->settings->get('sitemap.video.enabled', false)) {
            $this->renderUrlRow(__('Video sitemap', 'wp-seo-next'), $videoUrl, __('Optional Video sitemap.', 'wp-seo-next'));
        }

        echo '</tbody>';
        echo '</table>';

        echo '<h2 style="margin-top:18px;">' . esc_html__('Status', 'wp-seo-next') . '</h2>';
        echo '<table class="widefat striped" style="max-width: 980px;">';
        echo '<tbody>';
        $this->renderStatusRow(__('Sitemap module enabled', 'wp-seo-next'), $sitemapEnabled ? __('Yes', 'wp-seo-next') : __('No', 'wp-seo-next'));
        $this->renderStatusRow(__('Sitemap rewrite rules present', 'wp-seo-next'), $rewriteRulesOk ? __('Yes', 'wp-seo-next') : __('No', 'wp-seo-next'));
        $this->renderStatusRow(__('Append Sitemap to robots.txt', 'wp-seo-next'), $robotsAppend ? __('Yes', 'wp-seo-next') : __('No', 'wp-seo-next'));
        $this->renderStatusRow(__('Sitemap pings enabled', 'wp-seo-next'), $pingsEnabled ? __('Yes', 'wp-seo-next') : __('No', 'wp-seo-next'));
        $this->renderStatusRow(__('Local-only mode', 'wp-seo-next'), $localOnly ? __('Yes', 'wp-seo-next') : __('No', 'wp-seo-next'));
        $this->renderStatusRow(__('Discourage search engines', 'wp-seo-next'), $discourage ? __('Yes', 'wp-seo-next') : __('No', 'wp-seo-next'));
        echo '</tbody>';
        echo '</table>';

        echo '<h2 style="margin-top:18px;">' . esc_html__('Actions', 'wp-seo-next') . '</h2>';
        echo '<div style="display:flex; flex-wrap:wrap; gap:10px; margin-top:8px;">';
        echo '<a class="button button-secondary" href="' . esc_url($indexUrl) . '" target="_blank" rel="noopener">' . esc_html__('Open sitemap', 'wp-seo-next') . '</a>';
        echo '<a class="button button-secondary" href="' . esc_url($htmlUrl) . '" target="_blank" rel="noopener">' . esc_html__('Open HTML sitemap', 'wp-seo-next') . '</a>';
        echo '<a class="button button-secondary" href="' . esc_url(admin_url('tools.php?page=wpsn-robots')) . '">' . esc_html__('Robots Center', 'wp-seo-next') . '</a>';

        $flushUrl = wp_nonce_url(
            admin_url('admin-post.php?action=wpsn_sitemaps_flush_rewrites'),
            'wpsn_sitemaps_flush_rewrites'
        );
        echo '<a class="button button-secondary" href="' . esc_url($flushUrl) . '">' . esc_html__('Flush rewrite rules', 'wp-seo-next') . '</a>';

        $bustUrl = wp_nonce_url(
            admin_url('admin-post.php?action=wpsn_sitemaps_bust_cache'),
            'wpsn_sitemaps_bust_cache'
        );
        echo '<a class="button button-secondary" href="' . esc_url($bustUrl) . '">' . esc_html__('Bust sitemap cache', 'wp-seo-next') . '</a>';

        $pingUrl = wp_nonce_url(
            admin_url('admin-post.php?action=wpsn_sitemaps_ping_now'),
            'wpsn_sitemaps_ping_now'
        );
        echo '<a class="button button-secondary" href="' . esc_url($pingUrl) . '">' . esc_html__('Ping search engines now', 'wp-seo-next') . '</a>';

        $selfTestUrl = wp_nonce_url(
            admin_url('admin-post.php?action=wpsn_sitemaps_self_test'),
            'wpsn_sitemaps_self_test'
        );
        echo '<a class="button button-secondary" href="' . esc_url($selfTestUrl) . '">' . esc_html__('Run sitemap self-test', 'wp-seo-next') . '</a>';

        $selfTestClearUrl = wp_nonce_url(
            admin_url('admin-post.php?action=wpsn_sitemaps_self_test_clear'),
            'wpsn_sitemaps_self_test_clear'
        );
        echo '<a class="button button-secondary" href="' . esc_url($selfTestClearUrl) . '">' . esc_html__('Clear self-test', 'wp-seo-next') . '</a>';
        echo '</div>';

        echo '<p class="description" style="max-width:980px; margin-top:10px;">';
        echo esc_html__('If /sitemap.xml returns 404, run the self-test and/or flush rewrite rules (button above) or go to Settings → Permalinks and click Save.', 'wp-seo-next');
        echo '</p>';

        // Self-test results (if any).
        $st = get_transient($this->selfTestKey());
        if (is_array($st) && !empty($st)) {
            $this->renderSelfTest($st, $sitemapEnabled);
        }

        echo '<p class="description" style="max-width:980px;">';
        echo wp_kses_post(sprintf(
            /* translators: %s is the WP SEO Next settings URL */
            __('Sitemap configuration is in <a href="%s">WP SEO Next → Settings → Sitemaps</a>.', 'wp-seo-next'),
            esc_url(admin_url('admin.php?page=wp-seo-next#wpsn-sitemaps'))
        ));
        echo '</p>';

        echo '</div>';
    }

    private function selfTestKey(): string
    {
        $uid = function_exists('get_current_user_id') ? (int) get_current_user_id() : 0;
        return 'wpsn_sitemaps_self_test_' . $uid;
    }

    /**
     * @return array<string,mixed>
     */
    private function runSelfTest(): array
    {
        // IMPORTANT: Only test local URLs. Avoid SSRF.
        $tests = [
            [
                'label' => '/sitemap.xml (alias)',
                'url' => home_url('/sitemap.xml'),
                'accept' => 'application/xml,text/xml;q=0.9,*/*;q=0.8',
            ],
            [
                'label' => '/sitemap_index.xml (canonical)',
                'url' => home_url('/sitemap_index.xml'),
                'accept' => 'application/xml,text/xml;q=0.9,*/*;q=0.8',
            ],
            [
                'label' => '/?wpsn_sitemap=index (no-rewrite fallback)',
                'url' => add_query_arg(['wpsn_sitemap' => 'index'], home_url('/')),
                'accept' => 'application/xml,text/xml;q=0.9,*/*;q=0.8',
            ],
            [
                'label' => '/html-sitemap/ (human)',
                'url' => home_url('/html-sitemap/'),
                'accept' => 'text/html,*/*;q=0.8',
            ],
            [
                'label' => '/?wpsn_html_sitemap=1 (no-rewrite fallback)',
                'url' => add_query_arg(['wpsn_html_sitemap' => '1'], home_url('/')),
                'accept' => 'text/html,*/*;q=0.8',
            ],
        ];

        $out = [
            'ts' => time(),
            'rewrite_rules_present' => $this->rewriteRulesPresent(),
            'results' => [],
        ];

        foreach ($tests as $t) {
            $label = isset($t['label']) ? (string) $t['label'] : '';
            $url = isset($t['url']) ? (string) $t['url'] : '';
            $accept = isset($t['accept']) ? (string) $t['accept'] : 'application/xml,text/xml;q=0.9,*/*;q=0.8';
            if ($label === '' || $url === '') {
                continue;
            }
            $out['results'][] = $this->fetchProbe($label, $url, $accept);
        }

        return $out;
    }

    /**
     * @return array<string,mixed>
     */
    private function fetchProbe(string $label, string $url, string $accept): array
    {
        $resp = HttpClient::get($url, [
            'timeout' => 8,
            'redirection' => 2,
            'headers' => [
                'Accept' => $accept,
                'X-WPSN-Debug' => 'sitemap_self_test',
            ],
            'user-agent' => 'WPSN/' . (defined('WPSN_VERSION') ? WPSN_VERSION : 'dev') . '; ' . home_url('/'),
        ], [
            'service' => 'loopback',
            'context' => 'sitemap_self_test',
            'retries' => 0,
        ]);

        if (is_wp_error($resp)) {
            return [
                'label' => $label,
                'url' => $url,
                'ok' => false,
                'code' => 0,
                'content_type' => '',
                'len' => 0,
                'snip' => '',
                'error' => $resp->get_error_message(),
            ];
        }

        $code = (int) wp_remote_retrieve_response_code($resp);
        $headers = wp_remote_retrieve_headers($resp);
        $ct = '';
        if (is_array($headers) && isset($headers['content-type'])) {
            $ct = (string) $headers['content-type'];
        } elseif (is_object($headers) && method_exists($headers, 'get')) {
            $ct = (string) $headers->get('content-type');
        }

        $body = (string) wp_remote_retrieve_body($resp);
        $len = strlen($body);
        $snip = substr($body, 0, 180);
        $snip = preg_replace('/\s+/', ' ', $snip);

        $ok = $code >= 200 && $code < 300;

        return [
            'label' => $label,
            'url' => $url,
            'ok' => $ok,
            'code' => $code,
            'content_type' => $ct,
            'len' => $len,
            'snip' => $snip,
            'error' => '',
        ];
    }

    private function rewriteRulesPresent(): bool
    {
        $rules = get_option('rewrite_rules');
        if (!is_array($rules) || $rules === []) {
            return false;
        }

        $needles = [
            '^sitemap\\.xml$',
            '^sitemap_index\\.xml$',
            '^([a-z0-9_-]+)-sitemap\\.xml$',
            '^([a-z0-9_-]+)-sitemap([0-9]+)\\.xml$',
        ];

        foreach ($needles as $n) {
            if (array_key_exists($n, $rules)) {
                return true;
            }
        }

        // Some installs store variations (e.g. with trailing slashes stripped). Best-effort substring check.
        foreach ($rules as $regex => $_q) {
            $regex = (string) $regex;
            if (strpos($regex, 'sitemap\\.xml') !== false || strpos($regex, 'sitemap_index\\.xml') !== false) {
                return true;
            }
        }

        return false;
    }

    /**
     * @param array<string,mixed> $st
     */
    private function renderSelfTest(array $st, bool $sitemapEnabled): void
    {
        $ts = isset($st['ts']) ? (int) $st['ts'] : 0;
        $when = $ts > 0 ? gmdate('Y-m-d H:i:s', $ts) . ' UTC' : '';
        $rewriteOk = !empty($st['rewrite_rules_present']);

        echo '<h2 style="margin-top:18px;">' . esc_html__('Self-test results', 'wp-seo-next') . '</h2>';
        if ($when !== '') {
            echo '<p class="description">' . esc_html__('Last run:', 'wp-seo-next') . ' <code>' . esc_html($when) . '</code></p>';
        }

        if (!$sitemapEnabled) {
            echo '<div class="notice notice-warning"><p>' . esc_html__('Sitemap module is disabled, so endpoints will likely return 404. Enable it in WP SEO Next → Settings → Modules or turn off Compatibility mode.', 'wp-seo-next') . '</p></div>';
        }

        if (!$rewriteOk) {
            echo '<div class="notice notice-warning"><p>' . esc_html__('Rewrite rules for sitemaps were not found in WordPress. /sitemap.xml should still work via fallback, but you should flush rewrite rules.', 'wp-seo-next') . '</p></div>';
        }

        $rows = isset($st['results']) && is_array($st['results']) ? $st['results'] : [];

        echo '<table class="widefat striped" style="max-width: 980px;">';
        echo '<thead><tr>'
            . '<th style="width:220px;">' . esc_html__('Endpoint', 'wp-seo-next') . '</th>'
            . '<th style="width:80px;">' . esc_html__('HTTP', 'wp-seo-next') . '</th>'
            . '<th style="width:180px;">' . esc_html__('Content-Type', 'wp-seo-next') . '</th>'
            . '<th>' . esc_html__('Snippet', 'wp-seo-next') . '</th>'
            . '</tr></thead><tbody>';

        foreach ($rows as $r) {
            if (!is_array($r)) {
                continue;
            }
            $label = isset($r['label']) ? (string) $r['label'] : '';
            $url = isset($r['url']) ? (string) $r['url'] : '';
            $code = isset($r['code']) ? (int) $r['code'] : 0;
            $ct = isset($r['content_type']) ? (string) $r['content_type'] : '';
            $snip = isset($r['snip']) ? (string) $r['snip'] : '';
            $err = isset($r['error']) ? (string) $r['error'] : '';
            $ok = !empty($r['ok']);

            echo '<tr>';
            echo '<td><strong>' . esc_html($label) . '</strong><div class="description" style="margin-top:2px;"><a href="' . esc_url($url) . '" target="_blank" rel="noopener">' . esc_html($url) . '</a></div></td>';
            echo '<td>' . ($ok ? '<span class="wpsn-pill ok">OK</span> ' : '<span class="wpsn-pill bad">BAD</span> ') . esc_html((string) $code) . '</td>';
            echo '<td><code>' . esc_html($ct) . '</code></td>';
            echo '<td style="font-family:monospace; font-size:12px;">' . esc_html($err !== '' ? $err : $snip) . '</td>';
            echo '</tr>';
        }

        echo '</tbody></table>';

        echo '<p class="description" style="max-width:980px; margin-top:10px;">';
        echo esc_html__('If /sitemap.xml returns HTML instead of XML, exclude sitemap URLs from any caching/minification plugins, and ensure no security plugin blocks *.xml endpoints.', 'wp-seo-next');
        echo '</p>';
    }

    private function renderUrlRow(string $label, string $url, string $hint = ''): void
    {
        $url = trim($url);
        echo '<tr>';
        echo '<td style="width: 220px;"><strong>' . esc_html($label) . '</strong></td>';
        echo '<td>';
        if ($url !== '') {
            echo '<a href="' . esc_url($url) . '" target="_blank" rel="noopener">' . esc_html($url) . '</a>';
        } else {
            echo '<em>' . esc_html__('n/a', 'wp-seo-next') . '</em>';
        }
        if ($hint !== '') {
            echo '<div class="description" style="margin-top:2px;">' . esc_html($hint) . '</div>';
        }
        echo '</td>';
        echo '</tr>';
    }

    private function renderStatusRow(string $label, string $value): void
    {
        echo '<tr>';
        echo '<td style="width: 260px;"><strong>' . esc_html($label) . '</strong></td>';
        echo '<td>' . esc_html($value) . '</td>';
        echo '</tr>';
    }
}
