<?php

/**
 * @id 1
 * @name 百度收录巡检
 * @version 1.0.0
 * @description 百度页面收录状态批量巡检，支持自定义超时时间和每页显示数量，快速检测网站收录情况并生成报告
 * @price 0
 * @type 工具
 * @width 820
 * @height 460
 * @thumbnail /img/id1.png
 */

$plugin_config = <<<EOT
[config_params]
default_limit =
check_timeout =
[/config_params]
EOT;

$plugin_identifier = '1_seotools';

function qk1_extract_config_params($fileContent)
{
    $pairs = [];
    if (!preg_match('/\[config_params\](.*?)\[\/config_params\]/s', (string)$fileContent, $m)) {
        return $pairs;
    }
    $lines = preg_split('/\r\n|\r|\n/', trim($m[1]));
    foreach ($lines as $line) {
        $line = trim($line);
        if ($line === '' || strpos($line, '=') === false) {
            continue;
        }
        $pos = strpos($line, '=');
        $k = trim(substr($line, 0, $pos));
        $v = trim(substr($line, $pos + 1));
        $pairs[$k] = $v;
    }
    return $pairs;
}

function qk1_site_url()
{
    $proto = 'http://';
    if (
        (!empty($_SERVER['HTTPS']) && strtolower((string)$_SERVER['HTTPS']) !== 'off')
        || (isset($_SERVER['SERVER_PORT']) && (string)$_SERVER['SERVER_PORT'] === '443')
        || (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && stripos((string)$_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') !== false)
        || (!empty($_SERVER['REQUEST_SCHEME']) && strtolower((string)$_SERVER['REQUEST_SCHEME']) === 'https')
    ) {
        $proto = 'https://';
    }
    $host = '';
    if (!empty($_SERVER['HTTP_X_FORWARDED_HOST'])) {
        $host = trim((string)$_SERVER['HTTP_X_FORWARDED_HOST']);
    }
    if ($host === '' && !empty($_SERVER['HTTP_HOST'])) {
        $host = trim((string)$_SERVER['HTTP_HOST']);
    }
    if ($host === '' && !empty($_SERVER['SERVER_NAME'])) {
        $host = trim((string)$_SERVER['SERVER_NAME']);
        if (!empty($_SERVER['SERVER_PORT']) && !in_array((string)$_SERVER['SERVER_PORT'], ['80', '443'], true) && strpos($host, ':') === false) {
            $host .= ':' . trim((string)$_SERVER['SERVER_PORT']);
        }
    }
    return $host === '' ? '' : rtrim($proto . $host, '/');
}

function qk1_detect_columns($conn, $table)
{
    $cols = [];
    $table = trim((string)$table);
    if ($table === '') {
        return $cols;
    }
    $rs = @mysqli_query($conn, "SHOW COLUMNS FROM `{$table}`");
    if (!$rs) {
        return $cols;
    }
    while ($row = mysqli_fetch_assoc($rs)) {
        if (isset($row['Field'])) {
            $cols[(string)$row['Field']] = true;
        }
    }
    mysqli_free_result($rs);
    return $cols;
}

function qk1_table_exists($conn, $table)
{
    $table = trim((string)$table);
    if ($table === '') {
        return false;
    }
    $safe = mysqli_real_escape_string($conn, $table);
    $rs = @mysqli_query($conn, "SHOW TABLES LIKE '{$safe}'");
    if (!$rs) {
        return false;
    }
    $exists = mysqli_num_rows($rs) > 0;
    mysqli_free_result($rs);
    return $exists;
}

function qk1_normalize_path($path)
{
    $path = trim((string)$path);
    if ($path === '') {
        return '';
    }
    if (preg_match('#^https?://#i', $path)) {
        return rtrim($path, '/');
    }
    if (strpos($path, '/') !== 0) {
        $path = '/' . $path;
    }
    return $path;
}

function qk1_build_item_url($siteUrl, $row, $prefix)
{
    $custom = isset($row['custom_url']) ? trim((string)$row['custom_url']) : '';
    if ($custom !== '') {
        return $siteUrl . qk1_normalize_path($custom);
    }
    return $siteUrl . '/' . $prefix . intval($row['id']) . '.html';
}

function qk1_lower($text)
{
    $text = (string)$text;
    if (function_exists('mb_strtolower')) {
        return mb_strtolower($text, 'UTF-8');
    }
    return strtolower($text);
}

function qk1_normalize_text($text)
{
    $text = html_entity_decode(strip_tags((string)$text), ENT_QUOTES, 'UTF-8');
    $text = qk1_lower($text);
    $text = preg_replace('/\s+/u', '', $text);
    return trim((string)$text);
}

function qk1_normalize_compare_url($url)
{
    $url = html_entity_decode(trim((string)$url), ENT_QUOTES, 'UTF-8');
    if ($url === '') {
        return '';
    }
    if (strpos($url, '//') === 0) {
        $url = 'https:' . $url;
    }
    if (!preg_match('#^https?://#i', $url)) {
        return '';
    }
    $parts = @parse_url($url);
    if (!is_array($parts) || empty($parts['host'])) {
        return '';
    }
    $scheme = strtolower((string)($parts['scheme'] ?? 'https'));
    $host = strtolower((string)$parts['host']);
    $port = isset($parts['port']) ? intval($parts['port']) : 0;
    if (($scheme === 'http' && $port === 80) || ($scheme === 'https' && $port === 443)) {
        $port = 0;
    }
    $path = isset($parts['path']) ? rawurldecode((string)$parts['path']) : '/';
    if ($path === '') {
        $path = '/';
    }
    $path = preg_replace('#/+#', '/', $path);
    if ($path !== '/') {
        $path = rtrim($path, '/');
    }
    $query = isset($parts['query']) && $parts['query'] !== '' ? '?' . (string)$parts['query'] : '';
    return $host . ($port > 0 ? ':' . $port : '') . $path . $query;
}

function qk1_http_get($url, $timeout = 10)
{
    $url = trim((string)$url);
    if ($url === '') {
        return ['ok' => false, 'status' => 0, 'body' => '', 'error' => 'empty_url', 'effective_url' => ''];
    }
    $timeout = intval($timeout);
    if ($timeout <= 0) {
        $timeout = 10;
    }
    $headers = [
        'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
        'Accept-Language: zh-CN,zh;q=0.9',
        'Cache-Control: no-cache',
        'Pragma: no-cache',
        'Referer: https://www.baidu.com/'
    ];
    $ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36';
    if (function_exists('curl_init')) {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_ENCODING, '');
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_USERAGENT, $ua);
        $body = curl_exec($ch);
        $status = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $effectiveUrl = (string)curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
        $error = curl_error($ch);
        curl_close($ch);
        return [
            'ok' => $body !== false && $status >= 200 && $status < 400,
            'status' => $status,
            'body' => $body === false ? '' : (string)$body,
            'error' => $error,
            'effective_url' => $effectiveUrl === '' ? $url : $effectiveUrl
        ];
    }
    $ctx = stream_context_create([
        'http' => [
            'method' => 'GET',
            'timeout' => $timeout,
            'header' => implode("\r\n", array_merge($headers, ['User-Agent: ' . $ua])) . "\r\n"
        ],
        'ssl' => ['verify_peer' => false, 'verify_peer_name' => false]
    ]);
    $body = @file_get_contents($url, false, $ctx);
    return [
        'ok' => $body !== false,
        'status' => 0,
        'body' => $body === false ? '' : (string)$body,
        'error' => $body === false ? 'request_failed' : '',
        'effective_url' => $url
    ];
}

