<?php
// 引入自定义文章类型与分类注册
require_once get_template_directory() . '/inc/register-post-type.php';

// 后台分类图标选择器(Bootstrap Icons)
if (is_admin()) {
    require_once get_template_directory() . '/inc/admin-category-icons.php';
}

register_nav_menus(['primary' => '主导航菜单']);

function theme_enqueue_scripts() {
    wp_dequeue_style('wp-block-library');
    wp_dequeue_style('wp-block-library-theme');
    wp_dequeue_style('global-styles');

    wp_enqueue_style('tailwindcss', 'https://cdn.tailwindcss.com', array(), '3', 'all');
    // Bootstrap Icons(MIT 协议,免费可商用),自托管避免外网 CDN 不可用
    wp_enqueue_style('bootstrap-icons', get_template_directory_uri() . '/assets/vendor/bootstrap-icons/bootstrap-icons.min.css', array(), '1.11.3', 'all');
    wp_enqueue_style('theme-style', get_stylesheet_uri(), array('tailwindcss', 'bootstrap-icons'), '1.0', 'all');

    // 仅在首页加载导航主逻辑,避免在投稿页因元素缺失报错
    if (is_front_page() || is_home()) {
        wp_enqueue_script('theme-app', get_template_directory_uri() . '/assets/js/app.js', array(), '1.1', true);
        wp_localize_script('theme-app', 'WP_API_Settings', array(
            'root'          => esc_url_raw(rest_url()),
            'nonce'         => wp_create_nonce('wp_rest'),
            'themeDir'      => get_template_directory_uri(),
            'iconService'   => get_option('icon_service_url', 'https://f1.allesedv.com/16/%s/favicon.ico'),
            'iconFallback'  => get_option('icon_fallback_url', ''),
            'defaultTheme'  => get_option('default_theme', 'auto'),
            'logos'         => array(
                'lightRect'   => esc_url(get_option('logo_light_rect',   '')),
                'darkRect'    => esc_url(get_option('logo_dark_rect',    '')),
                'lightSquare' => esc_url(get_option('logo_light_square', '')),
                'darkSquare'  => esc_url(get_option('logo_dark_square',  '')),
            ),
        ));
    }
}
add_action('wp_enqueue_scripts', 'theme_enqueue_scripts', 100);

/**
 * 后台顶级菜单:主题设置
 * 子菜单:常规设置(默认页)、图标设置
 */
add_action('admin_menu', function() {
    add_menu_page(
        '主题设置',
        '主题设置',
        'manage_options',
        'navi-settings',           // 顶级 slug,首个子页复用此 slug,避免出现重复菜单项
        'navi_render_general_page',
        'dashicons-star-filled',
        58
    );
    add_submenu_page(
        'navi-settings',
        '常规设置',
        '常规设置',
        'manage_options',
        'navi-settings',           // 与顶级 slug 相同
        'navi_render_general_page'
    );
    add_submenu_page(
        'navi-settings',
        '站点 LOGO',
        '站点 LOGO',
        'manage_options',
        'navi-logo',
        'navi_render_logo_page'
    );
    add_submenu_page(
        'navi-settings',
        '侧边栏设置',
        '侧边栏设置',
        'manage_options',
        'navi-sidebar',
        'navi_render_sidebar_page'
    );
});

/**
 * 「网址」菜单下追加「网址检查」子项(与 添加新网址、网址分类 同级)
 */
add_action('admin_menu', function() {
    add_submenu_page(
        'edit.php?post_type=nav_link',  // 父级为「网址」CPT 菜单
        '网址检查',
        '网址检查',
        'edit_posts',
        'navi-check',
        'navi_render_check_page'
    );
}, 20);

/* ---------- 常规设置 ---------- */
add_action('admin_init', function() {
    register_setting('navi_general_group', 'default_theme', array(
        'type'              => 'string',
        'default'           => 'auto',
        'sanitize_callback' => function($v) {
            return in_array($v, array('auto', 'light', 'dark'), true) ? $v : 'auto';
        },
    ));

    // 网站 favicon 获取设置(原在"图标设置"页,保留功能)
    register_setting('navi_general_group', 'icon_service_url', array(
        'type'              => 'string',
        'default'           => 'https://f1.allesedv.com/16/%s/favicon.ico',
        'sanitize_callback' => 'esc_url_raw',
    ));
    register_setting('navi_general_group', 'icon_fallback_url', array(
        'type'              => 'string',
        'default'           => '',
        'sanitize_callback' => 'esc_url_raw',
    ));
});

/**
 * 主题设置页共享样式(卡片布局 + 胶囊开关),仅在本主题设置页输出
 */
