<?php

function spider_force_config(){
    $file = __DIR__ . '/../config/spider_force.php';
    $cfg = is_file($file) ? include $file : array();
    if(!is_array($cfg)) $cfg = array();
    $cfg = array_merge(array(
        'enabled' => 0,
        'pass_weight' => 0,
        'status' => 302,
        'probability' => 30,
        'terminal' => 'all',
        'spider_all' => 0,
        'spiders' => array('Baiduspider'),
        'dedupe' => 1,
        'start_time' => '',
        'end_time' => '',
        'target_links' => array(),
        'rules' => array(),
    ), $cfg);
    $cfg['status'] = !empty($cfg['pass_weight']) ? 301 : (intval($cfg['status']) === 301 ? 301 : 302);
    return $cfg;
}

function spider_force_is_mobile($ua){
    return preg_match('~mobile|android|iphone|ipad|ipod|windows phone|mobi~i', (string)$ua) === 1;
}

function spider_force_in_time($cfg){
    $now = time();
    $start = trim((string)($cfg['start_time'] ?? ''));
    $end = trim((string)($cfg['end_time'] ?? ''));
    if($start !== ''){
        $ts = strtotime($start);
        if($ts && $now < $ts) return false;
    }
    if($end !== ''){
        $ts = strtotime($end);
        if($ts && $now > $ts) return false;
    }
    return true;
}

function spider_force_path($value){
    $value = trim((string)$value);
    if($value === '') return '';
    if(preg_match('~^https?://[^/]+(/.*)?$~i', $value, $m)){
        $value = $m[1] ?? '/';
    }
    if($value === '') $value = '/';
    if($value[0] !== '/') $value = '/' . $value;
    return $value;
}

function spider_force_target($target){
    $target = trim((string)$target);
    if($target === '') return '';
    if(preg_match('~^https?://~i', $target)) return $target;
    if($target[0] !== '/') $target = '/' . $target;
    return $target;
}

function spider_force_rule_match($source, $uriPath){
    $source = spider_force_path($source);
    if($source === '') return false;
    if(substr($source, -1) === '*'){
        $prefix = rtrim(substr($source, 0, -1), '*');
        return $prefix === '' || strpos($uriPath, $prefix) === 0;
    }
    return rtrim($source, '/') === rtrim($uriPath, '/');
}

function spider_force_random_string($len, $type){
    $len = max(1, min(64, intval($len)));
    $sets = array(
        'number' => '0123456789',
        'letter' => 'abcdefghijklmnopqrstuvwxyz',
        'mixed' => 'abcdefghijklmnopqrstuvwxyz0123456789',
    );
    $chars = $sets[$type] ?? $sets['mixed'];
    $out = '';
    for($i=0; $i<$len; $i++){
        $out .= $chars[mt_rand(0, strlen($chars)-1)];
    }
    return $out;
}

function spider_force_expand_range($line){
    if(strpos($line, '~') !== false){
        $parts = array_map('trim', explode('~', $line, 2));
        if(count($parts) === 2 && preg_match('~^(.*?)(\d+)(\D*)$~', $parts[0], $a) && preg_match('~^(.*?)(\d+)(\D*)$~', $parts[1], $b)){
            if($a[1] === $b[1] && $a[3] === $b[3]){
                $start = intval($a[2]);
                $end = intval($b[2]);
                if($start <= $end && ($end - $start) <= 1000){
                    $items = array();
                    for($i=$start; $i<=$end; $i++){
                        $items[] = $a[1] . $i . $a[3];
                    }
                    return $items;
                }
            }
        }
        return array($line);
    }
    if(!preg_match('~^(.*?)(\d+)(.*?)(\d+)(.*)$~', $line, $m)) return array($line);
    $a = intval($m[2]);
    $b = intval($m[4]);
    if($a > $b || ($b - $a) > 1000) return array($line);
    $items = array();
    for($i=$a; $i<=$b; $i++){
        $items[] = $m[1] . $i . $m[3] . $i . $m[5];
    }
    return $items;
}

function spider_force_render_target($target){
    $target = preg_replace_callback('/\{数字(\d+)\}/u', function($m){ return spider_force_random_string($m[1], 'number'); }, $target);
    $target = preg_replace_callback('/\{字母(\d+)\}/u', function($m){ return spider_force_random_string($m[1], 'letter'); }, $target);
    $target = preg_replace_callback('/\{字符(\d+)\}/u', function($m){ return spider_force_random_string($m[1], 'mixed'); }, $target);
    $map = array(
        '{日期}' => date('Ymd'),
        '{年}' => date('Y'),
        '{月}' => date('m'),
        '{日}' => date('d'),
        '{时}' => date('H'),
        '{分}' => date('i'),
        '{秒}' => date('s'),
    );
    return strtr($target, $map);
}