function qk1_http_multi_get($requestMap, $timeout = 10, $concurrency = 6)
{
    $results = [];
    if (!is_array($requestMap) || empty($requestMap)) {
        return $results;
    }
    $timeout = intval($timeout);
    if ($timeout <= 0) {
        $timeout = 10;
    }
    $concurrency = intval($concurrency);
    if ($concurrency <= 0) {
        $concurrency = 6;
    }
    $queue = [];
    foreach ($requestMap as $key => $url) {
        $queue[] = ['key' => (string)$key, 'url' => trim((string)$url)];
    }
    if (!function_exists('curl_multi_init') || !function_exists('curl_init')) {
        foreach ($queue as $item) {
            $results[$item['key']] = qk1_http_get($item['url'], $timeout);
        }
        return $results;
    }
    $headers = [
        'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
        'Accept-Language: zh-CN,zh;q=0.9',
        'Cache-Control: no-cache',
        'Pragma: no-cache',
        'Referer: https://www.baidu.com/'
    ];
    $ua = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36';
    $mh = curl_multi_init();
    $handles = [];
    $index = 0;
    $total = count($queue);
    while ($index < $total || !empty($handles)) {
        while ($index < $total && count($handles) < $concurrency) {
            $item = $queue[$index++];
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $item['url']);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
            curl_setopt($ch, CURLOPT_ENCODING, '');
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
            curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
            curl_setopt($ch, CURLOPT_USERAGENT, $ua);
            curl_multi_add_handle($mh, $ch);
            $handles[(int)$ch] = [
                'handle' => $ch,
                'key' => $item['key'],
                'url' => $item['url']
            ];
        }
        do {
            $status = curl_multi_exec($mh, $running);
        } while ($status === CURLM_CALL_MULTI_PERFORM);
        while ($info = curl_multi_info_read($mh)) {
            $ch = $info['handle'];
            $meta = $handles[(int)$ch] ?? null;
            if ($meta) {
                $body = curl_multi_getcontent($ch);
                $statusCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
                $effectiveUrl = (string)curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
                $error = curl_error($ch);
                $results[$meta['key']] = [
                    'ok' => $body !== false && $statusCode >= 200 && $statusCode < 400,
                    'status' => $statusCode,
                    'body' => $body === false ? '' : (string)$body,
                    'error' => $error,
                    'effective_url' => $effectiveUrl === '' ? $meta['url'] : $effectiveUrl
                ];
                curl_multi_remove_handle($mh, $ch);
                curl_close($ch);
                unset($handles[(int)$ch]);
            }
        }
        if ($running > 0) {
            curl_multi_select($mh, 1.0);
        }
    }
    curl_multi_close($mh);
    foreach ($queue as $item) {
        if (!isset($results[$item['key']])) {
            $results[$item['key']] = qk1_http_get($item['url'], $timeout);
        }
    }
    return $results;
}

function qk1_to_timestamp($value)
{
    if ($value === null) {
        return 0;
    }
    $valueStr = trim((string)$value);
    if ($valueStr === '') {
        return 0;
    }
    if (is_numeric($valueStr)) {
        return intval($valueStr);
    }
    $ts = strtotime($valueStr);
    return $ts === false ? 0 : intval($ts);
}

function qk1_source_configs()
{
    static $configs = null;
    if ($configs !== null) {
        return $configs;
    }
    $configs = [
        'post' => [
            'table' => 'posts',
            'title_field' => 'title',
            'label' => '文章',
            'url_prefix' => 'post_id',
            'fields' => ['id', 'title', 'custom_url', 'created_at', 'updated_at'],
            'filters' => ['status' => 1],
            'order' => ['updated_at' => 'DESC', 'created_at' => 'DESC', 'id' => 'DESC'],
            'time_fields' => ['updated_at', 'created_at']
        ],
        'goods' => [
            'table' => 'goods',
            'title_field' => 'title',
            'label' => '商品',
            'url_prefix' => 'goods_id',
            'fields' => ['id', 'title', 'custom_url', 'create_time', 'update_time'],
            'filters' => ['status' => 1],
            'order' => ['update_time' => 'DESC', 'create_time' => 'DESC', 'id' => 'DESC'],
            'time_fields' => ['update_time', 'create_time']
        ],
        'category' => [
            'table' => 'categories',
            'title_field' => 'name',
            'label' => '栏目',
            'url_prefix' => 'list_id',
            'fields' => ['id', 'name', 'custom_url', 'created_at', 'updated_at'],
            'filters' => ['status' => 1, 'display' => 1],
            'order' => ['updated_at' => 'DESC', 'created_at' => 'DESC', 'sort' => 'ASC', 'id' => 'DESC'],
            'time_fields' => ['updated_at', 'created_at']
        ]
    ];
    return $configs;
}

function qk1_source_config($source)
{
    $configs = qk1_source_configs();
    return isset($configs[$source]) ? $configs[$source] : null;
}

function qk1_active_sources($source)
{
    if ($source === 'all') {
        return array_keys(qk1_source_configs());
    }
    return qk1_source_config($source) ? [$source] : [];
}

function qk1_sql_fields($cols, $fields)
{
    $list = [];
    foreach ($fields as $field) {
        if (($field === 'id' || isset($cols[$field])) && !isset($list[$field])) {
            $list[$field] = '`' . $field . '`';
        }
    }
    return array_values($list);
}

function qk1_sql_where($cols, $filters)
{
    $where = [];
    foreach ($filters as $field => $value) {
        if (isset($cols[$field])) {
            $where[] = '`' . $field . '` = ' . intval($value);
        }
    }
    return $where;
}

function qk1_sql_order($cols, $orders)
{
    $list = [];
    foreach ($orders as $field => $dir) {
        if (isset($cols[$field])) {
            $list[] = '`' . $field . '` ' . (strtoupper((string)$dir) === 'ASC' ? 'ASC' : 'DESC');
        }
    }
    if (empty($list)) {
        $list[] = '`id` DESC';
    }
    return implode(', ', $list);
}