function navi_admin_style() {
    ?>
    <style>
    .navi-admin{max-width:980px}
    .navi-card{background:#fff;border:1px solid #e0e2e6;border-radius:12px;padding:18px 22px 22px;margin:14px 0 0;box-shadow:0 1px 3px rgba(16,24,40,.04)}
    .navi-card > .title{margin:0 0 2px;padding:0;font-size:15px;line-height:1.4}
    .navi-card .navi-sub{color:#646970;font-size:13px;margin:0 0 16px}
    .navi-admin .form-table{margin-top:6px}
    .navi-admin .form-table th{padding-left:2px}
    .navi-admin .submit{padding-left:2px}
    /* 胶囊开关 */
    .navi-pills{display:flex;flex-wrap:wrap;gap:12px}
    .navi-pill{position:relative;display:inline-flex;align-items:center;gap:9px;padding:9px 16px 9px 14px;border-radius:999px;border:1.5px solid #d3d6da;background:#f6f7f7;color:#50575e;font-size:13.5px;font-weight:500;cursor:pointer;user-select:none;transition:all .18s ease}
    .navi-pill:hover{border-color:#2271b1;color:#2271b1;background:#f0f6fc}
    .navi-pill .dashicons{font-size:16px;width:16px;height:16px;line-height:1.2}
    .navi-pill .navi-pill-state{font-size:11px;line-height:1;padding:3px 9px;border-radius:999px;background:rgba(0,0,0,.07);color:#787c82;font-weight:400}
    .navi-pill input[type=checkbox]{position:absolute;opacity:0;width:0;height:0;margin:0}
    .navi-pill.is-on{background:linear-gradient(135deg,#2271b1,#135e96);border-color:transparent;color:#fff;box-shadow:0 4px 12px rgba(34,113,177,.28)}
    .navi-pill.is-on:hover{color:#fff;border-color:transparent;background:linear-gradient(135deg,#2271b1,#135e96)}
    .navi-pill.is-on .navi-pill-state{background:rgba(255,255,255,.25);color:#fff}
    .navi-pill-actions{margin:14px 0 0}
    .navi-pill-actions .button{margin-right:8px}
    /* LOGO / Favicon 预览:右上角 X 号删除按钮 */
    .nav-logo-preview{position:relative}
    .nav-logo-preview .nav-logo-clear{position:absolute;top:4px;right:4px;width:22px;height:22px;border-radius:50%;background:rgba(0,0,0,.55);color:#fff;border:none;cursor:pointer;display:none;align-items:center;justify-content:center;font-size:15px;line-height:1;padding:0;box-shadow:0 1px 3px rgba(0,0,0,.25);transition:background .15s}
    .nav-logo-preview .nav-logo-clear:hover{background:#d63638}
    .nav-logo-preview.has-image .nav-logo-clear{display:flex}
    </style>
    <?php
}

function navi_render_general_page() {
    navi_admin_style();
    ?>
    <div class="wrap navi-admin">
        <h1>常规设置</h1>
        <form method="post" action="options.php">
            <?php
            settings_fields('navi_general_group');
            ?>
            <div class="navi-card">
            <table class="form-table" role="presentation">
                <tr>
                    <th scope="row"><label for="default_theme">默认主题模式</label></th>
                    <td>
                        <select name="default_theme" id="default_theme">
                            <option value="auto" <?php selected(get_option('default_theme', 'auto'), 'auto'); ?>>跟随系统</option>
                            <option value="light" <?php selected(get_option('default_theme', 'auto'), 'light'); ?>>浅色</option>
                            <option value="dark" <?php selected(get_option('default_theme', 'auto'), 'dark'); ?>>深色</option>
                        </select>
                        <p class="description">新访客首次打开时使用的明暗主题,用户手动切换后以浏览器本地记录为准。</p>
                    </td>
                </tr>

                <tr>
                    <th scope="row"><label for="icon_service_url">Favicon 备用服务</label></th>
                    <td>
                        <input type="url" name="icon_service_url" id="icon_service_url"
                               value="<?php echo esc_attr(get_option('icon_service_url', 'https://f1.allesedv.com/16/%s/favicon.ico')); ?>"
                               class="regular-text">
                        <p class="description">
                            导航卡片图标自动获取顺序:① 网站自身 favicon → ② <strong>本备用服务</strong> → ③ 兜底占位图。<br>
                            用 <code>%s</code> 代替域名,国内推荐 <code>https://f1.allesedv.com/16/%s/favicon.ico</code>。
                        </p>
                    </td>
                </tr>
                <tr>
                    <th scope="row"><label for="icon_fallback_url">兜底图标 URL</label></th>
                    <td>
                        <input type="url" name="icon_fallback_url" id="icon_fallback_url"
                               value="<?php echo esc_attr(get_option('icon_fallback_url')); ?>"
                               class="regular-text" placeholder="留空使用内置地球图标">
                        <p class="description">所有网站图标均加载失败时显示的统一占位图,建议 SVG/PNG 正方形。</p>
                    </td>
                </tr>
            </table>
            </div>
            <?php submit_button('保存常规设置'); ?>
        </form>
    </div>
    <?php
}

/* ---------- 站点 LOGO ---------- */
add_action('admin_init', function() {
    register_setting('navi_logo_group', 'logo_light_rect', array(
        'type'              => 'string',
        'default'           => '',
        'sanitize_callback' => 'esc_url_raw',
    ));
    register_setting('navi_logo_group', 'logo_dark_rect', array(
        'type'              => 'string',
        'default'           => '',
        'sanitize_callback' => 'esc_url_raw',
    ));
    register_setting('navi_logo_group', 'logo_light_square', array(
        'type'              => 'string',
        'default'           => '',
        'sanitize_callback' => 'esc_url_raw',
    ));
    register_setting('navi_logo_group', 'logo_dark_square', array(
        'type'              => 'string',
        'default'           => '',
        'sanitize_callback' => 'esc_url_raw',
    ));
    // 站点 favicon(浏览器标签页图标),默认回退到主题内置 assets/logo.ico
    register_setting('navi_logo_group', 'site_favicon', array(
        'type'              => 'string',
        'default'           => '',
        'sanitize_callback' => 'esc_url_raw',
    ));
});

/**
 * 渲染一个 LOGO 上传字段(缩略图预览 + wp.media 上传 + URL 输入 + 删除)
 * $builtin:该字段的内置兜底图 URL,默认显示在预览中(与 favicon 行为一致)
 */
function nav_logo_field($name, $label, $hint, $builtin = '') {
    $value = esc_url(get_option($name));
    $preview = $value ? $value : $builtin;
    ?>
    <tr>
        <th scope="row"><label for="<?php echo esc_attr($name); ?>"><?php echo esc_html($label); ?></label></th>
        <td>
            <div class="nav-logo-field" data-target="<?php echo esc_attr($name); ?>" data-builtin="<?php echo esc_attr($builtin); ?>">
                <div class="nav-logo-preview <?php echo $preview ? 'has-image' : ''; ?>" style="width:220px;height:80px;border:1px dashed #c3c4c7;border-radius:4px;display:flex;align-items:center;justify-content:center;margin-bottom:8px;background:#fafafa;overflow:hidden;">
                    <?php if ($preview) : ?>
                        <img src="<?php echo esc_url($preview); ?>" style="max-width:100%;max-height:100%;object-fit:contain;">
                    <?php else : ?>
                        <span style="color:#a7aaad;font-size:12px;">未设置(将使用主题内置 Logo)</span>
                    <?php endif; ?>
                    <button type="button" class="nav-logo-clear" title="删除图片">×</button>
                </div>
                <input type="hidden" name="<?php echo esc_attr($name); ?>" class="nav-logo-input"
                       id="<?php echo esc_attr($name); ?>" value="<?php echo $value; ?>">
                <button type="button" class="button nav-logo-upload">上传</button>
                <button type="button" class="button nav-logo-delete">删除</button>
                <p class="description"><?php echo esc_html($hint); ?></p>
            </div>
        </td>
    </tr>
    <?php
}

/**
 * 渲染 favicon 字段(32×32 预览 + 上传 + 删除),默认回退到主题内置 assets/logo.ico
 */
function nav_favicon_field() {
    $value = esc_url(get_option('site_favicon'));
    $builtin = get_template_directory_uri() . '/assets/logo.ico';
    $preview = $value ? $value : $builtin;
    ?>
    <tr>
        <th scope="row"><label for="site_favicon">站点 Favicon</label></th>
        <td>
            <div class="nav-logo-field" data-target="site_favicon" data-builtin="<?php echo esc_attr($builtin); ?>">
                <div class="nav-logo-preview <?php echo $preview ? 'has-image' : ''; ?>" style="width:48px;height:48px;border:1px dashed #c3c4c7;border-radius:4px;display:flex;align-items:center;justify-content:center;margin-bottom:8px;background:#fafafa;overflow:hidden;">
                    <img src="<?php echo esc_url($preview); ?>" style="max-width:100%;max-height:100%;object-fit:contain;">
                    <button type="button" class="nav-logo-clear" title="删除图片">×</button>
                </div>
                <input type="hidden" name="site_favicon" class="nav-logo-input" id="site_favicon" value="<?php echo $value; ?>">
                <button type="button" class="button nav-logo-upload">上传</button>
                <button type="button" class="button nav-logo-delete">删除</button>
                <p class="description">浏览器标签页图标,建议 32×32 或 64×64 的 .ico/.png 文件;留空使用主题内置 <code>assets/logo.ico</code>。</p>
            </div>
        </td>
    </tr>
    <?php
}

function navi_render_logo_page() {
    wp_enqueue_media();
    navi_admin_style();
    ?>
    <div class="wrap navi-admin">
        <h1>站点 LOGO</h1>
        <form method="post" action="options.php">
            <?php
            settings_fields('navi_logo_group');
            ?>
            <div class="navi-card">
            <h2 class="title">Favicon</h2>
            <p class="navi-sub">浏览器标签页图标,默认使用主题内置 logo.ico。</p>
            <table class="form-table" role="presentation">
                <?php nav_favicon_field(); ?>
            </table>
            </div>
            <div class="navi-card">
            <h2 class="title">四套 LOGO 组合</h2>
            <p class="navi-sub">亮色/暗色主题与横版/方形 LOGO 组合使用,为空时自动回退到主题内置 Logo。</p>
            <table class="form-table" role="presentation">
                <?php
                $navi_assets = get_template_directory_uri() . '/assets';
                nav_logo_field('logo_light_rect',    '亮色主题 Logo(横版)', '展开侧边栏时使用,建议高≤80px、长≤360px 的横版图,白色/浅色背景可见。', $navi_assets . '/logohen.png');
                nav_logo_field('logo_dark_rect',     '暗色主题 Logo(横版)', '暗色主题展开侧边栏时使用;留空时依次回退:亮色横版 → 内置 logohen@2.png。', $navi_assets . '/logohen@2.png');
                nav_logo_field('logo_light_square',  '亮色主题方形 Logo',    '收起侧边栏时使用,建议 80×80 方形图标。', $navi_assets . '/logo.png');
                nav_logo_field('logo_dark_square',   '暗色主题方形 Logo',    '暗色主题收起侧边栏时使用;留空则复用亮色方形。', $navi_assets . '/logo.png');
                ?>
            </table>
            </div>
            <?php submit_button('保存 LOGO 设置'); ?>
        </form>
    </div>
    <script>
    jQuery(function($) {
        // 占位文案(无内置兜底图时用)
        var PLACEHOLDER = '<span style="color:#a7aaad;font-size:12px;">未设置(将使用主题内置 Logo)</span>';
        var CLEAR_BTN = '<button type="button" class="nav-logo-clear" title="删除图片">×</button>';

        // 渲染预览框:有自定义值显示自定义图;无值时若 data-builtin 则显示内置图;否则占位文案。只要显示图就带 X 号
        function renderPreview($field, url) {
            var $preview = $field.find('.nav-logo-preview');
            var builtin = $field.data('builtin');
            var shown = url || builtin;
            if (shown) {
                $preview.html('<img src="' + (url || builtin) + '" style="max-width:100%;max-height:100%;object-fit:contain;">' + CLEAR_BTN);
                $preview.addClass('has-image');
            } else {
                $preview.html(PLACEHOLDER);
                $preview.removeClass('has-image');
            }
        }

        $('.nav-logo-field').each(function() {
            var $field = $(this);
            var $input = $field.find('.nav-logo-input');

            // 上传
            $field.find('.nav-logo-upload').on('click', function(e) {
                e.preventDefault();
                var frame = wp.media({
                    title: '选择或上传图片',
                    button: { text: '使用此图片' },
                    multiple: false
                });
                frame.on('select', function() {
                    var attachment = frame.state().get('selection').first().toJSON();
                    $input.val(attachment.url);
                    renderPreview($field, attachment.url);
                });
                frame.open();
            });

            // 删除按钮(底部)— 清空自定义值,回退到内置图
            $field.find('.nav-logo-delete').on('click', function(e) {
                e.preventDefault();
                $input.val('');
                renderPreview($field, '');
            });

            // X 号删除(右上角,事件委托,按钮随预览重建)
            $field.on('click', '.nav-logo-clear', function(e) {
                e.preventDefault();
                e.stopPropagation();
                $input.val('');
                renderPreview($field, '');
            });
        });
    });
    </script>
    <?php
}

/* ---------- 侧边栏设置 ---------- */
add_action('admin_init', function() {
    // 4 个卡片显示开关(checkbox)
    foreach (array('sidebar_show_latest', 'sidebar_show_hotcats', 'sidebar_show_ranking', 'sidebar_show_siteinfo') as $opt) {
        register_setting('navi_sidebar_group', $opt, array(
            'type'              => 'integer',
            'default'           => 1,
            'sanitize_callback' => 'absint',
        ));
    }
    // 站点信息卡片标题
    register_setting('navi_sidebar_group', 'sidebar_site_title', array(
        'type'              => 'string',
        'default'           => '小辰导航',
        'sanitize_callback' => 'sanitize_text_field',
    ));
    // 站点信息卡片文案
    register_setting('navi_sidebar_group', 'site_intro', array(
        'type'              => 'string',
        'default'           => '',
        'sanitize_callback' => 'sanitize_textarea_field',
    ));
    register_setting('navi_sidebar_group', 'site_qq', array(
        'type'              => 'string',
        'default'           => '',
        'sanitize_callback' => 'sanitize_text_field',
    ));
    register_setting('navi_sidebar_group', 'site_copyright', array(
        'type'              => 'string',
        'default'           => '',
        'sanitize_callback' => 'sanitize_text_field',
    ));
    // ICP 备案号
    register_setting('navi_sidebar_group', 'site_icp', array(
        'type'              => 'string',
        'default'           => '',
        'sanitize_callback' => 'sanitize_text_field',
    ));
});

function navi_render_sidebar_page() {
    navi_admin_style();
    // 模块定义:option key => array(名称, dashicon 图标)
    $modules = array(
        'sidebar_show_latest'  => array('最新收录', 'dashicons-clock'),
        'sidebar_show_hotcats' => array('热门分类', 'dashicons-star-filled'),
        'sidebar_show_ranking' => array('点击排行', 'dashicons-chart-bar'),
        'sidebar_show_siteinfo'=> array('站点信息', 'dashicons-info-outline'),
    );
    ?>
    <div class="wrap navi-admin">
        <h1>侧边栏设置</h1>
        <form method="post" action="options.php">
            <?php settings_fields('navi_sidebar_group'); ?>

            <div class="navi-card">
                <h2 class="title">显示模块</h2>
                <p class="navi-sub">点击胶囊开关首页右侧边栏对应模块;关闭后前台完全不渲染,不会留下空白占位。</p>
                <div class="navi-pills" id="naviModulePills">
                    <?php foreach ($modules as $key => $m) :
                        $on = ((int) get_option($key, 1)) === 1;
                    ?>
                        <input type="hidden" name="<?php echo esc_attr($key); ?>" value="0">
                        <label class="navi-pill<?php echo $on ? ' is-on' : ''; ?>">
                            <span class="dashicons <?php echo esc_attr($m[1]); ?>" aria-hidden="true"></span>
                            <span><?php echo esc_html($m[0]); ?></span>
                            <span class="navi-pill-state"><?php echo $on ? '显示' : '隐藏'; ?></span>
                            <input type="checkbox" name="<?php echo esc_attr($key); ?>" value="1"
                                   <?php checked($on); ?>>
                        </label>
                    <?php endforeach; ?>
                </div>
                <p class="navi-pill-actions">
                    <button type="button" class="button button-small" data-navi-all="1">全部显示</button>
                    <button type="button" class="button button-small" data-navi-all="0">全部隐藏</button>
                </p>
            </div>

            <div class="navi-card">
                <h2 class="title">站点信息卡片文案</h2>
                <p class="navi-sub">显示在右侧边栏「站点信息」卡片中,需开启上方「站点信息」胶囊。</p>
                <table class="form-table" role="presentation">
                    <tr>
                        <th scope="row"><label for="sidebar_site_title">卡片标题</label></th>
                        <td>
                            <input type="text" name="sidebar_site_title" id="sidebar_site_title"
                                   value="<?php echo esc_attr(get_option('sidebar_site_title', '小辰导航')); ?>"
                                   class="regular-text">
                            <p class="description">卡片顶部标题,默认「小辰导航」。</p>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row"><label for="site_intro">站点简介</label></th>
                        <td>
                            <textarea name="site_intro" id="site_intro" rows="4"
                                      class="large-text"><?php echo esc_textarea(navi_get_intro()); ?></textarea>
                            <p class="description">未自定义时显示默认简介,可直接在此修改;清空后保存将恢复默认简介。</p>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row"><label for="site_qq">联系 QQ</label></th>
                        <td>
                            <input type="text" name="site_qq" id="site_qq"
                                   value="<?php echo esc_attr(get_option('site_qq')); ?>"
                                   class="regular-text" placeholder="例如:123456789">
                            <p class="description">留空则不显示该行。</p>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row"><label for="site_copyright">版权信息</label></th>
                        <td>
                            <input type="text" name="site_copyright" id="site_copyright"
                                   value="<?php echo esc_attr(get_option('site_copyright')); ?>"
                                   class="regular-text" placeholder="例如:© 2026 小辰导航">
                            <p class="description">留空则自动显示「© 当前年份 小辰导航」。</p>
                        </td>
                    </tr>
                    <tr>
                        <th scope="row"><label for="site_icp">ICP 备案号</label></th>
                        <td>
                            <input type="text" name="site_icp" id="site_icp"
                                   value="<?php echo esc_attr(get_option('site_icp')); ?>"
                                   class="regular-text" placeholder="例如:京ICP备12345678号-1">
                            <p class="description">留空则不显示;填写后显示在版权下方,可点击跳转工信部备案系统。</p>
                        </td>
                    </tr>
                </table>
            </div>

            <?php submit_button('保存侧边栏设置'); ?>
        </form>
    </div>
    <script>
    (function () {
        var box = document.getElementById('naviModulePills');
        if (!box) { return; }
        function sync(pill) {
            var cb = pill.querySelector('input[type=checkbox]');
            var state = pill.querySelector('.navi-pill-state');
            var on = cb.checked;
            pill.classList.toggle('is-on', on);
            if (state) { state.textContent = on ? '显示' : '隐藏'; }
        }
        box.querySelectorAll('.navi-pill').forEach(function (pill) {
            var cb = pill.querySelector('input[type=checkbox]');
            cb.addEventListener('change', function () { sync(pill); });
        });
        document.querySelectorAll('[data-navi-all]').forEach(function (btn) {
            btn.addEventListener('click', function () {
                var on = btn.getAttribute('data-navi-all') === '1';
                box.querySelectorAll('.navi-pill').forEach(function (pill) {
                    var cb = pill.querySelector('input[type=checkbox]');
                    cb.checked = on;
                    sync(pill);
                });
            });
        });
    })();
    </script>
    <?php
}

/* ---------- 网址检查 ---------- */
function navi_render_check_page() {
    navi_admin_style();
    // 当前页所有 nav_link 文章(含 link_url)
    $query = new WP_Query(array(
        'post_type'      => 'nav_link',
        'post_status'    => array('publish', 'pending', 'draft'),
        'posts_per_page' => -1,
        'orderby'        => 'title',
        'order'          => 'ASC',
        'no_found_rows'  => true,
    ));
    $links = array();
    while ($query->have_posts()) {
        $p = $query->next_post();
        $url = trim((string) get_post_meta($p->ID, 'link_url', true));
        $links[] = array(
            'id'    => (int) $p->ID,
            'title' => get_the_title($p),
            'url'   => $url,
            'edit'  => admin_url('post.php?post=' . $p->ID . '&action=edit'),
        );
    }
    wp_reset_postdata();
    $rest_root = esc_url_raw(rest_url());
    $nonce     = wp_create_nonce('wp_rest');
    ?>
    <div class="wrap navi-admin">
        <h1>网址检查</h1>

        <div class="navi-card">
            <h2 class="title">批量检查网址可访问性</h2>
            <p class="navi-sub">对每个网址发送 HTTP HEAD 请求(不支持时降级 GET),8 秒超时,最多跟随 5 次重定向。状态码 200–399 视为正常,其他视为失效。</p>

            <p class="navi-pill-actions">
                <button type="button" class="button button-primary" id="naviCheckStart">开始检查</button>
                <button type="button" class="button" id="naviCheckStop" disabled>停止</button>
                <button type="button" class="button" id="naviCheckFailOnly">仅看失效</button>
            </p>

            <div style="margin:12px 0 6px;display:flex;align-items:center;gap:12px;">
                <div style="flex:1;height:8px;background:#f0f0f1;border-radius:999px;overflow:hidden;">
                    <div id="naviCheckBar" style="width:0;height:100%;background:linear-gradient(90deg,#2271b1,#135e96);transition:width .2s;"></div>
                </div>
                <span id="naviCheckStat" style="font-size:12px;color:#646970;min-width:160px;text-align:right;">未开始</span>
            </div>

            <table class="wp-list-table widefat striped" id="naviCheckTable" style="margin-top:8px;">
                <thead>
                    <tr>
                        <th style="width:40px;">#</th>
                        <th>标题</th>
                        <th>链接</th>
                        <th style="width:120px;">状态码</th>
                        <th style="width:240px;">结果</th>
                        <th style="width:160px;">操作</th>
                    </tr>
                </thead>
                <tbody>
                <?php if (empty($links)) : ?>
                    <tr><td colspan="6">暂无网址,请先在「网址」菜单添加。</td></tr>
                <?php else :
                    $i = 0;
                    foreach ($links as $link) :
                        $i++;
                        $host = $link['url'] ? preg_replace('#^https?://#', '', esc_url($link['url'])) : '';
                ?>
                    <tr data-id="<?php echo esc_attr($link['id']); ?>" data-status="pending">
                        <td><?php echo $i; ?></td>
                        <td><strong><?php echo esc_html($link['title'] ?: '(无标题)'); ?></strong></td>
                        <td>
                            <?php if ($link['url']) : ?>
                                <a href="<?php echo esc_url($link['url']); ?>" target="_blank" rel="noopener" style="color:#2271b1;"><?php echo esc_html($host); ?></a>
                            <?php else : ?>
                                <span style="color:#a7aaad;">未填写</span>
                            <?php endif; ?>
                        </td>
                        <td class="navi-code">—</td>
                        <td class="navi-result"><span style="color:#a7aaad;">待检查</span></td>
                        <td class="navi-ops">
                            <a href="<?php echo esc_url($link['edit']); ?>" class="button button-small">编辑</a>
                            <button type="button" class="button button-small navi-recheck" disabled>重检</button>
                        </td>
                    </tr>
                <?php endforeach; endif; ?>
                </tbody>
            </table>

            <p style="margin-top:10px;font-size:12px;color:#646970;" id="naviCheckSummary"></p>
        </div>
    </div>
    <script>
    (function () {
        var root   = '<?php echo $rest_root; ?>nav/v1/check-link';
        var nonce  = '<?php echo $nonce; ?>';
        var table  = document.getElementById('naviCheckTable');
        if (!table) { return; }
        var rows   = Array.prototype.slice.call(table.querySelectorAll('tbody tr[data-id]'));
        var idx    = 0;
        var stop   = false;
        var stats  = { ok: 0, fail: 0, empty: 0, total: rows.length };
        var startBtn = document.getElementById('naviCheckStart');
        var stopBtn  = document.getElementById('naviCheckStop');
        var failBtn  = document.getElementById('naviCheckFailOnly');
        var bar      = document.getElementById('naviCheckBar');
        var stat     = document.getElementById('naviCheckStat');
        var summary  = document.getElementById('naviCheckSummary');

        function fmtStat() {
            var done = stats.ok + stats.fail + stats.empty;
            return done + ' / ' + stats.total + '  正常 ' + stats.ok + '  失效 ' + stats.fail + '  无链接 ' + stats.empty;
        }
        function setRow(row, data) {
            row.dataset.status = data.status;
            var c = row.querySelector('.navi-code');
            var r = row.querySelector('.navi-result');
            var badge = '';
            if (data.status === 'ok') {
                badge = '<span style="color:#1a8754;font-weight:600;">✓ 正常</span>';
                c.innerHTML = '<span style="color:#1a8754;">' + (data.code || '—') + '</span>';
            } else if (data.status === 'empty') {
                badge = '<span style="color:#a7aaad;">— 未填写</span>';
                c.innerHTML = '—';
            } else {
                badge = '<span style="color:#d63638;font-weight:600;">✗ 失效</span><br><span style="font-size:11px;color:#787c82;">' + (data.message || '').slice(0, 80) + '</span>';
                c.innerHTML = '<span style="color:#d63638;">' + (data.code || '0') + '</span>';
                row.style.background = '#fef7f7';
            }
            r.innerHTML = badge;
            row.querySelector('.navi-recheck').disabled = false;
        }
        function checkOne(row) {
            var id = row.dataset.id;
            row.dataset.status = 'checking';
            row.querySelector('.navi-result').innerHTML = '<span style="color:#2271b1;">检查中…</span>';
            return fetch(root + '?id=' + id, { headers: { 'X-WP-Nonce': nonce } })
                .then(function (r) { return r.json(); })
                .then(function (d) { setRow(row, d); stats[d.status]++; })
                .catch(function (e) {
                    setRow(row, { status: 'fail', code: 0, message: '请求异常' });
                    stats.fail++;
                });
        }
        async function run() {
            stop = false;
            startBtn.disabled = true;
            stopBtn.disabled  = false;
            idx = 0;
            stats = { ok: 0, fail: 0, empty: 0, total: rows.length };
            bar.style.width = '0%';
            stat.textContent = fmtStat();
            summary.textContent = '';
            while (idx < rows.length && !stop) {
                var row = rows[idx];
                if (row.dataset.status === 'pending') {
                    await checkOne(row);
                }
                idx++;
                bar.style.width = (Math.round((idx / rows.length) * 100)) + '%';
                stat.textContent = fmtStat();
            }
            startBtn.disabled = false;
            stopBtn.disabled  = true;
            if (!stop) {
                summary.innerHTML = '检查完成:共 ' + stats.total + ' 条,<strong style="color:#d63638;">失效 ' + stats.fail + '</strong>,<strong style="color:#1a8754;">正常 ' + stats.ok + '</strong>,无链接 ' + stats.empty + '。';
            }
        }
        startBtn.addEventListener('click', run);
        stopBtn.addEventListener('click', function () { stop = true; });
        failBtn.addEventListener('click', function () {
            rows.forEach(function (r) {
                r.style.display = (r.dataset.status === 'fail' || r.dataset.status === 'pending') ? '' : 'none';
            });
        });
        // 单行重检
        table.addEventListener('click', function (e) {
            if (e.target.classList.contains('navi-recheck')) {
                var tr = e.target.closest('tr[data-id]');
                if (tr) {
                    var status = tr.dataset.status;
                    if (status === 'fail' || status === 'ok' || status === 'empty') {
                        stats[status]--;
                    }
                    checkOne(tr).then(function () {
                        stat.textContent = fmtStat();
                    });
                }
            }
        });
    })();
    </script>
    <?php
}

/**
 * 投稿页真实地址(兼容任意固定链接结构,如 /index.php/12/)
 */
function navi_submit_url() {
    $page = get_page_by_path('submit');
    if ($page) {
        return get_permalink($page);
    }
    return home_url('/submit');
}

/**
 * 供模板使用的默认值辅助函数
 */
function navi_get_intro() {
    $intro = trim((string) get_option('site_intro'));
    if ($intro !== '') {
        return $intro;
    }
    return '小辰导航是一个简约实用的网址导航平台,聚合常用搜索、工具网站、学习资源、影视娱乐、设计素材、开发文档等各类优质网址。界面干净无冗余,分类清晰易查找,致力于成为你打开浏览器的第一个主页,让上网更高效、更省心。';
}

function navi_get_copyright() {
    $copyright = trim((string) get_option('site_copyright'));
    if ($copyright !== '') {
        return $copyright;
    }
    return '© ' . gmdate('Y') . ' 小辰导航';
}

/**
 * 分类图标的 slug 默认值(Bootstrap Icons,MIT 协议可商用)
 * 后台“网址分类”中可按分类单独选择图标,选择结果(term meta)优先于此映射
 */
function nav_default_category_icons() {
    return array(
        'common'        => 'bi-globe2',
        'ai-tools'      => 'bi-robot',
        'utility'       => 'bi-tools',
        'gallery'       => 'bi-image',
        'entertainment' => 'bi-controller',
        'design'        => 'bi-palette',
        'office'        => 'bi-book',
        'life'          => 'bi-house-heart',
        'resources'     => 'bi-box-seam',
    );
}

/**
 * 获取分类图标标识
 * 接受 term ID、slug 或 term 对象;返回 bi-* 名称,未设置时取 slug 默认值,兜底文件夹
 */
function get_category_icon($category) {
    if (is_object($category)) {
        $term_id = isset($category->term_id) ? (int) $category->term_id : 0;
        $slug    = isset($category->slug) ? $category->slug : '';
    } elseif (is_numeric($category)) {
        $term_id = (int) $category;
        $term    = get_term($term_id, 'nav_category');
        $slug    = ($term && !is_wp_error($term)) ? $term->slug : '';
    } else {
        $slug    = sanitize_title((string) $category);
        $term    = get_term_by('slug', $slug, 'nav_category');
        $term_id = ($term && !is_wp_error($term)) ? (int) $term->term_id : 0;
    }

    if ($term_id) {
        $meta = trim((string) get_term_meta($term_id, 'nav_cat_icon', true));
        if ($meta !== '') {
            // 图标字体名做类名净化;其余值(如 emoji 文本)原样交由渲染层转义
            if (strpos($meta, 'bi-') === 0) {
                return sanitize_html_class($meta);
            }
            return $meta;
        }
    }

    $defaults = nav_default_category_icons();
    return isset($defaults[$slug]) ? $defaults[$slug] : 'bi-folder';
}

/**
 * 把图标标识渲染为 HTML;bi-* 用图标字体,其余(如 emoji)按文本输出
 */
function nav_icon_html($icon, $extra_class = '') {
    $icon = trim((string) $icon);
    if ($icon === '') {
        $icon = 'bi-folder';
    }
    if (strpos($icon, 'bi-') === 0) {
        $class = 'bi ' . $icon . ($extra_class ? ' ' . $extra_class : '');
        return '<i class="' . esc_attr($class) . '" aria-hidden="true"></i>';
    }
    return esc_html($icon);
}

function get_category_order($category_id) {
    $orders = array(
        'common' => 1,
        'ai-tools' => 2,
        'utility' => 3,
        'gallery' => 4,
        'entertainment' => 5,
        'design' => 6,
        'office' => 7,
        'life' => 8,
        'resources' => 9
    );
    return isset($orders[$category_id]) ? $orders[$category_id] : 99;
}

add_action('rest_api_init', function() {
    register_rest_field('nav_category', 'icon', array(
        'get_callback' => function($object) {
            return get_category_icon($object['slug']);
        },
        'schema' => array(
            'type' => 'string',
            'description' => '分类图标',
        ),
    ));
    
    register_rest_field('nav_category', 'order', array(
        'get_callback' => function($object) {
            return get_category_order($object['slug']);
        },
        'schema' => array(
            'type' => 'integer',
            'description' => '分类排序',
        ),
    ));
});

/**
 * 主题激活时自动创建“网站投稿”页面
 * page-{slug}.php 模板会自动套用到对应 slug 的页面
 */
add_action('after_switch_theme', function() {
    if (!get_page_by_path('submit')) {
        wp_insert_post(array(
            'post_type'   => 'page',
            'post_status' => 'publish',
            'post_title'  => '网站投稿',
            'post_name'   => 'submit',
            'post_content'=> '',
        ));
    }
});