<?php

namespace WPSN\Modules\Robots\Admin;

use WPSN\Core\Capabilities;
use WPSN\Core\Container;
use WPSN\Core\Http\HttpClient;
use WPSN\Core\ModuleRegistry;
use WPSN\Core\Settings;

defined('ABSPATH') || exit;

/**
 * Robots Center
 *
 * Users often ask "where is robots.txt" and expect an easy place to manage presets.
 * This page provides:
 * - Link to /robots.txt
 * - Live preview of the generated robots.txt output
 * - Custom rules editor (validated on save)
 * - One-click preset: Block AI bots (via robots.txt)
 */
final class RobotsAdmin
{
    public const PAGE_SLUG = 'wpsn-robots';

    private const SELFTEST_TRANSIENT = 'wpsn_robots_selftest';

    private const PRESET_AI_BLOCK_START = '# WPSN Preset: Block AI bots (start)';
    private const PRESET_AI_BLOCK_END   = '# WPSN Preset: Block AI bots (end)';

    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_robots_save', [$this, 'handleSave']);
        add_action('admin_post_wpsn_robots_apply_ai_block', [$this, 'handleApplyAiBlock']);
        add_action('admin_post_wpsn_robots_remove_ai_block', [$this, 'handleRemoveAiBlock']);

        add_action('admin_post_wpsn_robots_self_test', [$this, 'handleSelfTest']);
        add_action('admin_post_wpsn_robots_self_test_clear', [$this, 'handleSelfTestClear']);

        // Managed Disallow/Allow rules.
        add_action('admin_post_wpsn_robots_disallow_add', [$this, 'handleDisallowAdd']);
        add_action('admin_post_wpsn_robots_disallow_toggle', [$this, 'handleDisallowToggle']);
        add_action('admin_post_wpsn_robots_disallow_delete', [$this, 'handleDisallowDelete']);