function qk1_map_item_row($source, $config, $siteUrl, $row)
{
    $updatedAt = '';
    foreach ($config['time_fields'] as $field) {
        if (!empty($row[$field])) {
            $updatedAt = (string)$row[$field];
            break;
        }
    }
    return [
        'key' => $source . ':' . intval($row['id']),
        'type' => $source,
        'type_name' => $config['label'],
        'id' => intval($row['id']),
        'title' => (string)($row[$config['title_field']] ?? ''),
        'url' => qk1_build_item_url($siteUrl, $row, $config['url_prefix']),
        'updated_at' => $updatedAt,
        'sort_value' => qk1_to_timestamp($updatedAt),
        'baidu_status' => '待巡检',
        'baidu_detail' => '',
        'search_url' => '',
    ];
}

function qk1_fetch_source_items($conn, $siteUrl, $source, $limit, $offset = 0, $countOnly = false)
{
    $config = qk1_source_config($source);
    if (!$config || !qk1_table_exists($conn, $config['table'])) {
        return $countOnly ? 0 : [];
    }
    $cols = qk1_detect_columns($conn, $config['table']);
    $where = qk1_sql_where($cols, $config['filters']);
    if ($countOnly) {
        $sql = 'SELECT COUNT(*) AS c FROM `' . $config['table'] . '`';
        if (!empty($where)) {
            $sql .= ' WHERE ' . implode(' AND ', $where);
        }
        $rs = @mysqli_query($conn, $sql);
        if (!$rs) {
            return 0;
        }
        $row = mysqli_fetch_assoc($rs);
        mysqli_free_result($rs);
        return intval($row['c'] ?? 0);
    }
    $fields = qk1_sql_fields($cols, $config['fields']);
    if (empty($fields)) {
        return [];
    }
    $sql = 'SELECT ' . implode(',', $fields) . ' FROM `' . $config['table'] . '`';
    if (!empty($where)) {
        $sql .= ' WHERE ' . implode(' AND ', $where);
    }
    $sql .= ' ORDER BY ' . qk1_sql_order($cols, $config['order']) . ' LIMIT ' . intval($offset) . ',' . intval($limit);
    $rs = @mysqli_query($conn, $sql);
    if (!$rs) {
        return [];
    }
    $rows = [];
    while ($row = mysqli_fetch_assoc($rs)) {
        $rows[] = qk1_map_item_row($source, $config, $siteUrl, $row);
    }
    mysqli_free_result($rs);
    return $rows;
}

function qk1_fetch_items_paged($conn, $siteUrl, $source, $page, $pageSize)
{
    $page = intval($page);
    if ($page <= 0) {
        $page = 1;
    }
    $pageSize = intval($pageSize);
    if ($pageSize <= 0) {
        $pageSize = 20;
    }
    if ($pageSize > 100) {
        $pageSize = 100;
    }
    $offset = ($page - 1) * $pageSize;
    if ($offset < 0) {
        $offset = 0;
    }
    $sources = qk1_active_sources($source);
    if (count($sources) === 1) {
        $sourceKey = $sources[0];
        $total = qk1_fetch_source_items($conn, $siteUrl, $sourceKey, 0, 0, true);
        $items = $total > 0 ? qk1_fetch_source_items($conn, $siteUrl, $sourceKey, $pageSize, $offset) : [];
        return ['total' => $total, 'items' => $items];
    }
    $total = 0;
    foreach ($sources as $sourceKey) {
        $total += qk1_fetch_source_items($conn, $siteUrl, $sourceKey, 0, 0, true);
    }
    if ($total <= 0) {
        return ['total' => 0, 'items' => []];
    }
    $need = $offset + $pageSize;
    if ($need < 0) {
        $need = $pageSize;
    }
    if ($need > $total) {
        $need = $total;
    }
    if ($need > 5000) {
        $need = 5000;
    }
    $items = [];
    foreach ($sources as $sourceKey) {
        $items = array_merge($items, qk1_fetch_source_items($conn, $siteUrl, $sourceKey, $need, 0));
    }
    usort($items, function ($a, $b) {
        return intval($b['sort_value'] ?? 0) <=> intval($a['sort_value'] ?? 0);
    });
    $paged = array_slice($items, $offset, $pageSize);
    return ['total' => $total, 'items' => $paged];
}

function qk1_baidu_search_url($wd)
{
    return 'https://www.baidu.com/s?wd=' . rawurlencode((string)$wd);
}

function qk1_baidu_build_queries($siteUrl, $targetUrl)
{
    $siteUrl = trim((string)$siteUrl);
    $targetUrl = trim((string)$targetUrl);
    $domain = trim((string)parse_url($siteUrl, PHP_URL_HOST));
    if ($domain === '') {
        $domain = trim((string)parse_url($targetUrl, PHP_URL_HOST));
    }
    $path = trim((string)parse_url($targetUrl, PHP_URL_PATH), '/');
    $pathBase = $path !== '' ? basename($path) : '';
    $queries = ['"' . $targetUrl . '"'];
    if ($domain !== '' && $path !== '') {
        $queries[] = 'site:' . $domain . ' "' . $path . '"';
    }
    if ($domain !== '' && $pathBase !== '') {
        $queries[] = 'site:' . $domain . ' "' . $pathBase . '"';
    }
    return array_values(array_unique(array_filter($queries)));
}

function qk1_baidu_has_no_result($bodyText)
{
    $phrases = [
        '抱歉没有找到与',
        '没有找到该url',
        '没有找到和',
        '百度为您找到相关结果约0个',
        '未找到相关结果',
        '没有找到相关结果'
    ];
    foreach ($phrases as $phrase) {
        if (strpos($bodyText, qk1_normalize_text($phrase)) !== false) {
            return true;
        }
    }
    return false;
}

function qk1_baidu_is_blocked($bodyText)
{
    $phrases = [
        '百度安全验证',
        '请输入以下验证码',
        '网络不给力',
        '访问过于频繁',
        '验证后继续访问',
        '请输入验证码'
    ];
    foreach ($phrases as $phrase) {
        if (strpos($bodyText, qk1_normalize_text($phrase)) !== false) {
            return true;
        }
    }
    return false;
}

function qk1_baidu_collect_candidate_urls($body)
{
    $body = (string)$body;
    if ($body === '') {
        return [];
    }
    $urls = [];
    $patterns = [
        '/\bmu="([^"]+)"/i',
        "/\bmu='([^']+)'/i",
        '/\bdata-landurl="([^"]+)"/i',
        "/\bdata-landurl='([^']+)'/i",
        '/\bhref="([^"]+)"/i',
        "/\bhref='([^']+)'/i"
    ];
    foreach ($patterns as $pattern) {
        if (!preg_match_all($pattern, $body, $matches)) {
            continue;
        }
        foreach ($matches[1] as $raw) {
            $url = html_entity_decode(trim((string)$raw), ENT_QUOTES, 'UTF-8');
            if ($url === '' || $url === '#' || stripos($url, 'javascript:') === 0) {
                continue;
            }
            if (strpos($url, '//') === 0) {
                $url = 'https:' . $url;
            } elseif (strpos($url, '/link?') === 0) {
                $url = 'https://www.baidu.com' . $url;
            } elseif (strpos($url, '/') === 0) {
                continue;
            }
            if (!preg_match('#^https?://#i', $url)) {
                continue;
            }
            $host = strtolower((string)parse_url($url, PHP_URL_HOST));
            $path = (string)parse_url($url, PHP_URL_PATH);
            if ($host !== '' && strpos($host, 'baidu.com') !== false && strpos($path, '/link') !== 0) {
                continue;
            }
            if (!isset($urls[$url])) {
                $urls[$url] = true;
            }
            if (count($urls) >= 12) {
                break 2;
            }
        }
    }
    return array_keys($urls);
}