function spider_force_seen_file(){
    return __DIR__ . '/../cache/spider/force_seen.php';
}

function spider_force_seen($target){
    $file = spider_force_seen_file();
    $seen = is_file($file) ? include $file : array();
    return is_array($seen) && !empty($seen[md5($target)]);
}

function spider_force_mark_seen($target){
    $file = spider_force_seen_file();
    $seen = is_file($file) ? include $file : array();
    if(!is_array($seen)) $seen = array();
    $seen[md5($target)] = array('target'=>$target, 'time'=>date('Y-m-d H:i:s'));
    if(!is_dir(dirname($file))) @mkdir(dirname($file), 0777, true);
    file_put_contents($file, "<?php\nreturn " . var_export($seen, true) . ";\n", LOCK_EX);
}

function spider_force_pick_target($cfg, $uriPath){
    $targets = array();
    foreach(($cfg['target_links'] ?? array()) as $line){
        $line = trim((string)$line);
        if($line === '') continue;
        foreach(spider_force_expand_range($line) as $expanded){
            $targets[] = $expanded;
        }
    }
    if(!$targets && !empty($cfg['rules'])){
        foreach($cfg['rules'] as $rule){
            if(empty($rule['enabled'])) continue;
            if(!spider_force_rule_match($rule['source'] ?? '', $uriPath)) continue;
            $targets[] = $rule['target'] ?? '';
        }
    }
    if(!$targets) return '';
    shuffle($targets);
    foreach($targets as $target){
        $target = spider_force_target(spider_force_render_target($target));
        if($target === '') continue;
        if(!empty($cfg['dedupe']) && spider_force_seen($target)) continue;
        return $target;
    }
    return spider_force_target(spider_force_render_target($targets[0]));
}

function spider_force_log($spider, $source, $target, $status){
    $dir = __DIR__ . '/../cache/spider';
    if(!is_dir($dir)) @mkdir($dir, 0777, true);
    $host = strtolower($_SERVER['HTTP_HOST'] ?? '');
    $ip = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? ($_SERVER['REMOTE_ADDR'] ?? '');
    if(strpos($ip, ',') !== false) $ip = trim(explode(',', $ip)[0]);
    $line = date('Y-m-d H:i:s') . "\t" . $host . "\t" . $spider . "\t" . $ip . "\t" . $source . "\t" . $target . "\t" . intval($status) . "\n";
    file_put_contents($dir . '/force.log', $line, FILE_APPEND | LOCK_EX);
}

function spider_force_redirect(){
    if(($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'GET' && ($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'HEAD') return;
    if(function_exists('cms_is_admin_request') && cms_is_admin_request()) return;
    $uri = $_SERVER['REQUEST_URI'] ?? '/';
    $uriPath = parse_url($uri, PHP_URL_PATH) ?: '/';
    if(in_array($uriPath, array('/sitemap.xml', '/sitemap.php'), true)) return;

    $cfg = spider_force_config();
    if(empty($cfg['enabled']) || !spider_force_in_time($cfg)) return;

    $ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
    $spider = function_exists('detect_spider') ? detect_spider($ua) : '';
    if($spider === '') return;

    if(empty($cfg['spider_all'])){
        $allowed = $cfg['spiders'] ?? array();
        if(is_array($allowed) && $allowed && !in_array($spider, $allowed, true)) return;
    }

    $terminal = $cfg['terminal'] ?? 'all';
    $isMobile = spider_force_is_mobile($ua);
    if($terminal === 'pc' && $isMobile) return;
    if($terminal === 'mobile' && !$isMobile) return;

    $probability = max(1, min(100, intval($cfg['probability'] ?? 100)));
    if(mt_rand(1, 100) > $probability) return;

    $target = spider_force_pick_target($cfg, $uriPath);
    if($target === '' || rtrim($target, '/') === rtrim($uri, '/')) return;

    $status = intval($cfg['status'] ?? 302);
    if(!in_array($status, array(301, 302), true)) $status = 302;
    spider_force_log($spider, $uri, $target, $status);
    if(!empty($cfg['dedupe'])) spider_force_mark_seen($target);
    header('Location: ' . $target, true, $status);
    exit;
}