        // Structured User-agent rules.
        add_action('admin_post_wpsn_robots_agent_add', [$this, 'handleAgentAdd']);
        add_action('admin_post_wpsn_robots_agent_toggle', [$this, 'handleAgentToggle']);
        add_action('admin_post_wpsn_robots_agent_delete', [$this, 'handleAgentDelete']);
        add_action('admin_post_wpsn_robots_agent_preset', [$this, 'handleAgentPreset']);
    }

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

        $line = isset($_POST['line']) ? sanitize_text_field((string) wp_unslash($_POST['line'])) : '';
        $note = isset($_POST['note']) ? sanitize_text_field((string) wp_unslash($_POST['note'])) : '';
        $line = trim($line);
        $note = trim($note);

        if ($line === '') {
            wp_safe_redirect(admin_url('tools.php?page=' . self::PAGE_SLUG . '&wpsn_notice=disallow_invalid'));
            exit;
        }

        /** @var \WPSN\Admin\SettingsRepository $repo */
        $repo = new \WPSN\Admin\SettingsRepository();
        $all = $repo->getAll();

        $added = \WPSN\Modules\Robots\RobotsDisallowRules::addLinesToAll($all, [$line], 'robots_center', $note);
        $repo->update($all);

        $notice = $added > 0 ? 'disallow_added' : 'disallow_noop';
        wp_safe_redirect(admin_url('tools.php?page=' . self::PAGE_SLUG . '&wpsn_notice=' . $notice));
        exit;
    }

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

        $id = isset($_GET['id']) ? sanitize_text_field((string) wp_unslash($_GET['id'])) : '';
        $enable = isset($_GET['enable']) ? (int) $_GET['enable'] : 1;

        /** @var \WPSN\Admin\SettingsRepository $repo */
        $repo = new \WPSN\Admin\SettingsRepository();
        $all = $repo->getAll();

        $ok = \WPSN\Modules\Robots\RobotsDisallowRules::toggle($all, $id, $enable === 1);
        if ($ok) {
            $repo->update($all);
        }

        wp_safe_redirect(admin_url('tools.php?page=' . self::PAGE_SLUG . '&wpsn_notice=' . ($ok ? 'disallow_toggled' : 'disallow_invalid')));
        exit;
    }

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

        $id = isset($_GET['id']) ? sanitize_text_field((string) wp_unslash($_GET['id'])) : '';

        /** @var \WPSN\Admin\SettingsRepository $repo */
        $repo = new \WPSN\Admin\SettingsRepository();
        $all = $repo->getAll();

        $ok = \WPSN\Modules\Robots\RobotsDisallowRules::delete($all, $id);
        if ($ok) {
            $repo->update($all);
        }

        wp_safe_redirect(admin_url('tools.php?page=' . self::PAGE_SLUG . '&wpsn_notice=' . ($ok ? 'disallow_deleted' : 'disallow_invalid')));
        exit;
    }

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

        $userAgent = isset($_POST['user_agent']) ? sanitize_text_field((string) wp_unslash($_POST['user_agent'])) : '';
        $rules = isset($_POST['rules']) ? (string) wp_unslash($_POST['rules']) : '';
        $note = isset($_POST['note']) ? sanitize_text_field((string) wp_unslash($_POST['note'])) : '';

        /** @var \WPSN\Admin\SettingsRepository $repo */
        $repo = $this->container->get('settings_repo');
        $all = $repo->getAll();

        $added = \WPSN\Modules\Robots\RobotsUserAgentRules::addGroup(
            $all,
            $userAgent,
            \WPSN\Modules\Robots\RobotsUserAgentRules::normalizeLinesFromText($rules),
            'robots_center',
            $note
        );

        if ($added) {
            $repo->update($all);
        }

        wp_safe_redirect(admin_url('tools.php?page=' . self::PAGE_SLUG . '&wpsn_notice=' . ($added ? 'agent_added' : 'agent_invalid')));
        exit;
    }

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

        $id = isset($_GET['id']) ? sanitize_text_field((string) wp_unslash($_GET['id'])) : '';
        $enable = isset($_GET['enable']) ? (int) $_GET['enable'] : 1;

        /** @var \WPSN\Admin\SettingsRepository $repo */
        $repo = $this->container->get('settings_repo');
        $all = $repo->getAll();

        $ok = \WPSN\Modules\Robots\RobotsUserAgentRules::toggle($all, $id, $enable === 1);
        if ($ok) {
            $repo->update($all);
        }

        wp_safe_redirect(admin_url('tools.php?page=' . self::PAGE_SLUG . '&wpsn_notice=' . ($ok ? 'agent_toggled' : 'agent_invalid')));
        exit;
    }

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

        $id = isset($_GET['id']) ? sanitize_text_field((string) wp_unslash($_GET['id'])) : '';

        /** @var \WPSN\Admin\SettingsRepository $repo */
        $repo = $this->container->get('settings_repo');
        $all = $repo->getAll();

        $ok = \WPSN\Modules\Robots\RobotsUserAgentRules::delete($all, $id);
        if ($ok) {
            $repo->update($all);
        }

        wp_safe_redirect(admin_url('tools.php?page=' . self::PAGE_SLUG . '&wpsn_notice=' . ($ok ? 'agent_deleted' : 'agent_invalid')));
        exit;
    }

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

        $presetKey = isset($_POST['preset_key']) ? sanitize_key((string) wp_unslash($_POST['preset_key'])) : '';

        /** @var \WPSN\Admin\SettingsRepository $repo */
        $repo = $this->container->get('settings_repo');
        $all = $repo->getAll();

        $ok = \WPSN\Modules\Robots\RobotsUserAgentRules::addPreset($all, $presetKey);
        if ($ok) {
            $repo->update($all);
        }

        wp_safe_redirect(admin_url('tools.php?page=' . self::PAGE_SLUG . '&wpsn_notice=' . ($ok ? 'agent_preset_added' : 'agent_invalid')));
        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_robots_self_test');

        $res = $this->runSelfTest();
        set_transient(self::SELFTEST_TRANSIENT, $res, 6 * HOUR_IN_SECONDS);

        wp_safe_redirect(admin_url('tools.php?page=' . self::PAGE_SLUG . '&wpsn_notice=self_test_ran'));
        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_robots_self_test_clear');

        delete_transient(self::SELFTEST_TRANSIENT);

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

    /**
     * @return array<string,mixed>
     */
    private function runSelfTest(): array
    {
        $url = home_url('/robots.txt');

        $out = [
            'tested_at_utc' => gmdate('c'),
            'url' => $url,
            'ok' => false,
            'http_code' => 0,
            'error' => '',
            'content_length' => 0,
            'content_sample' => '',
            'has_user_agent' => false,
            'has_sitemap' => false,
            'ai_preset_present' => false,
        ];

        $resp = HttpClient::get($url, [
            'timeout' => 6,
            'redirection' => 3,
            'user-agent' => 'WPSEO-Next/' . (defined('WPSN_VERSION') ? (string) WPSN_VERSION : 'dev'),
        ], [
            'service' => 'loopback',
            'context' => 'robots_selftest',
            'retries' => 0,
        ]);

        if (is_wp_error($resp)) {
            $out['error'] = $resp->get_error_message();
            return $out;
        }

        $code = (int) wp_remote_retrieve_response_code($resp);
        $body = (string) wp_remote_retrieve_body($resp);

        $out['http_code'] = $code;
        $out['content_length'] = strlen($body);

        $sample = $body;
        if (strlen($sample) > 1000) {
            $sample = substr($sample, 0, 1000) . "\n…";
        }
        $out['content_sample'] = $sample;

        $out['has_user_agent'] = (stripos($body, 'user-agent:') !== false);
        $out['has_sitemap'] = (stripos($body, 'sitemap:') !== false);
        $out['ai_preset_present'] = (strpos($body, self::PRESET_AI_BLOCK_START) !== false);

        $out['ok'] = ($code >= 200 && $code < 300 && trim($body) !== '' && $out['has_user_agent']);
        if (!$out['ok'] && $out['error'] === '' && trim($body) === '') {
            $out['error'] = __('Empty robots.txt response body.', 'wp-seo-next');
        }

        return $out;
    }

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

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

        $append = !empty($_POST['wpsn_append_sitemap']) ? true : false;
        $custom = isset($_POST['wpsn_custom']) ? (string) wp_unslash($_POST['wpsn_custom']) : '';

        /** @var \WPSN\Admin\SettingsRepository $repo */
        $repo = $this->container->get('settings_repo');
        $all = $repo->getAll();
        if (!isset($all['robots_txt']) || !is_array($all['robots_txt'])) {
            $all['robots_txt'] = [];
        }
        $all['robots_txt']['append_sitemap'] = $append;
        $all['robots_txt']['custom'] = $custom;
        $repo->update($all);

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

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

        $custom = (string) $this->settings->get('robots_txt.custom', '');
        $custom = $this->removePresetBlock($custom, self::PRESET_AI_BLOCK_START, self::PRESET_AI_BLOCK_END);
        $custom = trim($custom);

        $block = $this->aiBlockPresetBlock();

        $new = $custom;
        if ($new !== '') {
            $new .= "\n\n";
        }
        $new .= $block;

        /** @var \WPSN\Admin\SettingsRepository $repo */
        $repo = $this->container->get('settings_repo');
        $all = $repo->getAll();
        if (!isset($all['robots_txt']) || !is_array($all['robots_txt'])) {
            $all['robots_txt'] = [];
        }
        $all['robots_txt']['custom'] = $new;
        $repo->update($all);

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

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

        $custom = (string) $this->settings->get('robots_txt.custom', '');
        $custom = $this->removePresetBlock($custom, self::PRESET_AI_BLOCK_START, self::PRESET_AI_BLOCK_END);

        /** @var \WPSN\Admin\SettingsRepository $repo */
        $repo = $this->container->get('settings_repo');
        $all = $repo->getAll();
        if (!isset($all['robots_txt']) || !is_array($all['robots_txt'])) {
            $all['robots_txt'] = [];
        }
        $all['robots_txt']['custom'] = $custom;
        $repo->update($all);

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

    private function aiBlockPresetBlock(): string
    {
        // Keep the list minimal and editable by users (they can tweak in Custom rules).
        $agents = [
            'GPTBot',
            'ChatGPT-User',
        ];

        $out = [];
        $out[] = self::PRESET_AI_BLOCK_START;
        $out[] = '# This preset is best-effort. You can edit/remove it safely.';

        foreach ($agents as $ua) {
            $ua = sanitize_text_field((string) $ua);
            if ($ua === '') {
                continue;
            }
            $out[] = 'User-agent: ' . $ua;
            $out[] = 'Disallow: /';
            $out[] = '';
        }

        // Remove trailing blank.
        while (!empty($out) && trim((string) end($out)) === '') {
            array_pop($out);
        }
        $out[] = self::PRESET_AI_BLOCK_END;

        return trim(implode("\n", $out));
    }

    private function removePresetBlock(string $custom, string $start, string $end): string
    {
        $custom = trim($custom);
        if ($custom === '') {
            return '';
        }

        $lines = preg_split('/\r\n|\r|\n/', $custom) ?: [];
        $out = [];
        $skip = false;

        foreach ($lines as $line) {
            $t = trim((string) $line);
            if ($t === $start) {
                $skip = true;
                continue;
            }
            if ($t === $end) {
                $skip = false;
                continue;
            }
            if ($skip) {
                continue;
            }
            $out[] = (string) $line;
        }

        $text = trim(implode("\n", $out));
        // Normalize excessive blank lines.
        $text = preg_replace("/\n{3,}/", "\n\n", $text) ?? $text;

        return trim($text);
    }

    private function robotsTxtPreview(): string
    {
        // Best-effort preview.
        // We avoid outbound HTTP and we do NOT call do_robots() because it sends headers.
        $publicRaw = get_option('blog_public');
        $public = ($publicRaw !== '0');

        $home = wp_parse_url(home_url());
        $path = '';
        if (is_array($home) && isset($home['path'])) {
            $path = (string) $home['path'];
        }
        $path = trim($path);
        $path = $path === '' ? '' : '/' . trim($path, '/');

        $out = "User-agent: *\n";

        if (!$public) {
            $out .= "Disallow: /\n";
        } else {
            $out .= "Disallow: {$path}/wp-admin/\n";
            $out .= "Allow: {$path}/wp-admin/admin-ajax.php\n";
        }

        // Apply filters (including WP SEO Next robots module, if enabled).
        $out = (string) apply_filters('robots_txt', $out, $public);

        $out = trim($out);
        return $out !== '' ? $out : "User-agent: *\nDisallow:";
    }

    private function presetIsApplied(string $custom, string $start, string $end): bool
    {
        $custom = trim($custom);
        if ($custom === '') {
            return false;
        }
        return (strpos($custom, $start) !== false) && (strpos($custom, $end) !== false);
    }

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

        /** @var ModuleRegistry $modules */
        $modules = $this->container->get('modules');

        $robotsEnabled = $modules instanceof ModuleRegistry ? $modules->isEnabled('robots') : true;
        $sitemapEnabled = $modules instanceof ModuleRegistry ? $modules->isEnabled('sitemap') : true;

        $appendSitemap = (bool) $this->settings->get('robots_txt.append_sitemap', true);
        $custom = (string) $this->settings->get('robots_txt.custom', '');

        $robotsUrl = home_url('/robots.txt');
        $sitemapUrl = home_url('/sitemap.xml');

        $disableOutputStored = (bool) $this->settings->get('compatibility.disable_output', false);
        $autoDisableOnConflict = (bool) $this->settings->get('compatibility.auto_disable_output_on_conflict', false);
        $disableOutputConst = defined('WPSN_DISABLE_OUTPUT') && (bool) WPSN_DISABLE_OUTPUT;
        $disableOutputEffective = $disableOutputConst || $disableOutputStored || ($autoDisableOnConflict && \WPSN\Core\CompatibilityDetector::hasSeoConflict());

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

        echo '<div class="wrap">';
        echo '<h1>' . esc_html__('Robots', 'wp-seo-next') . '</h1>';
        echo '<p class="description">' . esc_html__('Manage your virtual robots.txt rules. WordPress serves robots.txt dynamically at /robots.txt.', 'wp-seo-next') . '</p>';

        if ($notice !== '') {
            $type = 'info';
            $msg = '';
            switch ($notice) {
                case 'saved':
                    $type = 'success';
                    $msg = __('Robots settings saved.', 'wp-seo-next');
                    break;
                case 'disallow_added':
                    $type = 'success';
                    $msg = __('Managed robots rule added.', 'wp-seo-next');
                    break;
                case 'disallow_noop':
                    $type = 'info';
                    $msg = __('Rule already exists (no changes).', 'wp-seo-next');
                    break;
                case 'disallow_toggled':
                    $type = 'success';
                    $msg = __('Managed robots rule updated.', 'wp-seo-next');
                    break;
                case 'disallow_deleted':
                    $type = 'success';
                    $msg = __('Managed robots rule deleted.', 'wp-seo-next');
                    break;
                case 'disallow_invalid':
                    $type = 'error';
                    $msg = __('Invalid rule.', 'wp-seo-next');
                    break;
                case 'agent_added':
                    $type = 'success';
                    $msg = __('User-agent rule added.', 'wp-seo-next');
                    break;
                case 'agent_toggled':
                    $type = 'success';
                    $msg = __('User-agent rule updated.', 'wp-seo-next');
                    break;
                case 'agent_deleted':
                    $type = 'success';
                    $msg = __('User-agent rule deleted.', 'wp-seo-next');
                    break;
                case 'agent_preset_added':
                    $type = 'success';
                    $msg = __('Crawler preset added.', 'wp-seo-next');
                    break;
                case 'agent_invalid':
                    $type = 'error';
                    $msg = __('Invalid User-agent block.', 'wp-seo-next');
                    break;
                case 'ai_block_applied':
                    $type = 'success';
                    $msg = __('AI bots block preset applied to custom robots.txt rules.', 'wp-seo-next');
                    break;
                case 'ai_block_removed':
                    $type = 'success';
                    $msg = __('AI bots block preset removed from custom robots.txt rules.', 'wp-seo-next');
                    break;
                case 'self_test_ran':
                    $type = 'success';
                    $msg = __('Robots self-test completed.', 'wp-seo-next');
                    break;
                case 'self_test_cleared':
                    $type = 'success';
                    $msg = __('Robots self-test results cleared.', 'wp-seo-next');
                    break;
            }
            if ($msg !== '') {
                echo '<div class="notice notice-' . esc_attr($type) . '"><p>' . esc_html($msg) . '</p></div>';
            }
        }

        if ($disableOutputEffective || !$robotsEnabled) {
            echo '<div class="notice notice-warning"><p>' . esc_html__('Robots module output is currently disabled (Compatibility mode / Output OFF). WordPress will still serve /robots.txt, but WP SEO Next custom rules may not be appended.', 'wp-seo-next') . '</p></div>';
        }

        echo '<div class="wpsn-grid" style="display:grid;grid-template-columns: 1fr 1fr;gap:16px;max-width:1200px;">';

        // Left: actions + settings.
        echo '<div class="postbox" style="padding:16px;">';
        echo '<h2 style="margin-top:0;">' . esc_html__('Quick links', 'wp-seo-next') . '</h2>';
        echo '<p>';
        echo '<a class="button button-primary" href="' . esc_url($robotsUrl) . '" target="_blank" rel="noopener">' . esc_html__('Open robots.txt', 'wp-seo-next') . '</a> ';
        if ($sitemapEnabled) {
            echo '<a class="button" href="' . esc_url($sitemapUrl) . '" target="_blank" rel="noopener">' . esc_html__('Open sitemap.xml', 'wp-seo-next') . '</a>';
        }
        echo ' <a class="button" href="' . esc_url(admin_url('tools.php?page=wpsn-url-rules')) . '">' . esc_html__('URL Rules (noindex)', 'wp-seo-next') . '</a>';
        echo '</p>';

        // Self-test (loopback request).
        $st = get_transient(self::SELFTEST_TRANSIENT);
        $st = is_array($st) ? $st : [];

        echo '<h3 style="margin-top:14px;">' . esc_html__('Self-test', 'wp-seo-next') . '</h3>';
        echo '<p class="description">' . esc_html__('Checks whether /robots.txt is reachable from WordPress (loopback request). Some hosts block loopback; in that case the test may fail even if robots.txt works for real visitors.', 'wp-seo-next') . '</p>';

        echo '<div style="display:flex; gap:8px; align-items:center; flex-wrap:wrap;">';
        echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" style="display:inline;">';
        echo '<input type="hidden" name="action" value="wpsn_robots_self_test" />';
        wp_nonce_field('wpsn_robots_self_test');
        submit_button(__('Run self-test', 'wp-seo-next'), 'secondary', 'submit', false);
        echo '</form>';
        echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" style="display:inline;">';
        echo '<input type="hidden" name="action" value="wpsn_robots_self_test_clear" />';
        wp_nonce_field('wpsn_robots_self_test_clear');
        submit_button(__('Clear', 'wp-seo-next'), 'secondary', 'submit', false);
        echo '</form>';
        echo '</div>';

        if (!empty($st)) {
            $ok = !empty($st['ok']);
            $code = isset($st['http_code']) ? (int) $st['http_code'] : 0;
            $testedAt = isset($st['tested_at_utc']) ? sanitize_text_field((string) $st['tested_at_utc']) : '';
            $err = isset($st['error']) ? sanitize_text_field((string) $st['error']) : '';
            $len = isset($st['content_length']) ? (int) $st['content_length'] : 0;
            $ua = !empty($st['has_user_agent']);
            $sm = !empty($st['has_sitemap']);

            echo '<table class="widefat striped" style="margin-top:10px;"><tbody>';
            echo '<tr><th style="width:220px;">' . esc_html__('Result', 'wp-seo-next') . '</th><td>' . ($ok ? '<span style="color:green;font-weight:600;">OK</span>' : '<span style="color:#b32d2e;font-weight:600;">FAIL</span>') . '</td></tr>';
            echo '<tr><th>' . esc_html__('Tested at (UTC)', 'wp-seo-next') . '</th><td><code>' . esc_html($testedAt) . '</code></td></tr>';
            echo '<tr><th>' . esc_html__('HTTP code', 'wp-seo-next') . '</th><td><code>' . esc_html((string) $code) . '</code></td></tr>';
            echo '<tr><th>' . esc_html__('Body length', 'wp-seo-next') . '</th><td><code>' . esc_html((string) $len) . '</code></td></tr>';
            echo '<tr><th>' . esc_html__('Contains User-agent', 'wp-seo-next') . '</th><td>' . ($ua ? esc_html__('Yes', 'wp-seo-next') : esc_html__('No', 'wp-seo-next')) . '</td></tr>';
            echo '<tr><th>' . esc_html__('Contains Sitemap', 'wp-seo-next') . '</th><td>' . ($sm ? esc_html__('Yes', 'wp-seo-next') : esc_html__('No', 'wp-seo-next')) . '</td></tr>';
            if ($err !== '') {
                echo '<tr><th>' . esc_html__('Error', 'wp-seo-next') . '</th><td><code>' . esc_html($err) . '</code></td></tr>';
            }
            echo '</tbody></table>';

            if (!empty($st['content_sample'])) {
                $sample = (string) $st['content_sample'];
                echo '<details style="margin-top:8px;"><summary>' . esc_html__('Response sample', 'wp-seo-next') . '</summary>';
                echo '<pre style="background:#fff;border:1px solid #ccd0d4;padding:10px;max-height:240px;overflow:auto;">' . esc_html($sample) . '</pre>';
                echo '</details>';
            }
        }

        echo '<hr />';

        echo '<h2>' . esc_html__('Robots.txt settings', 'wp-seo-next') . '</h2>';

        // Managed rules (structured).
        $managed = $this->settings->get('robots_txt.disallow_rules', []);
        $managed = is_array($managed) ? array_values(array_filter($managed, static fn($r) => is_array($r))) : [];

        echo '<h3 style="margin-top:10px;">' . esc_html__('Managed rules (Allow/Disallow)', 'wp-seo-next') . '</h3>';
        echo '<p class="description">' . esc_html__('One-click actions from Site Crawl and Crawl Budget add rules here to keep “Custom rules” clean. These rules are appended as a separate “User-agent: *” section in robots.txt.', 'wp-seo-next') . '</p>';
        echo '<p class="description">' . esc_html__('Tip: If you just added a rule from another screen, come back here to disable/delete it instead of editing robots.txt manually.', 'wp-seo-next') . '</p>';

        // Add form.
        echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin:10px 0;">';
        echo '<input type="hidden" name="action" value="wpsn_robots_disallow_add" />';
        wp_nonce_field('wpsn_robots_disallow_add');
        echo '<input type="text" class="regular-text" name="line" placeholder="Disallow: /search/" />';
        echo '<input type="text" class="regular-text" name="note" placeholder="' . esc_attr__('Optional note (e.g. Crawl Budget preset)', 'wp-seo-next') . '" />';
        submit_button(__('Add rule', 'wp-seo-next'), 'secondary', 'submit', false);
        echo '</form>';

        if (empty($managed)) {
            echo '<p><em>' . esc_html__('No managed rules yet.', 'wp-seo-next') . '</em></p>';
        } else {
            echo '<table class="widefat striped" style="margin-top:8px;">';
            echo '<thead><tr>';
            echo '<th style="width:70px;">' . esc_html__('On', 'wp-seo-next') . '</th>';
            echo '<th>' . esc_html__('Rule', 'wp-seo-next') . '</th>';
            echo '<th style="width:140px;">' . esc_html__('Source', 'wp-seo-next') . '</th>';
            echo '<th>' . esc_html__('Note', 'wp-seo-next') . '</th>';
            echo '<th style="width:160px;">' . esc_html__('Actions', 'wp-seo-next') . '</th>';
            echo '</tr></thead><tbody>';

            foreach ($managed as $r) {
                $id = isset($r['id']) ? sanitize_text_field((string) $r['id']) : '';
                $enabled = !empty($r['enabled']);
                $line = isset($r['line']) ? sanitize_text_field((string) $r['line']) : '';
                $source = isset($r['source']) ? sanitize_key((string) $r['source']) : '';
                $note = isset($r['note']) ? sanitize_text_field((string) $r['note']) : '';

                $toggleUrl = add_query_arg([
                    'action' => 'wpsn_robots_disallow_toggle',
                    'id' => $id,
                    'enable' => $enabled ? 0 : 1,
                ], admin_url('admin-post.php'));
                $toggleUrl = wp_nonce_url($toggleUrl, 'wpsn_robots_disallow_toggle');

                $deleteUrl = add_query_arg([
                    'action' => 'wpsn_robots_disallow_delete',
                    'id' => $id,
                ], admin_url('admin-post.php'));
                $deleteUrl = wp_nonce_url($deleteUrl, 'wpsn_robots_disallow_delete');

                echo '<tr>';
                echo '<td>' . ($enabled ? '<span class="dashicons dashicons-yes" style="color:green;"></span>' : '<span class="dashicons dashicons-no" style="color:#b32d2e;"></span>') . '</td>';
                echo '<td><code>' . esc_html($line) . '</code></td>';
                echo '<td><code>' . esc_html($source !== '' ? $source : '-') . '</code></td>';
                echo '<td>' . esc_html($note !== '' ? $note : '-') . '</td>';
                echo '<td>';
                echo '<a class="button button-small" href="' . esc_url($toggleUrl) . '">' . esc_html($enabled ? __('Disable', 'wp-seo-next') : __('Enable', 'wp-seo-next')) . '</a> ';
                echo '<a class="button button-small" href="' . esc_url($deleteUrl) . '" onclick="return confirm(' . json_encode(__('Delete this rule?', 'wp-seo-next')) . ');">' . esc_html__('Delete', 'wp-seo-next') . '</a>';
                echo '</td>';
                echo '</tr>';
            }

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

        echo '<hr />';

        $agentGroups = $this->settings->get('robots_txt.agent_groups', []);
        $agentGroups = is_array($agentGroups) ? array_values(array_filter($agentGroups, static fn($group) => is_array($group))) : [];
        $agentCatalog = \WPSN\Modules\Robots\RobotsUserAgentRules::catalog();
        $delayChoices = \WPSN\Modules\Robots\RobotsUserAgentRules::crawlDelayChoices();
        $delayHelpMap = [
            'default' => \WPSN\Modules\Robots\RobotsUserAgentRules::crawlDelayHelpText(''),
            'google' => \WPSN\Modules\Robots\RobotsUserAgentRules::crawlDelayHelpText('AdsBot-Google'),
            'perplexitybot' => \WPSN\Modules\Robots\RobotsUserAgentRules::crawlDelayHelpText('PerplexityBot'),
            'ccbot' => \WPSN\Modules\Robots\RobotsUserAgentRules::crawlDelayHelpText('CCBot'),
            'ahrefsbot' => \WPSN\Modules\Robots\RobotsUserAgentRules::crawlDelayHelpText('AhrefsBot'),
            'semrushbot' => \WPSN\Modules\Robots\RobotsUserAgentRules::crawlDelayHelpText('SemrushBot'),
        ];

        echo '<h3 style="margin-top:10px;">' . esc_html__('User-agent rules', 'wp-seo-next') . '</h3>';
        echo '<p class="description">' . esc_html__('Create dedicated robots.txt blocks for a specific crawler without hand-editing raw syntax. Use this for cases like CCBot, AdsBot, AhrefsBot, or other crawler-specific Allow / Disallow rules.', 'wp-seo-next') . '</p>';
        echo '<p class="description">' . esc_html__('For advanced directives like Sitemap, Host, or mixed multi-bot layouts, keep using the Custom rules textarea below.', 'wp-seo-next') . '</p>';
        echo '<div class="notice notice-info inline" style="margin:10px 0 14px 0;"><p>' . esc_html__('Crawl-delay is empty by default. Use 1 / 2 / 5 seconds only when you intentionally want to slow a non-Google crawler. Google crawlers ignore Crawl-delay.', 'wp-seo-next') . '</p></div>';

        echo '<div style="display:flex;flex-wrap:wrap;gap:10px;margin:10px 0 14px;">';
        foreach ($agentCatalog as $presetKey => $preset) {
            if (!is_array($preset)) {
                continue;
            }
            $label = isset($preset['label']) ? (string) $preset['label'] : strtoupper((string) $presetKey);
            $purpose = isset($preset['purpose']) ? (string) $preset['purpose'] : '';
            $group = isset($preset['group']) ? (string) $preset['group'] : '';
            $presetUserAgent = isset($preset['user_agent']) ? (string) $preset['user_agent'] : '';
            $delaySummary = \WPSN\Modules\Robots\RobotsUserAgentRules::crawlDelaySummary($presetUserAgent);

            echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" class="card" style="margin:0;padding:12px;min-width:220px;max-width:280px;">';
            echo '<input type="hidden" name="action" value="wpsn_robots_agent_preset" />';
            echo '<input type="hidden" name="preset_key" value="' . esc_attr((string) $presetKey) . '" />';
            wp_nonce_field('wpsn_robots_agent_preset');
            echo '<strong>' . esc_html($label) . '</strong>';
            if ($group !== '') {
                echo '<div class="description" style="margin-top:4px;">' . esc_html($group) . '</div>';
            }
            if ($purpose !== '') {
                echo '<p class="description" style="min-height:36px;">' . esc_html($purpose) . '</p>';
            }
            if ($delaySummary !== '') {
                echo '<p class="description" style="margin-top:-2px;">' . esc_html($delaySummary) . '</p>';
            }
            submit_button(__('Add preset', 'wp-seo-next'), 'secondary', 'submit', false);
            echo '</form>';
        }
        echo '</div>';

        echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '" style="margin:10px 0 14px;">';
        echo '<input type="hidden" name="action" value="wpsn_robots_agent_add" />';
        wp_nonce_field('wpsn_robots_agent_add');
        echo '<div style="display:grid;grid-template-columns:minmax(220px,280px) minmax(280px,1fr);gap:12px;align-items:start;">';
        echo '<div>';
        echo '<p><label for="wpsn_agent_user_agent"><strong>' . esc_html__('User-agent', 'wp-seo-next') . '</strong></label><br />';
        echo '<input type="text" class="regular-text" id="wpsn_agent_user_agent" name="user_agent" placeholder="CCBot" /></p>';
        echo '<p><label for="wpsn_agent_note"><strong>' . esc_html__('Note', 'wp-seo-next') . '</strong></label><br />';
        echo '<input type="text" class="regular-text" id="wpsn_agent_note" name="note" placeholder="' . esc_attr__('Optional note for your team', 'wp-seo-next') . '" /></p>';
        echo '</div>';
        echo '<div>';
        echo '<p><label for="wpsn_agent_rules"><strong>' . esc_html__('Rules', 'wp-seo-next') . '</strong></label></p>';
        echo '<textarea class="large-text" rows="5" id="wpsn_agent_rules" name="rules" placeholder="Disallow: /\nAllow: /public-api/"></textarea>';
        echo '<p class="description">' . esc_html__('Allowed directives here: Disallow, Allow, Crawl-delay. The User-agent line is generated automatically.', 'wp-seo-next') . '</p>';
        echo '<div style="display:flex;flex-wrap:wrap;align-items:center;gap:6px;margin:8px 0 4px;">';
        echo '<span class="description" style="padding-top:2px;">' . esc_html__('Quick delay', 'wp-seo-next') . ':</span>';
        foreach ($delayChoices as $delayChoice) {
            echo '<button type="button" class="button button-small" data-wpsn-crawl-delay="' . esc_attr((string) $delayChoice) . '">' . esc_html(sprintf(__('%ds', 'wp-seo-next'), $delayChoice)) . '</button>';
        }
        echo '<button type="button" class="button button-small" data-wpsn-crawl-delay="">' . esc_html__('Remove delay', 'wp-seo-next') . '</button>';
        echo '</div>';
        echo '<p class="description" id="wpsn_agent_delay_help">' . esc_html($delayHelpMap['default']) . '</p>';
        echo '</div>';
        echo '</div>';
        submit_button(__('Add User-agent rule', 'wp-seo-next'), 'secondary', 'submit', false);
        echo '</form>';

        echo '<script>(function(){';
        echo 'var uaInput=document.getElementById("wpsn_agent_user_agent");';
        echo 'var rulesInput=document.getElementById("wpsn_agent_rules");';
        echo 'var help=document.getElementById("wpsn_agent_delay_help");';
        echo 'var buttons=document.querySelectorAll("[data-wpsn-crawl-delay]");';
        echo 'var map=' . json_encode($delayHelpMap) . ';';
        echo 'function normalizeUa(value){return (value||"").toLowerCase().replace(/\s+/g, "").trim();}';
        echo 'function helpText(){var ua=normalizeUa(uaInput?uaInput.value:"");if(!ua){return map.default||"";}if(ua.indexOf("google")!==-1||ua.indexOf("adsbot")!==-1){return map.google||map.default||"";}if(Object.prototype.hasOwnProperty.call(map, ua)){return map[ua]||map.default||"";}return map.default||"";}';
        echo 'function updateHelp(){if(help){help.textContent=helpText();}}';
        echo 'function setDelay(value){if(!rulesInput){return;}var rows=rulesInput.value.split(/\r?\n/);var out=[];for(var i=0;i<rows.length;i++){var raw=rows[i];var trimmed=(raw||"").trim();if(trimmed!==""&& !/^crawl-delay\s*:/i.test(trimmed)){out.push(raw.replace(/\s+$/,""));}}while(out.length && out[out.length-1]===""){out.pop();}if(value!==""){out.push("Crawl-delay: "+value);}rulesInput.value=out.join("\n");}';
        echo 'for(var i=0;i<buttons.length;i++){buttons[i].addEventListener("click",function(ev){ev.preventDefault();setDelay(this.getAttribute("data-wpsn-crawl-delay")||"");});}';
        echo 'if(uaInput){uaInput.addEventListener("input", updateHelp); updateHelp();}';
        echo '})();</script>';

        if (empty($agentGroups)) {
            echo '<p><em>' . esc_html__('No structured User-agent rules yet.', 'wp-seo-next') . '</em></p>';
        } else {
            echo '<table class="widefat striped" style="margin-top:8px;">';
            echo '<thead><tr>';
            echo '<th style="width:70px;">' . esc_html__('On', 'wp-seo-next') . '</th>';
            echo '<th style="width:190px;">' . esc_html__('User-agent', 'wp-seo-next') . '</th>';
            echo '<th>' . esc_html__('Rules', 'wp-seo-next') . '</th>';
            echo '<th style="width:140px;">' . esc_html__('Source', 'wp-seo-next') . '</th>';
            echo '<th>' . esc_html__('Note', 'wp-seo-next') . '</th>';
            echo '<th style="width:160px;">' . esc_html__('Actions', 'wp-seo-next') . '</th>';
            echo '</tr></thead><tbody>';

            foreach ($agentGroups as $group) {
                $id = isset($group['id']) ? sanitize_text_field((string) $group['id']) : '';
                $enabled = !empty($group['enabled']);
                $userAgent = isset($group['user_agent']) ? \WPSN\Modules\Robots\RobotsUserAgentRules::normalizeUserAgent((string) $group['user_agent']) : '';
                $lines = isset($group['lines']) && is_array($group['lines']) ? \WPSN\Modules\Robots\RobotsUserAgentRules::normalizeLines($group['lines']) : [];
                $source = isset($group['source']) ? sanitize_key((string) $group['source']) : '';
                $note = isset($group['note']) ? sanitize_text_field((string) $group['note']) : '';
                $delayBehavior = \WPSN\Modules\Robots\RobotsUserAgentRules::crawlDelayBehavior($userAgent);
                $delaySummary = \WPSN\Modules\Robots\RobotsUserAgentRules::crawlDelaySummary($userAgent);
                $showDelaySummary = $delayBehavior !== 'generic';
                $hasCrawlDelay = \WPSN\Modules\Robots\RobotsUserAgentRules::hasCrawlDelay($lines);

                $toggleUrl = add_query_arg([
                    'action' => 'wpsn_robots_agent_toggle',
                    'id' => $id,
                    'enable' => $enabled ? 0 : 1,
                ], admin_url('admin-post.php'));
                $toggleUrl = wp_nonce_url($toggleUrl, 'wpsn_robots_agent_toggle');

                $deleteUrl = add_query_arg([
                    'action' => 'wpsn_robots_agent_delete',
                    'id' => $id,
                ], admin_url('admin-post.php'));
                $deleteUrl = wp_nonce_url($deleteUrl, 'wpsn_robots_agent_delete');

                echo '<tr>';
                echo '<td>' . ($enabled ? '<span class="dashicons dashicons-yes" style="color:green;"></span>' : '<span class="dashicons dashicons-no" style="color:#b32d2e;"></span>') . '</td>';
                echo '<td><code>' . esc_html($userAgent !== '' ? $userAgent : '-') . '</code>';
                if ($showDelaySummary && $delaySummary !== '') {
                    $delayColor = ($hasCrawlDelay && $delayBehavior === 'ignored') ? '#b32d2e' : '#646970';
                    echo '<div class="description" style="margin-top:4px;color:' . esc_attr($delayColor) . ';">' . esc_html($delaySummary) . '</div>';
                }
                echo '</td>';
                echo '<td>';
                if (empty($lines)) {
                    echo '—';
                } else {
                    foreach ($lines as $line) {
                        echo '<code style="display:block;margin:2px 0;">' . esc_html($line) . '</code>';
                    }
                }
                echo '</td>';
                echo '<td><code>' . esc_html($source !== '' ? $source : 'manual') . '</code></td>';
                echo '<td>' . esc_html($note !== '' ? $note : '-') . '</td>';
                echo '<td>';
                echo '<a class="button button-small" href="' . esc_url($toggleUrl) . '">' . esc_html($enabled ? __('Disable', 'wp-seo-next') : __('Enable', 'wp-seo-next')) . '</a> ';
                echo '<a class="button button-small" href="' . esc_url($deleteUrl) . '" onclick="return confirm(' . json_encode(__('Delete this User-agent block?', 'wp-seo-next')) . ');">' . esc_html__('Delete', 'wp-seo-next') . '</a>';
                echo '</td>';
                echo '</tr>';
            }

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

        echo '<hr />';

        echo '<form method="post" action="' . esc_url(admin_url('admin-post.php')) . '">';
        echo '<input type="hidden" name="action" value="wpsn_robots_save" />';
        wp_nonce_field('wpsn_robots_save');

        echo '<p><label>';
        echo '<input type="checkbox" name="wpsn_append_sitemap" value="1" ' . checked($appendSitemap, true, false) . ' /> ';
        echo esc_html__('Append Sitemap directive (if sitemap module enabled)', 'wp-seo-next');
        echo '</label></p>';

        echo '<p><label for="wpsn_custom"><strong>' . esc_html__('Custom rules (validated)', 'wp-seo-next') . '</strong></label></p>';
        echo '<textarea class="large-text" rows="12" id="wpsn_custom" name="wpsn_custom" placeholder="# Examples:
User-agent: *
Disallow: /wp-admin/
Allow: /wp-admin/admin-ajax.php">' . esc_textarea($custom) . '</textarea>';
        echo '<p class="description">' . esc_html__('Allowed directives: User-agent, Disallow, Allow, Sitemap, Crawl-delay, Host. Google crawlers ignore Crawl-delay, so use it only for selected non-Google bots.', 'wp-seo-next') . '</p>';

        echo '<p><button class="button button-primary" type="submit">' . esc_html__('Save', 'wp-seo-next') . '</button></p>';
        echo '</form>';

        echo '<hr />';

        // Presets.
        $aiApplied = $this->presetIsApplied($custom, self::PRESET_AI_BLOCK_START, self::PRESET_AI_BLOCK_END);

        echo '<h2>' . esc_html__('Presets', 'wp-seo-next') . '</h2>';
        echo '<p class="description">' . esc_html__('Presets modify the Custom rules block above. They are optional and safe to remove.', 'wp-seo-next') . '</p>';

        echo '<div style="display:flex;align-items:center;gap:8px;">';
        echo '<strong>' . esc_html__('Block AI bots', 'wp-seo-next') . '</strong>';
        echo $aiApplied ? '<span class="dashicons dashicons-yes" style="color:green;"></span>' : '<span class="dashicons dashicons-minus" style="color:#999;"></span>';
        echo '</div>';

        echo '<p>';
        if (!$aiApplied) {
            $url = add_query_arg(['action' => 'wpsn_robots_apply_ai_block'], admin_url('admin-post.php'));
            $url = wp_nonce_url($url, 'wpsn_robots_apply_ai_block');
            echo '<a class="button" href="' . esc_url($url) . '">' . esc_html__('Apply preset', 'wp-seo-next') . '</a>';
        } else {
            $url = add_query_arg(['action' => 'wpsn_robots_remove_ai_block'], admin_url('admin-post.php'));
            $url = wp_nonce_url($url, 'wpsn_robots_remove_ai_block');
            echo '<a class="button" href="' . esc_url($url) . '">' . esc_html__('Remove preset', 'wp-seo-next') . '</a>';
        }
        echo '</p>';

        echo '<p class="description">' . esc_html__('Note: robots.txt rules are respected by well-behaved crawlers, but not a security mechanism.', 'wp-seo-next') . '</p>';

        echo '</div>'; // left box

        // Right: preview.
        echo '<div class="postbox" style="padding:16px;">';
        echo '<h2 style="margin-top:0;">' . esc_html__('robots.txt preview', 'wp-seo-next') . '</h2>';
        echo '<p class="description">' . esc_html__('This is the effective robots.txt output generated by WordPress + WP SEO Next filters (best-effort).', 'wp-seo-next') . '</p>';

        $preview = $this->robotsTxtPreview();
        echo '<pre style="background:#fff;border:1px solid #ccd0d4;padding:12px;max-height:520px;overflow:auto;">' . esc_html($preview) . '</pre>';

        echo '</div>'; // right

        echo '</div>'; // grid

        echo '</div>'; // wrap
    }
}