function qk1_resolve_url($url, $timeout = 10)
{
    static $cache = [];
    $url = trim((string)$url);
    if ($url === '') {
        return '';
    }
    if (isset($cache[$url])) {
        return $cache[$url];
    }
    if (stripos($url, 'baidu.com/link?') === false) {
        $cache[$url] = $url;
        return $url;
    }
    $timeout = intval($timeout);
    if ($timeout <= 0) {
        $timeout = 10;
    }
    if (function_exists('curl_init')) {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_NOBODY, true);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36');
        curl_exec($ch);
        $effectiveUrl = trim((string)curl_getinfo($ch, CURLINFO_EFFECTIVE_URL));
        curl_close($ch);
        if ($effectiveUrl !== '') {
            $cache[$url] = $effectiveUrl;
            return $effectiveUrl;
        }
    }
    $resp = qk1_http_get($url, $timeout);
    $cache[$url] = trim((string)($resp['effective_url'] ?? $url));
    return $cache[$url];
}

function qk1_url_matches($targetUrl, $candidateUrl)
{
    $targetNorm = qk1_normalize_compare_url($targetUrl);
    $candidateNorm = qk1_normalize_compare_url($candidateUrl);
    if ($targetNorm === '' || $candidateNorm === '') {
        return false;
    }
    return $targetNorm === $candidateNorm;
}

function qk1_baidu_parse_search_response($targetUrl, $searchUrl, $resp, $timeout)
{
    if (!is_array($resp) || empty($resp['ok']) || trim((string)($resp['body'] ?? '')) === '') {
        return [
            'matched' => false,
            'blocked' => false,
            'no_result' => false,
            'request_failed' => true,
            'message' => '百度请求失败',
            'search_url' => $searchUrl
        ];
    }
    $body = (string)$resp['body'];
    $bodyText = qk1_normalize_text($body);
    if (qk1_baidu_is_blocked($bodyText)) {
        return [
            'matched' => false,
            'blocked' => true,
            'no_result' => false,
            'request_failed' => false,
            'message' => '百度校验受限，建议重试',
            'search_url' => $searchUrl
        ];
    }
    $noResult = qk1_baidu_has_no_result($bodyText);
    $candidateUrls = qk1_baidu_collect_candidate_urls($body);
    foreach ($candidateUrls as $candidateUrl) {
        if (qk1_url_matches($targetUrl, $candidateUrl)) {
            return [
                'matched' => true,
                'blocked' => false,
                'no_result' => false,
                'request_failed' => false,
                'message' => '百度结果命中当前URL',
                'search_url' => $searchUrl
            ];
        }
        if (stripos($candidateUrl, 'baidu.com/link?') !== false) {
            $finalUrl = qk1_resolve_url($candidateUrl, $timeout);
            if (qk1_url_matches($targetUrl, $finalUrl)) {
                return [
                    'matched' => true,
                    'blocked' => false,
                    'no_result' => false,
                    'request_failed' => false,
                    'message' => '百度结果命中当前URL',
                    'search_url' => $searchUrl
                ];
            }
        }
    }
    if ($noResult) {
        return [
            'matched' => false,
            'blocked' => false,
            'no_result' => true,
            'request_failed' => false,
            'message' => '百度暂无该URL结果',
            'search_url' => $searchUrl
        ];
    }
    return [
        'matched' => false,
        'blocked' => false,
        'no_result' => false,
        'request_failed' => false,
        'message' => !empty($candidateUrls) ? '百度结果存在，但未命中当前URL' : '百度未命中当前URL',
        'search_url' => $searchUrl
    ];
}

function qk1_baidu_check_urls_batch($siteUrl, $items, $timeout)
{
    $results = [];
    if (!is_array($items) || empty($items)) {
        return $results;
    }
    $contexts = [];
    foreach ($items as $item) {
        $key = trim((string)($item['key'] ?? ''));
        $url = trim((string)($item['url'] ?? ''));
        if ($key === '') {
            continue;
        }
        if ($url === '') {
            $results[$key] = [
                'status' => '未收录',
                'message' => 'URL为空',
                'search_url' => ''
            ];
            continue;
        }
        $contexts[$key] = [
            'url' => $url,
            'queries' => qk1_baidu_build_queries($siteUrl, $url),
            'success_count' => 0,
            'blocked_count' => 0,
            'saw_no_result' => false,
            'last_message' => '百度暂无相关结果',
            'last_search_url' => '',
            'done' => false
        ];
    }
    if (empty($contexts)) {
        return $results;
    }
    $maxQueryCount = 0;
    foreach ($contexts as $context) {
        $count = count($context['queries']);
        if ($count > $maxQueryCount) {
            $maxQueryCount = $count;
        }
    }
    for ($stage = 0; $stage < $maxQueryCount; $stage++) {
        $requestMap = [];
        foreach ($contexts as $key => $context) {
            if (!empty($context['done']) || !isset($context['queries'][$stage])) {
                continue;
            }
            $searchUrl = qk1_baidu_search_url($context['queries'][$stage]);
            $contexts[$key]['last_search_url'] = $searchUrl;
            $requestMap[$key] = $searchUrl;
        }
        if (empty($requestMap)) {
            continue;
        }
        $responses = qk1_http_multi_get($requestMap, $timeout, 6);
        foreach ($requestMap as $key => $searchUrl) {
            if (empty($contexts[$key]) || !empty($contexts[$key]['done'])) {
                continue;
            }
            $parsed = qk1_baidu_parse_search_response($contexts[$key]['url'], $searchUrl, $responses[$key] ?? [], $timeout);
            if (!empty($parsed['matched'])) {
                $results[$key] = [
                    'status' => '已收录',
                    'message' => $parsed['message'],
                    'search_url' => $parsed['search_url']
                ];
                $contexts[$key]['done'] = true;
                continue;
            }
            if (empty($parsed['request_failed'])) {
                $contexts[$key]['success_count']++;
            }
            if (!empty($parsed['blocked'])) {
                $contexts[$key]['blocked_count']++;
            }
            if (!empty($parsed['no_result'])) {
                $contexts[$key]['saw_no_result'] = true;
            }
            $contexts[$key]['last_message'] = (string)$parsed['message'];
        }
    }
    foreach ($contexts as $key => $context) {
        if (!empty($context['done'])) {
            continue;
        }
        if ($context['blocked_count'] > 0 && $context['success_count'] === $context['blocked_count']) {
            $results[$key] = [
                'status' => '未收录',
                'message' => '百度校验受限，建议重试',
                'search_url' => $context['last_search_url']
            ];
            continue;
        }
        if (!empty($context['saw_no_result'])) {
            $results[$key] = [
                'status' => '未收录',
                'message' => '百度暂无该URL结果',
                'search_url' => $context['last_search_url']
            ];
            continue;
        }
        $results[$key] = [
            'status' => '未收录',
            'message' => $context['last_message'] === '百度请求失败' ? '百度未命中当前URL' : $context['last_message'],
            'search_url' => $context['last_search_url']
        ];
    }
    return $results;
}

if (
    $_SERVER['REQUEST_METHOD'] === 'POST'
    && isset($_POST['action'])
    && isset($_POST['plugin'])
    && (string)$_POST['plugin'] === $plugin_identifier
    && (string)$_POST['action'] !== 'render_tool_html'
) {
    header('Content-Type: application/json; charset=utf-8');
    $root = realpath(dirname(__DIR__, 2));
    $common = $root . '/common.php';
    if (file_exists($common)) {
        require_once $common;
    }

    $fileContent = @file_get_contents(__FILE__);
    $pairs = $fileContent !== false ? qk1_extract_config_params($fileContent) : [];
    $siteUrl = qk1_site_url();
    $action = (string)$_POST['action'];

    if ($action === 'fetch_items') {
        if (!function_exists('get_db_connection')) {
            echo json_encode(['success' => false, 'message' => '缺少系统函数'], JSON_UNESCAPED_UNICODE);
            exit;
        }
        $page = isset($_POST['page']) ? intval($_POST['page']) : 1;
        if ($page <= 0) {
            $page = 1;
        }
        $pageSize = isset($_POST['page_size']) ? intval($_POST['page_size']) : (isset($_POST['limit']) ? intval($_POST['limit']) : intval($pairs['default_limit'] ?? 30));
        if ($pageSize <= 0) {
            $pageSize = 30;
        }
        if ($pageSize > 100) {
            $pageSize = 100;
        }
        $source = isset($_POST['source']) ? trim((string)$_POST['source']) : 'all';
        if (!in_array($source, ['all', 'post', 'goods', 'category'], true)) {
            $source = 'all';
        }
        $conn = get_db_connection();
        if (!$conn || isset($conn->connect_error)) {
            echo json_encode(['success' => false, 'message' => '数据库连接失败'], JSON_UNESCAPED_UNICODE);
            exit;
        }
        $paged = qk1_fetch_items_paged($conn, $siteUrl, $source, $page, $pageSize);
        @mysqli_close($conn);
        $total = intval($paged['total'] ?? 0);
        $items = is_array($paged['items'] ?? null) ? $paged['items'] : [];
        if ($total <= 0) {
            echo json_encode(['success' => false, 'message' => '缺少巡检数据'], JSON_UNESCAPED_UNICODE);
            exit;
        }
        $totalPages = max(1, (int)ceil($total / $pageSize));
        if ($page > $totalPages) {
            $page = $totalPages;
        }
        echo json_encode([
            'success' => true,
            'items' => $items,
            'total' => $total,
            'page' => $page,
            'page_size' => $pageSize,
            'total_pages' => $totalPages
        ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        exit;
    }

    if ($action === 'check_urls') {
        $itemsRaw = isset($_POST['items']) ? (string)$_POST['items'] : '[]';
        $items = json_decode($itemsRaw, true);
        if (!is_array($items) || empty($items)) {
            echo json_encode(['success' => false, 'message' => '请先勾选要检查的URL'], JSON_UNESCAPED_UNICODE);
            exit;
        }
        $timeout = isset($_POST['timeout']) ? intval($_POST['timeout']) : intval($pairs['check_timeout'] ?? 10);
        if ($timeout <= 0) {
            $timeout = 10;
        }
        if ($timeout > 20) {
            $timeout = 20;
        }
        $results = [];
        $items = array_slice($items, 0, 50);
        $checkedMap = qk1_baidu_check_urls_batch($siteUrl, $items, $timeout);
        foreach ($items as $item) {
            $url = trim((string)($item['url'] ?? ''));
            $key = trim((string)($item['key'] ?? ''));
            $checked = $checkedMap[$key] ?? ['status' => '未收录', 'message' => '检查失败', 'search_url' => ''];
            $results[] = [
                'key' => $key,
                'type_name' => (string)($item['type_name'] ?? ''),
                'id' => (string)($item['id'] ?? ''),
                'title' => (string)($item['title'] ?? ''),
                'url' => $url,
                'baidu_status' => $checked['status'],
                'baidu_detail' => $checked['message'],
                'search_url' => $checked['search_url'],
                'updated_at' => (string)($item['updated_at'] ?? '')
            ];
        }
        echo json_encode([
            'success' => true,
            'results' => $results
        ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        exit;
    }

    echo json_encode(['success' => false, 'message' => '未知操作'], JSON_UNESCAPED_UNICODE);
    exit;
}

$fileContent = @file_get_contents(__FILE__);
$pairs = $fileContent !== false ? qk1_extract_config_params($fileContent) : [];
$defaultLimit = intval($pairs['default_limit'] ?? 30);
if ($defaultLimit <= 0) {
    $defaultLimit = 30;
}
?>

<div class="qk1-wrap">
    <div class="qk1-card">

        <div class="qk1-toolbar">
            <div class="qk1-inline">
                <select class="qk1-input" id="qk1Source">
                    <option value="" selected>请选择范围</option>
                    <option value="all">全部</option>
                    <option value="post">文章</option>
                    <option value="goods">商品</option>
                    <option value="category">栏目</option>
                </select>
            </div>
            <div class="qk1-actions qk1-actions-inline">
                <button class="qk1-btn qk1-primary" id="qk1CheckSelected">检查选中</button>
                <div class="qk1-tip qk1-tip-inline" id="qk1Status">请先选择范围，再勾选需要检查的URL。</div>
            </div>
        </div>
    </div>

    <div class="qk1-card">

        <div class="qk1-table-wrap">
            <table class="qk1-table">
                <thead>
                    <tr>
                        <th class="qk1-col-check"><input type="checkbox" id="qk1CheckAll"></th>
                        <th>类型</th>
                        <th>ID</th>
                        <th>标题</th>
                        <th>URL</th>
                        <th>收录结果</th>
                        <th>搜索页</th>
                    </tr>
                </thead>
                <tbody id="qk1Tbody"></tbody>
            </table>
        </div>
        <div class="qk1-pagination" id="qk1Pagination"></div>
    </div>
</div>

<style>
    .qk1-wrap {
        font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial;
        color: #0f172a;



        box-sizing: border-box;
        --qk1-border: #e5e7eb;
        --qk1-soft-border: #eef2f7;
        --qk1-muted: #64748b;
        --qk1-muted-2: #94a3b8;
        --qk1-bg: #f6f7fb;
        --qk1-card: #ffffff;
        --qk1-primary: #FF5722;
        --qk1-primary-hover: #ec5d31ff;

        --qk1-shadow: 0 10px 30px rgba(15, 23, 42, .06);

    }

    background: var(--qk1-card);
    border: 1px solid var(--qk1-border);
    border-radius: var(--qk1-radius);
    padding: 12px 14px;
    margin-bottom: 10px;
    box-shadow: var(--qk1-shadow);
    margin-bottom: 10px;
    box-sizing: border-box;

    .qk1-title {
        font-size: 14px;
        font-weight: 700;
        margin-bottom: 8px;
    }

    .qk1-toolbar {
        display: flex;
        align-items: center;
        gap: 10px;
        flex-wrap: nowrap;
        margin-bottom: 15px;
    }

    .qk1-inline {
        display: inline-flex;
        align-items: center;
        gap: 6px;
    }

    .qk1-inline-limit {
        width: 118px;
    }

    .qk1-label {
        font-size: 12px;
        color: var(--qk1-muted);
        white-space: nowrap;
    }

    .qk1-input {
        height: 32px;
        border: 1px solid var(--qk1-border);
        border-radius: 4px;
        padding: 0 10px;
        font-size: 12px;
        box-sizing: border-box;
        outline: none;
        width: 100%;
        background: #fff;
        color: #0f172a;
        transition: border-color .18s ease, box-shadow .18s ease;
    }

    #qk1Source {
        width: 110px;
    }

    .qk1-input:focus {
        border-color: rgba(37, 99, 235, .65);
        box-shadow: 0 0 0 4px rgba(37, 99, 235, .12);
    }

    .qk1-actions {
        display: flex;
        gap: 8px;
        flex-wrap: wrap;
        align-items: center;
    }

    .qk1-actions-inline {
        flex-wrap: nowrap;
    }

    .qk1-btn {
        min-width: 110px;
        height: 32px;
        border: 1px solid var(--qk1-border);
        background: #fff;
        color: #111827;
        border-radius: 4px;
        padding: 0 14px;
        font-size: 12px;
        cursor: pointer;
        transition: background-color .18s ease, border-color .18s ease, box-shadow .18s ease, transform .18s ease;
    }

    .qk1-btn:hover {
        background: #f1f5f9;
        border-color: #cbd5e1;
        box-shadow: 0 8px 20px rgba(15, 23, 42, .06);
        transform: translateY(-1px);
    }

    .qk1-btn:active {
        transform: translateY(0);
    }

    .qk1-btn:focus-visible {
        outline: none;
        box-shadow: 0 0 0 4px rgba(37, 99, 235, .14);
    }

    .qk1-primary {
        background: var(--qk1-primary);
        border-color: var(--qk1-primary);
        color: #fff;
    }

    .qk1-primary:hover {
        background: var(--qk1-primary-hover);
        border-color: var(--qk1-primary-hover);
        color: #fff;
        box-shadow: 0 10px 24px rgba(37, 99, 235, .22);
    }

    .qk1-tip {
        margin-top: 8px;
        font-size: 12px;
        line-height: 1.5;
        color: var(--qk1-muted);



        padding: 8px 10px;
    }

    .qk1-tip-inline {
        margin-top: 0;
        margin-left: 2px;
        flex: 0 1 260px;
        min-width: 160px;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    .qk1-table-wrap {
        overflow: auto;
        border: 1px solid var(--qk1-border);
        border-radius: var(--qk1-radius);
        background: #fff;
        max-height: 335px;
    }

    .qk1-table {
        width: 100%;
        border-collapse: collapse;
        table-layout: fixed;
    }

    .qk1-table th,
    .qk1-table td {
        border-bottom: 1px solid var(--qk1-soft-border);
        padding: 9px 8px;
        font-size: 12px;
        text-align: left;
        vertical-align: top;
        word-break: break-all;
    }

    .qk1-table th {
        color: var(--qk1-muted);
        background: #f8fafc;
        position: sticky;
        top: 0;
        z-index: 1;
        border-bottom: 1px solid var(--qk1-border);
    }

    .qk1-col-check {
        width: 32px;
        text-align: center;
    }

    .qk1-table tbody tr td {
        background: #fff;
        transition: background-color .12s ease;
    }

    .qk1-table tbody tr:hover td {
        background: #f8fafc;
    }

    .qk1-table input[type="checkbox"] {
        width: 14px;
        height: 14px;
        accent-color: var(--qk1-primary);
        cursor: pointer;
    }

    .qk1-table th:nth-child(2),
    .qk1-table td:nth-child(2) {
        width: 52px;
    }

    .qk1-table th:nth-child(3),
    .qk1-table td:nth-child(3) {
        width: 52px;
    }

    .qk1-table th:nth-child(6),
    .qk1-table td:nth-child(6) {
        width: 84px;
    }

    .qk1-table th:nth-child(7),
    .qk1-table td:nth-child(7) {
        width: 72px;
    }

    .qk1-badge {
        display: inline-flex;
        align-items: center;
        justify-content: center;

        height: 22px;
        border-radius: 999px;
        padding: 0 10px;
        font-size: 12px;
        font-weight: 600;
        white-space: nowrap;
    }

    .qk1-badge-pending {
        background: #f1f5f9;
        color: #475569;
    }

    .qk1-badge-indexed {
        background: #dcfce7;
        color: #166534;
    }

    .qk1-badge-unindexed {
        background: #fee2e2;
        color: #991b1b;
    }

    .qk1-link {
        color: #0f172a;
        text-decoration: none;
    }

    .qk1-link:hover {
        text-decoration: underline;
    }

    .qk1-pagination {
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 6px;
        padding-top: 10px;
        flex-wrap: wrap;
    }

    .qk1-page-info {
        font-size: 12px;
        color: var(--qk1-muted);
        white-space: nowrap;
    }

    .qk1-page-btn {
        min-width: 58px;
        height: 28px;
        border: 1px solid var(--qk1-border);
        background: #fff;
        color: #111827;
        border-radius: 10px;
        font-size: 12px;
        cursor: pointer;
        padding: 0 10px;
        transition: background-color .18s ease, border-color .18s ease, box-shadow .18s ease, transform .18s ease;
    }

    .qk1-page-btn:hover {
        background: #f1f5f9;
        border-color: #cbd5e1;
    }

    .qk1-page-btn.qk1-page-num {
        min-width: 34px;
        padding: 0 10px;
    }

    .qk1-page-btn.qk1-active {
        background: var(--qk1-primary);
        border-color: var(--qk1-primary);
        color: #fff;
    }

    .qk1-page-btn.qk1-active:hover {
        background: var(--qk1-primary-hover);
        border-color: var(--qk1-primary-hover);
    }

    .qk1-page-btn:disabled {
        color: #9ca3af;
        background: #f8fafc;
        cursor: not-allowed;
    }

    .qk1-page-select,
    .qk1-page-size {
        height: 28px;
        border: 1px solid var(--qk1-border);
        border-radius: 10px;
        font-size: 12px;
        padding: 0 28px 0 8px;
        background: #fff;
        color: #111827;
        outline: none;
        box-sizing: border-box;
        width: auto;
        transition: border-color .18s ease, box-shadow .18s ease;
        appearance: none;
        -moz-appearance: none;
        -webkit-appearance: none;
        background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='%23666' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
        background-repeat: no-repeat;
        background-position: right 6px center;
        background-size: 16px;
    }

    .qk1-page-select:focus,
    .qk1-page-size:focus {
        border-color: rgba(37, 99, 235, .65);
        box-shadow: 0 0 0 4px rgba(37, 99, 235, .12);
    }

    .qk1-page-select {
        min-width: 0;
    }

    .qk1-page-size {
        min-width: 0;
    }

    .qk1-btn-loading {
        position: relative;
        pointer-events: none;
        opacity: .88;
    }

    .qk1-btn-loading::before {
        content: '';
        width: 12px;
        height: 12px;
        border: 2px solid rgba(255, 255, 255, .45);
        border-top-color: #fff;
        border-radius: 50%;
        display: inline-block;
        margin-right: 6px;
        vertical-align: -2px;
        animation: qk1spin .8s linear infinite;
    }

    @keyframes qk1spin {
        from {
            transform: rotate(0deg);
        }

        to {
            transform: rotate(360deg);
        }
    }
</style>

<script>
    (function() {
        const plugin = '<?php echo $plugin_identifier; ?>';
        const state = {
            pageRows: [],
            cache: {},
            selected: {},
            source: '',
            currentPage: 1,
            pageSize: <?php echo intval($defaultLimit); ?>,
            total: 0,
            totalPages: 1
        };

        function $(id) {
            return document.getElementById(id);
        }

        function toast(message, type) {
            try {
                if (window.parent && typeof window.parent.showToast === 'function') {
                    window.parent.showToast(message, type === 'success' ? 'success' : 'error', 2400);
                    return;
                }
            } catch (e) {}
            alert(message);
        }

        function escapeHtml(text) {
            return String(text || '')
                .replace(/&/g, '&amp;')
                .replace(/</g, '&lt;')
                .replace(/>/g, '&gt;')
                .replace(/"/g, '&quot;')
                .replace(/'/g, '&#39;');
        }

        function setStatus(text) {
            $('qk1Status').textContent = text;
        }

        function postAction(action, extra) {
            const body = new URLSearchParams(Object.assign({
                plugin,
                action
            }, extra || {}));
            return fetch('/admin/Plugin.php', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                    'X-Requested-With': 'XMLHttpRequest'
                },
                body
            }).then(function(r) {
                return r.json();
            });
        }

        function badgeClass(status) {
            if (status === '已收录') return 'qk1-badge qk1-badge-indexed';
            if (status === '未收录') return 'qk1-badge qk1-badge-unindexed';
            return 'qk1-badge qk1-badge-pending';
        }

        function setCheckButtonLoading(loading) {
            const btn = $('qk1CheckSelected');
            if (!btn) return;
            if (loading) {
                btn.disabled = true;
                btn.classList.add('qk1-btn-loading');
                btn.textContent = '检查中...';
            } else {
                btn.disabled = false;
                btn.classList.remove('qk1-btn-loading');
                btn.textContent = '检查选中';
            }
        }

        function normalizePageSize() {
            const el = $('qk1Limit');
            if (!el) {
                const sizeFallback = Math.max(1, Math.min(100, parseInt(state.pageSize || '30', 10) || 30));
                state.pageSize = sizeFallback;
                return sizeFallback;
            }
            const raw = parseInt(el.value || '30', 10) || 30;
            const size = Math.max(1, Math.min(100, raw));
            el.value = String(size);
            state.pageSize = size;
            return size;
        }

        function urlText(url) {
            const s = String(url || '');
            if (!s) return '';
            try {
                const u = new URL(s);
                return (u.pathname || '/') + (u.search || '') + (u.hash || '');
            } catch (e) {
                const noProto = s.replace(/^https?:\/\/[^/]+/i, '');
                return noProto || s;
            }
        }

        function rowPayload(row) {
            return {
                key: row.key,
                type_name: row.type_name,
                id: row.id,
                title: row.title,
                url: row.url,
                updated_at: row.updated_at
            };
        }

        function mergeRow(row, redraw) {
            if (!row || !row.key) {
                return null;
            }
            const old = state.cache[row.key] || {};
            const merged = Object.assign({}, old, row);
            merged.baidu_status = merged.baidu_status || '待巡检';
            merged.baidu_detail = merged.baidu_detail || '';
            merged.search_url = merged.search_url || '';
            state.cache[row.key] = merged;
            if (redraw) {
                state.pageRows = state.pageRows.map(function(item) {
                    return item.key === row.key ? merged : item;
                });
                renderRows();
            }
            return merged;
        }

        function resetList(source) {
            state.source = source || '';
            state.selected = {};
            state.cache = {};
            state.pageRows = [];
            state.currentPage = 1;
            state.total = 0;
            state.totalPages = 1;
            renderRows();
        }

        function renderPagination() {
            const wrap = $('qk1Pagination');
            if (!wrap) return;
            const total = state.total || 0;
            const totalPages = state.totalPages || 1;
            const currentPage = state.currentPage || 1;
            const shownMax = Math.min(5, totalPages);
            let pageButtons = '';
            for (let i = 1; i <= shownMax; i++) {
                pageButtons += '<button class="qk1-page-btn qk1-page-num ' + (i === currentPage ? 'qk1-active' : '') + '" data-page="' + i + '">' + i + '</button>';
            }
            let pageOptions = '';
            for (let p = 1; p <= totalPages; p++) {
                pageOptions += '<option value="' + p + '"' + (p === currentPage ? ' selected' : '') + '>' + p + '</option>';
            }
            const pageSize = normalizePageSize();
            const sizeOptions = [10, 20, 30, 50, 100].map(function(n) {
                return '<option value="' + n + '"' + (n === pageSize ? ' selected' : '') + '>' + n + '</option>';
            }).join('');

            wrap.innerHTML =
                '<button class="qk1-page-btn" id="qk1PrevPage" ' + (currentPage <= 1 ? 'disabled' : '') + '>上一页</button>' +
                pageButtons +
                '<select class="qk1-page-select" id="qk1PageSelect" ' + (totalPages <= 1 ? 'disabled' : '') + '>' + pageOptions + '</select>' +
                '<button class="qk1-page-btn" id="qk1NextPage" ' + (currentPage >= totalPages ? 'disabled' : '') + '>下一页</button>' +
                '<select class="qk1-page-size" id="qk1Limit">' + sizeOptions + '</select>' +
                '<span class="qk1-page-info">共 ' + total + ' 条，第 ' + currentPage + '/' + totalPages + ' 页</span>';
            const prev = $('qk1PrevPage');
            const next = $('qk1NextPage');
            if (prev) {
                prev.addEventListener('click', function() {
                    if (state.currentPage > 1) {
                        fetchPage(state.currentPage - 1);
                    }
                });
            }
            if (next) {
                next.addEventListener('click', function() {
                    if (state.currentPage < state.totalPages) {
                        fetchPage(state.currentPage + 1);
                    }
                });
            }
            Array.prototype.slice.call(wrap.querySelectorAll('button[data-page]')).forEach(function(btn) {
                btn.addEventListener('click', function() {
                    const p = parseInt(btn.getAttribute('data-page') || '1', 10) || 1;
                    fetchPage(p);
                });
            });
            const sel = $('qk1PageSelect');
            if (sel) {
                sel.addEventListener('change', function() {
                    const p = parseInt(sel.value || '1', 10) || 1;
                    fetchPage(p);
                });
            }
            const sizeSel = $('qk1Limit');
            if (sizeSel) {
                sizeSel.addEventListener('change', function() {
                    normalizePageSize();
                    fetchPage(1, true);
                });
            }
        }

        function renderRows() {
            const tbody = $('qk1Tbody');
            if (!state.pageRows.length) {
                tbody.innerHTML = '';
                $('qk1CheckAll').checked = false;
                renderPagination();
                return;
            }
            tbody.innerHTML = state.pageRows.map(function(row) {
                const search = row.search_url ? '<a class="qk1-link" href="' + escapeHtml(row.search_url) + '" target="_blank">打开</a>' : '-';
                const checked = state.selected[row.key] ? 'checked' : '';
                return '' +
                    '<tr data-key="' + escapeHtml(row.key) + '">' +
                    '<td class="qk1-col-check"><input type="checkbox" class="qk1-row-check" data-key="' + escapeHtml(row.key) + '" ' + checked + '></td>' +
                    '<td>' + escapeHtml(row.type_name || '') + '</td>' +
                    '<td>' + escapeHtml(row.id || '-') + '</td>' +
                    '<td>' + escapeHtml(row.title || '') + '</td>' +
                    '<td><a class="qk1-link" href="' + escapeHtml(row.url || '#') + '" target="_blank">' + escapeHtml(urlText(row.url || '')) + '</a></td>' +
                    '<td><span class="' + badgeClass(row.baidu_status || '') + '">' + escapeHtml(row.baidu_status || '待巡检') + '</span></td>' +
                    '<td>' + search + '</td>' +
                    '</tr>';
            }).join('');
            syncCheckAll();
            renderPagination();
        }

        function applyCheckResult(row) {
            mergeRow(row, true);
        }

        function checkSingleRow(row) {
            return postAction('check_urls', {
                items: JSON.stringify([rowPayload(row)])
            }).then(function(data) {
                if (!data || !data.success || !Array.isArray(data.results) || !data.results[0]) {
                    throw new Error(data && data.message ? data.message : '检查失败');
                }
                return data.results[0];
            });
        }

        function syncCheckAll() {
            if (!state.pageRows.length) {
                $('qk1CheckAll').checked = false;
                return;
            }
            let allChecked = true;
            state.pageRows.forEach(function(row) {
                if (!state.selected[row.key]) {
                    allChecked = false;
                }
            });
            $('qk1CheckAll').checked = allChecked;
        }

        function loadItems() {
            const v = ($('qk1Source').value || '').trim();
            if (!v) {
                resetList('');
                setStatus('请先选择范围，再勾选需要检查的URL。');
                return;
            }
            normalizePageSize();
            resetList(v);
            fetchPage(1, true);
        }

        function fetchPage(page, resetStatusText) {
            const targetPage = Math.max(1, parseInt(page || '1', 10) || 1);
            if (resetStatusText) {
                setStatus('正在加载巡检列表...');
            } else {
                setStatus('正在加载第 ' + targetPage + ' 页...');
            }
            postAction('fetch_items', {
                source: state.source,
                page: String(targetPage),
                page_size: String(normalizePageSize())
            }).then(function(data) {
                if (!data || !data.success) {
                    toast(data && data.message ? data.message : '加载失败', 'error');
                    setStatus('加载失败');
                    return;
                }
                state.total = parseInt(data.total || '0', 10) || 0;
                state.currentPage = parseInt(data.page || String(targetPage), 10) || targetPage;
                state.pageSize = parseInt(data.page_size || String(state.pageSize), 10) || state.pageSize;
                state.totalPages = parseInt(data.total_pages || '1', 10) || 1;
                state.pageRows = (Array.isArray(data.items) ? data.items : []).map(function(row) {
                    return mergeRow(row, false);
                });
                renderRows();
                setStatus('已加载 ' + state.total + ' 条URL，第 ' + state.currentPage + '/' + state.totalPages + ' 页，可翻页后勾选检查。');
            }).catch(function() {
                toast('加载失败', 'error');
                setStatus('加载失败');
            });
        }

        function selectedRows() {
            return Object.keys(state.selected).filter(function(key) {
                return !!state.selected[key];
            }).map(function(key) {
                return state.cache[key];
            }).filter(function(row) {
                return !!row;
            });
        }

        function checkSelected() {
            const rows = selectedRows();
            if (!rows.length) {
                toast('请先勾选要检查的URL', 'error');
                return;
            }
            let rowsToSend = rows;
            if (rows.length > 50) {
                rowsToSend = rows.slice(0, 50);
                toast('一次最多检查50条，已只取前50条', 'error');
            }
            setCheckButtonLoading(true);
            let completed = 0;
            let failed = 0;
            let active = 0;
            let index = 0;
            const total = rowsToSend.length;
            const concurrency = Math.min(4, total);
            setStatus('正在检查 0/' + total + ' 条URL...');

            function finishOne() {
                completed++;
                setStatus('正在检查 ' + completed + '/' + total + ' 条URL...');
                if (completed >= total) {
                    setCheckButtonLoading(false);
                    if (failed > 0) {
                        setStatus('检查完成，成功 ' + (total - failed) + ' 条，失败 ' + failed + ' 条。');
                        toast('检查完成，部分失败', 'error');
                    } else {
                        setStatus('检查完成，共处理 ' + total + ' 条URL。');
                        toast('检查完成', 'success');
                    }
                    return;
                }
                runNext();
            }

            function runNext() {
                while (active < concurrency && index < total) {
                    const row = rowsToSend[index++];
                    active++;
                    checkSingleRow(row).then(function(result) {
                        applyCheckResult(result);
                    }).catch(function() {
                        failed++;
                    }).finally(function() {
                        active--;
                        finishOne();
                    });
                }
            }

            runNext();
        }

        document.addEventListener('change', function(e) {
            if (e.target && e.target.classList.contains('qk1-row-check')) {
                const key = e.target.getAttribute('data-key') || '';
                state.selected[key] = !!e.target.checked;
                syncCheckAll();
            }
        });

        $('qk1CheckAll').addEventListener('change', function() {
            const checked = !!this.checked;
            state.pageRows.forEach(function(row) {
                state.selected[row.key] = checked;
            });
            renderRows();
        });

        renderRows();
        $('qk1Source').addEventListener('change', loadItems);
        $('qk1CheckSelected').addEventListener('click', checkSelected);
    })();
</script>