<?php
/**
 * Admin tools: health-check, maintenance mode, admin audit logs, ledger viewer,
 * DB backup, CSV exports, and user management.
 * Designed as an optional service: if SQL is unavailable it falls back safely.
 */

if(!function_exists('bot_admin_tools_default_settings')){
function bot_admin_tools_default_settings(){
    return [
        'maintenance_enabled' => '❌غیر فعال',
        'maintenance_text' => '🛠 ربات موقتاً در حال بروزرسانی است. لطفاً کمی بعد دوباره تلاش کنید.',
        'backup_enabled' => '✅فعال',
        'backup_keep' => 7,
        'last_backup_ts' => 0,
    ];
}}

if(!function_exists('bot_admin_tools_settings')){
function bot_admin_tools_settings(){
    $s = function_exists('bot_data_get_json') ? bot_data_get_json('lib/kodam/data-admin-tools.json', []) : [];
    if(!is_array($s)) $s=[];
    return array_merge(bot_admin_tools_default_settings(), $s);
}}

if(!function_exists('bot_admin_tools_save_settings')){
function bot_admin_tools_save_settings(array $s){
    $s = array_merge(bot_admin_tools_default_settings(), $s);
    if(function_exists('bot_data_put_json')) return bot_data_put_json('lib/kodam/data-admin-tools.json', $s);
    @file_put_contents('lib/kodam/data-admin-tools.json', json_encode($s, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT), LOCK_EX);
    return true;
}}

if(!function_exists('bot_admin_tools_is_owner')){
function bot_admin_tools_is_owner($uid = null){
    global $userID, $admin;
    $uid = $uid === null ? (string)($userID ?? '') : (string)$uid;
    if(function_exists('bot_is_master_admin')) return bot_is_master_admin($uid);
    $main = trim((string)($admin ?? ''));
    return $main !== '' && $uid !== '' && hash_equals($main, $uid);
}}

if(!function_exists('bot_admin_tools_sensitive_denied')){
function bot_admin_tools_sensitive_denied($label = 'این بخش'){
    global $chatID, $msg_id, $userID, $data_id;
    if(function_exists('bot_admin_log')) bot_admin_log($userID ?? '', 'sensitive_denied', 'admin_tools', null, (string)$label);
    if(!empty($data_id) && function_exists('bot')) @bot('answercallbackquery', ['callback_query_id'=>$data_id, 'text'=>'فقط ادمین اصلی دسترسی دارد.', 'show_alert'=>true]);
    if(function_exists('SM')) SM($chatID, "🔒 {$label} فقط برای ادمین اصلی باز است.", 'MarkDown', $msg_id ?? null);
    return true;
}}

if(!function_exists('bot_admin_tools_base_url')){
function bot_admin_tools_base_url(){
    if(defined('BOT_PUBLIC_BASE_URL') && trim((string)BOT_PUBLIC_BASE_URL) !== '') return rtrim((string)BOT_PUBLIC_BASE_URL, '/');
    $https = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || (isset($_SERVER['SERVER_PORT']) && (int)$_SERVER['SERVER_PORT'] === 443);
    $scheme = $https ? 'https' : 'http';
    $host = $_SERVER['HTTP_HOST'] ?? '';
    $dir = isset($_SERVER['SCRIPT_NAME']) ? rtrim(str_replace('\\','/', dirname($_SERVER['SCRIPT_NAME'])), '/') : '';
    if($host === '') return '';
    return $scheme.'://'.$host.($dir && $dir !== '/' ? $dir : '');
}}

if(!function_exists('bot_admin_tools_backup_link')){
function bot_admin_tools_backup_link($file){
    $real = realpath((string)$file);
    $base = realpath('cache/backups');
    if(!$real || !$base || strpos($real, $base) !== 0 || !is_file($real)) return '';
    if(!is_dir('cache')) @mkdir('cache', 0775, true);
    $mapFile = 'cache/backup_downloads.json';
    $map = [];
    if(is_file($mapFile)){
        $tmp = json_decode((string)@file_get_contents($mapFile), true);
        if(is_array($tmp)) $map = $tmp;
    }
    $now = time();
    foreach($map as $tok=>$row){
        if(!is_array($row) || (int)($row['expires'] ?? 0) < $now) unset($map[$tok]);
    }
    $token = bin2hex(random_bytes(24));
    $map[$token] = ['file'=>$real, 'expires'=>$now + 86400, 'created'=>$now];
    @file_put_contents($mapFile, json_encode($map, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT), LOCK_EX);
    $baseUrl = bot_admin_tools_base_url();
    return $baseUrl !== '' ? $baseUrl.'/download_backup.php?token='.$token : '';
}}

if(!function_exists('bot_admin_tools_install_schema')){
function bot_admin_tools_install_schema(){
    if(!function_exists('bot_sql_pdo')) return false;
    $pdo = bot_sql_pdo();
    if(!$pdo) return false;
    $sqls = [
        "CREATE TABLE IF NOT EXISTS bot_admin_logs (
            id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
            actor_id VARCHAR(32) NULL,
            action_type VARCHAR(80) NOT NULL,
            target_type VARCHAR(80) NULL,
            target_id VARCHAR(191) NULL,
            description TEXT NULL,
            data_json LONGTEXT NULL,
            created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
            KEY idx_actor_created (actor_id, created_at),
            KEY idx_action_created (action_type, created_at),
            KEY idx_target (target_type, target_id)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
        "CREATE TABLE IF NOT EXISTS bot_wallet_ledger (
            id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
            user_id VARCHAR(32) NOT NULL,
            asset_type VARCHAR(30) NOT NULL DEFAULT 'diamond',
            delta_amount INT NOT NULL DEFAULT 0,
            balance_before INT NULL,
            balance_after INT NULL,
            reason VARCHAR(100) NULL,
            ref_type VARCHAR(80) NULL,
            ref_id VARCHAR(191) NULL,
            actor_id VARCHAR(32) NULL,
            data_json LONGTEXT NULL,
            created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
            KEY idx_user_created (user_id, created_at),
            KEY idx_reason_created (reason, created_at),
            KEY idx_actor_created (actor_id, created_at),
            KEY idx_ref (ref_type, ref_id)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
        "CREATE TABLE IF NOT EXISTS bot_cron_runs (
            id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
            task_name VARCHAR(80) NOT NULL,
            status VARCHAR(30) NOT NULL DEFAULT 'ok',
            result_json LONGTEXT NULL,
            started_ts BIGINT NOT NULL DEFAULT 0,
            finished_ts BIGINT NOT NULL DEFAULT 0,
            created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
            KEY idx_task_created (task_name, created_at),
            KEY idx_status_created (status, created_at)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
    ];
    try{ foreach($sqls as $sql) $pdo->exec($sql); return true; }
    catch(Throwable $e){ error_log('AdminTools schema failed: '.$e->getMessage()); return false; }
}}

if(!function_exists('bot_admin_log')){
function bot_admin_log($actorId, $actionType, $targetType = null, $targetId = null, $description = '', array $data = []){
    bot_admin_tools_install_schema();
    $payload = json_encode($data, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT);
    if(function_exists('bot_sql_pdo') && ($pdo = bot_sql_pdo())){
        try{
            $st=$pdo->prepare('INSERT INTO bot_admin_logs (actor_id, action_type, target_type, target_id, description, data_json) VALUES (?,?,?,?,?,?)');
            $st->execute([(string)$actorId, (string)$actionType, $targetType, $targetId, (string)$description, $payload]);
            return true;
        }catch(Throwable $e){ error_log('admin log failed: '.$e->getMessage()); }
    }
    $dir='cache'; if(!is_dir($dir)) @mkdir($dir,0775,true);
    @file_put_contents($dir.'/admin_logs.jsonl', json_encode(['time'=>time(),'actor_id'=>$actorId,'action'=>$actionType,'target_type'=>$targetType,'target_id'=>$targetId,'description'=>$description,'data'=>$data], JSON_UNESCAPED_UNICODE)."\n", FILE_APPEND|LOCK_EX);
    return true;
}}

if(!function_exists('bot_ledger_record')){
function bot_ledger_record($userId, $assetType, $delta, $before, $after, $reason = '', array $extra = []){
    bot_admin_tools_install_schema();
    $actorId = isset($extra['admin_id']) ? (string)$extra['admin_id'] : (isset($extra['actor_id']) ? (string)$extra['actor_id'] : null);
    $refType = null; $refId = null;
    foreach(['order_id'=>'order','gift_code'=>'gift_code','payment_id'=>'payment','referral_id'=>'referral'] as $k=>$type){
        if(isset($extra[$k]) && $extra[$k] !== ''){ $refType=$type; $refId=(string)$extra[$k]; break; }
    }
    $payload = json_encode($extra, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT);
    if(function_exists('bot_sql_pdo') && ($pdo = bot_sql_pdo())){
        try{
            $st=$pdo->prepare('INSERT INTO bot_wallet_ledger (user_id, asset_type, delta_amount, balance_before, balance_after, reason, ref_type, ref_id, actor_id, data_json) VALUES (?,?,?,?,?,?,?,?,?,?)');
            $st->execute([(string)$userId, (string)$assetType, (int)$delta, $before===null?null:(int)$before, $after===null?null:(int)$after, (string)$reason, $refType, $refId, $actorId, $payload]);
            return true;
        }catch(Throwable $e){ error_log('ledger record failed: '.$e->getMessage()); }
    }
    return false;
}}

if(!function_exists('bot_admin_tools_maintenance_guard')){
function bot_admin_tools_maintenance_guard(){
    global $isAdmin, $Tc, $chatID;
    if(!isset($Tc) || (string)$Tc !== 'private') return false;
    if(!empty($isAdmin)) return false;
    $s = bot_admin_tools_settings();
    if((string)($s['maintenance_enabled'] ?? '') !== '✅فعال') return false;
    if(function_exists('SM')) SM($chatID, (string)($s['maintenance_text'] ?? '🛠 ربات در حال بروزرسانی است.'), 'MarkDown');
    else if(function_exists('bot')) bot('sendMessage', ['chat_id'=>$chatID, 'text'=>(string)($s['maintenance_text'] ?? '🛠 ربات در حال بروزرسانی است.')]);
    return true;
}}

if(!function_exists('bot_admin_tools_keyboard')){
function bot_admin_tools_keyboard(){
    $sensitiveBackup = bot_admin_tools_is_owner() ? '💾 بکاپ دیتابیس' : '🔒 بکاپ دیتابیس';
    $sensitiveImport = bot_admin_tools_is_owner() ? '📥 ورود بکاپ' : '🔒 ورود بکاپ';
    $sensitiveExport = bot_admin_tools_is_owner() ? '📤 خروجی CSV' : '🔒 خروجی CSV';
    return json_encode(['keyboard'=>[
        [['text'=>'🧪 تست سلامت'], ['text'=>$sensitiveBackup]],
        [['text'=>'🧾 بکاپ تنظیمات'], ['text'=>$sensitiveImport]],
        [['text'=>'👤 مدیریت کاربران'], ['text'=>'📒 دفتر حساب']],
        [['text'=>'🧾 لاگ مدیران'], ['text'=>$sensitiveExport]],
        [['text'=>'🧯 حالت تعمیرات'], ['text'=>'📋 خطاهای اخیر']],
        [['text'=>'🔙بازگشت به منوی ادمین']],
    ], 'resize_keyboard'=>true], JSON_UNESCAPED_UNICODE);
}}

if(!function_exists('bot_admin_tools_panel_text')){
function bot_admin_tools_panel_text(){
    $s=bot_admin_tools_settings();
    return "🧰 **ابزارهای مدیریتی**\n\n".
        "🧯 حالت تعمیرات: **".($s['maintenance_enabled'] ?? '❌غیر فعال')."**\n".
        "💾 بکاپ خودکار: **".($s['backup_enabled'] ?? '✅فعال')."**\n".
        "📦 تعداد بکاپ‌هایی که نگه می‌دارم: **".((int)($s['backup_keep'] ?? 7))."**\n\n".
        "از این بخش سلامت ربات، بکاپ، خروجی، لاگ و کاربران را مدیریت کن.

🔒 بکاپ و خروجی فقط برای ادمین اصلی باز است.";
}}

if(!function_exists('bot_admin_tools_health_text')){
function bot_admin_tools_health_text(){
    global $admin;
    $lines=[];
    $ok=true;
    $add=function($name,$status,$detail='') use (&$lines,&$ok){
        $lines[] = ($status?'✅':'❌').' '.$name.($detail!==''?' — '.$detail:'');
        if(!$status) $ok=false;
    };
    $add('نسخه PHP', version_compare(PHP_VERSION, '7.4.0', '>='), PHP_VERSION);
    $add('cURL', function_exists('curl_init'), function_exists('curl_version') ? (curl_version()['version'] ?? '') : 'غیرفعال');
    $add('PDO MySQL', class_exists('PDO') && in_array('mysql', PDO::getAvailableDrivers(), true));
    $pdo = (function_exists('bot_sql_pdo') ? bot_sql_pdo() : null);
    $add('اتصال دیتابیس', $pdo instanceof PDO, $pdo instanceof PDO ? 'وصل است' : 'وصل نیست/SQL غیرفعال');
    $add('پوشه cache قابل نوشتن', is_dir('cache') && is_writable('cache'));
    $add('config.php', is_file('config.php'));
    $base = defined('BOT_PUBLIC_BASE_URL') ? (string)BOT_PUBLIC_BASE_URL : '';
    $add('آدرس عمومی HTTPS', strpos($base, 'https://') === 0, $base ?: 'BOT_PUBLIC_BASE_URL تنظیم نشده');
    $add('توکن API', defined('API_KEY') && API_KEY !== '' && API_KEY !== 'BOT_TOKEN_HERE');
    $add('ادمین‌های اصلی', function_exists('bot_config_master_admin_ids') ? (count(bot_config_master_admin_ids()) > 0) : !empty($admin), function_exists('bot_master_admin_label') ? bot_master_admin_label() : (string)$admin);
    bot_admin_tools_install_schema();
    $add('جداول مدیریتی', true, 'بررسی/ایجاد شد');
    return "🧪 **گزارش سلامت ربات**\n\n".implode("\n", $lines)."\n\n".($ok?'✅ وضعیت کلی خوب است.':'⚠️ چند مورد نیاز به بررسی دارد.');
}}

if(!function_exists('bot_admin_tools_tail_file')){
function bot_admin_tools_tail_file($file, $lines = 40){
    if(!is_file($file)) return '';
    $arr = @file($file, FILE_IGNORE_NEW_LINES);
    if(!is_array($arr)) return '';
    return implode("\n", array_slice($arr, -$lines));
}}

if(!function_exists('bot_admin_tools_errors_text')){
function bot_admin_tools_errors_text(){
    $parts=[];
    foreach(['debug.log','cache/modular_fatal.log','cache/admin_logs.jsonl'] as $f){
        $tail = bot_admin_tools_tail_file($f, 20);
        if($tail !== '') $parts[] = "**{$f}**\n".$tail;
    }
    if(!$parts) return "📋 **خطاهای اخیر**\n\nخطایی ثبت نشده است.";
    $text="📋 **خطاهای اخیر**\n\n".implode("\n\n", $parts);
    if(strlen($text) > 3800) $text = substr($text, -3800);
    return $text;
}}

if(!function_exists('bot_admin_tools_backup_write_gzip')){
function bot_admin_tools_backup_write_gzip($sourceFile){
    $sourceFile = (string)$sourceFile;
    if(!is_file($sourceFile)) return $sourceFile;
    $gzFile = $sourceFile . '.gz';
    if(function_exists('gzopen')){
        $in = @fopen($sourceFile, 'rb');
        $out = @gzopen($gzFile, 'wb9');
        if($in && $out){
            while(!feof($in)) gzwrite($out, fread($in, 1024 * 256));
            fclose($in); gzclose($out);
            if(is_file($gzFile) && filesize($gzFile) > 0){ @unlink($sourceFile); return $gzFile; }
        }
        if($in) @fclose($in);
        if($out) @gzclose($out);
    }
    if(function_exists('gzencode')){
        $raw = @file_get_contents($sourceFile);
        if($raw !== false){
            $gz = @gzencode($raw, 9);
            if($gz !== false && @file_put_contents($gzFile, $gz, LOCK_EX) !== false){ @unlink($sourceFile); return $gzFile; }
        }
    }
    return $sourceFile;
}}

if(!function_exists('bot_admin_tools_make_backup')){
function bot_admin_tools_make_backup($manual = true){
    bot_admin_tools_install_schema();
    $dir='cache/backups'; if(!is_dir($dir)) @mkdir($dir,0775,true);
    if(!function_exists('bot_sql_pdo') || !($pdo=bot_sql_pdo())) return ['ok'=>false,'error'=>'اتصال دیتابیس برقرار نیست.'];
    $sqlFile=$dir.'/telegram_db_backup_'.date('Ymd_His').'.sql';
    try{
        $tables=[];
        foreach($pdo->query('SHOW TABLES') as $row){ $tables[] = array_values($row)[0]; }
        $fh=fopen($sqlFile,'w');
        if(!$fh) return ['ok'=>false,'error'=>'امکان ساخت فایل بکاپ وجود ندارد.'];
        fwrite($fh, "-- Telegram bot SQL backup\n-- Created: ".date('c')."\n-- Format: SQL dump generated by bot_admin_tools_make_backup\n\nSET NAMES utf8mb4;\nSET FOREIGN_KEY_CHECKS=0;\n\n");
        foreach($tables as $table){
            $safeTable = str_replace('`','``',$table);
            $create=$pdo->query('SHOW CREATE TABLE `'.$safeTable.'`')->fetch(PDO::FETCH_ASSOC);
            $createSql = $create['Create Table'] ?? array_values($create)[1] ?? '';
            fwrite($fh, "\nDROP TABLE IF EXISTS `{$safeTable}`;\n{$createSql};\n\n");
            $st=$pdo->query('SELECT * FROM `'.$safeTable.'`');
            while($r=$st->fetch(PDO::FETCH_ASSOC)){
                $cols=array_map(fn($c)=>'`'.str_replace('`','``',$c).'`', array_keys($r));
                $vals=[]; foreach($r as $v){ $vals[] = $v===null?'NULL':$pdo->quote((string)$v); }
                fwrite($fh, 'INSERT INTO `'.$safeTable.'` ('.implode(',',$cols).') VALUES ('.implode(',',$vals).');' . "\n");
            }
        }
        fwrite($fh, "\nSET FOREIGN_KEY_CHECKS=1;\n"); fclose($fh);
        $file = bot_admin_tools_backup_write_gzip($sqlFile);
        $s=bot_admin_tools_settings(); $s['last_backup_ts']=time(); bot_admin_tools_save_settings($s);
        $keep=max(1,(int)($s['backup_keep']??7));
        $files=array_merge(glob($dir.'/telegram_db_backup_*.sql') ?: [], glob($dir.'/telegram_db_backup_*.sql.gz') ?: [], glob($dir.'/db_backup_*.sql') ?: [], glob($dir.'/db_backup_*.sql.gz') ?: []);
        usort($files, fn($a,$b)=>filemtime($b)<=>filemtime($a));
        foreach(array_slice($files,$keep) as $old) @unlink($old);
        return ['ok'=>true,'file'=>$file,'size'=>filesize($file),'format'=>substr($file,-3)==='.gz'?'sql.gz':'sql'];
    }catch(Throwable $e){ if(isset($fh) && is_resource($fh)) @fclose($fh); error_log('backup failed: '.$e->getMessage()); return ['ok'=>false,'error'=>$e->getMessage()]; }
}}

if(!function_exists('bot_admin_tools_sql_uncompress')){
function bot_admin_tools_sql_uncompress($file){
    $file = (string)$file;
    if(!is_file($file)) return [false, 'فایل پیدا نشد.'];
    $max = 80 * 1024 * 1024;
    if(substr($file, -3) === '.gz'){
        if(function_exists('gzdecode')){
            $raw = @file_get_contents($file);
            if($raw === false) return [false, 'خواندن فایل gzip انجام نشد.'];
            $sql = @gzdecode($raw);
            if($sql === false) return [false, 'فایل gzip معتبر نیست.'];
            if(strlen($sql) > $max) return [false, 'حجم SQL بعد از باز شدن بیش از حد مجاز است.'];
            return [$sql, null];
        }
        if(function_exists('gzopen')){
            $fh = @gzopen($file, 'rb');
            if(!$fh) return [false, 'باز کردن gzip انجام نشد.'];
            $sql = '';
            while(!gzeof($fh)){
                $sql .= gzread($fh, 1024 * 256);
                if(strlen($sql) > $max){ gzclose($fh); return [false, 'حجم SQL بعد از باز شدن بیش از حد مجاز است.']; }
            }
            gzclose($fh);
            return [$sql, null];
        }
        return [false, 'افزونه gzip روی هاست فعال نیست.'];
    }
    if(filesize($file) > $max) return [false, 'حجم فایل SQL بیش از حد مجاز است.'];
    $sql = @file_get_contents($file);
    return $sql === false ? [false, 'خواندن فایل SQL انجام نشد.'] : [$sql, null];
}}

if(!function_exists('bot_admin_tools_strip_sql_comments')){
function bot_admin_tools_strip_sql_comments($sql){
    $sql = preg_replace('/^\xEF\xBB\xBF/', '', (string)$sql);
    $lines = preg_split('/\R/u', $sql);
    $out = [];
    foreach($lines as $line){
        $trim = ltrim($line);
        if($trim === '' || strpos($trim, '--') === 0 || strpos($trim, '#') === 0) continue;
        $out[] = $line;
    }
    return implode("\n", $out);
}}

if(!function_exists('bot_admin_tools_sql_split_statements')){
function bot_admin_tools_sql_split_statements($sql){
    $sql = bot_admin_tools_strip_sql_comments($sql);
    $statements = [];
    $buf = '';
    $len = strlen($sql);
    $quote = null;
    $escape = false;
    $blockComment = false;
    for($i=0; $i<$len; $i++){
        $ch = $sql[$i];
        $next = $i + 1 < $len ? $sql[$i+1] : '';
        if($blockComment){
            if($ch === '*' && $next === '/'){ $blockComment=false; $i++; }
            continue;
        }
        if($quote === null && $ch === '/' && $next === '*'){ $blockComment=true; $i++; continue; }
        if($quote !== null){
            $buf .= $ch;
            if($escape){ $escape=false; continue; }
            if($ch === '\\'){ $escape=true; continue; }
            if($ch === $quote){ $quote=null; }
            continue;
        }
        if($ch === "'" || $ch === '"' || $ch === '`'){
            $quote = $ch; $buf .= $ch; continue;
        }
        if($ch === ';'){
            $stmt = trim($buf);
            if($stmt !== '') $statements[] = $stmt;
            $buf = '';
            continue;
        }
        $buf .= $ch;
    }
    $tail = trim($buf);
    if($tail !== '') $statements[] = $tail;
    return $statements;
}}

if(!function_exists('bot_admin_tools_restore_database_file')){
function bot_admin_tools_restore_database_file($file, $actor = 'admin'){
    if(!function_exists('bot_sql_pdo') || !($pdo=bot_sql_pdo())) return ['ok'=>false,'error'=>'اتصال دیتابیس برقرار نیست.'];
    $file = (string)$file;
    $base = realpath('cache/backups');
    $real = realpath($file);
    if(!$real || !$base || strpos($real, $base) !== 0 || !is_file($real)) return ['ok'=>false,'error'=>'مسیر فایل بکاپ معتبر نیست.'];
    if(preg_match('/\.php$/i', $real)) return ['ok'=>false,'error'=>'فایل PHP برای ریستور مجاز نیست. فایل باید json، sql یا sql.gz باشد.'];
    [$sql, $err] = bot_admin_tools_sql_uncompress($real);
    if($sql === false) return ['ok'=>false,'error'=>$err ?: 'فایل SQL خوانده نشد.'];
    if(stripos($sql, '<?php') !== false || stripos($sql, '<?=') !== false) return ['ok'=>false,'error'=>'محتوای PHP داخل فایل دیده شد؛ برای امنیت رد شد.'];
    if(stripos($sql, 'CREATE TABLE') === false && stripos($sql, 'INSERT INTO') === false && stripos($sql, 'DROP TABLE') === false) return ['ok'=>false,'error'=>'این فایل شبیه بکاپ دیتابیس SQL نیست.'];
    $pre = bot_admin_tools_make_backup(false);
    $statements = bot_admin_tools_sql_split_statements($sql);
    if(!$statements) return ['ok'=>false,'error'=>'هیچ دستور SQL قابل اجرا پیدا نشد.'];
    $done = 0;
    try{
        $pdo->exec('SET FOREIGN_KEY_CHECKS=0');
        foreach($statements as $stmt){
            $stmt = trim($stmt);
            if($stmt === '') continue;
            $pdo->exec($stmt);
            $done++;
        }
        $pdo->exec('SET FOREIGN_KEY_CHECKS=1');
        if(function_exists('bot_cache_clear_all')) @bot_cache_clear_all();
        if(function_exists('bot_admin_log')) @bot_admin_log((string)$actor, 'database_restore', 'backup', basename($real), 'بازگردانی دیتابیس از داخل ربات', ['statements'=>$done,'pre_backup'=>$pre['file'] ?? null]);
        return ['ok'=>true,'statements'=>$done,'pre_backup'=>$pre['file'] ?? null];
    }catch(Throwable $e){
        try{ $pdo->exec('SET FOREIGN_KEY_CHECKS=1'); }catch(Throwable $ignore){}
        error_log('database restore failed: '.$e->getMessage());
        return ['ok'=>false,'error'=>$e->getMessage(),'statements'=>$done,'pre_backup'=>$pre['file'] ?? null];
    }
}}

if(!function_exists('bot_admin_tools_export_csv')){
function bot_admin_tools_export_csv($type){
    bot_admin_tools_install_schema();
    $dir='cache/exports'; if(!is_dir($dir)) @mkdir($dir,0775,true);
    $allowed=[
        'users'=>['table'=>'bot_users','cols'=>'user_id, username, first_name, points, step, created_at, updated_at'],
        'orders'=>['table'=>'bot_orders','cols'=>'order_id, owner_id, channel_username, target_count, received_count, status, created_at, updated_at'],
        'ledger'=>['table'=>'bot_wallet_ledger','cols'=>'id, user_id, asset_type, delta_amount, balance_before, balance_after, reason, actor_id, created_at'],
        'admin_logs'=>['table'=>'bot_admin_logs','cols'=>'id, actor_id, action_type, target_type, target_id, description, created_at'],
        'gift_codes'=>['table'=>'bot_gift_codes','cols'=>'id, code, amount, status, created_by, used_by, created_at, used_at'],
        'reports'=>['table'=>'bot_order_reports','cols'=>'id, order_id, reporter_id, status, created_at'],
    ];
    if(!isset($allowed[$type])) return ['ok'=>false,'error'=>'unknown_export_type'];
    if(!function_exists('bot_sql_pdo') || !($pdo=bot_sql_pdo())) return ['ok'=>false,'error'=>'اتصال دیتابیس برقرار نیست.'];
    $file=$dir.'/'.$type.'_'.date('Ymd_His').'.csv';
    try{
        $q=$allowed[$type]; $st=$pdo->query('SELECT '.$q['cols'].' FROM '.$q['table'].' ORDER BY 1 DESC LIMIT 5000');
        $fh=fopen($file,'w'); fprintf($fh, chr(0xEF).chr(0xBB).chr(0xBF));
        $first=true; $count=0;
        while($row=$st->fetch(PDO::FETCH_ASSOC)){
            if($first){ fputcsv($fh, array_keys($row)); $first=false; }
            fputcsv($fh, $row); $count++;
        }
        fclose($fh); return ['ok'=>true,'file'=>$file,'count'=>$count];
    }catch(Throwable $e){ return ['ok'=>false,'error'=>$e->getMessage()]; }
}}

if(!function_exists('bot_admin_tools_mime_for_file')){
function bot_admin_tools_mime_for_file($path){
    $path = (string)$path;
    if(preg_match('/\.json$/i', $path)) return 'application/json';
    if(preg_match('/\.sql$/i', $path)) return 'application/sql';
    if(preg_match('/\.sql\.gz$/i', $path)) return 'application/gzip';
    if(preg_match('/\.csv$/i', $path)) return 'text/csv';
    if(function_exists('mime_content_type')){ $m=@mime_content_type($path); if($m) return $m; }
    return 'application/octet-stream';
}}

if(!function_exists('bot_admin_tools_send_file')){
function bot_admin_tools_send_file($chatId, $path, $caption=''){
    if(!is_file($path)) return false;
    if(function_exists('curl_init') && class_exists('CURLFile', false)){
        $mime = bot_admin_tools_mime_for_file($path);
        $name = basename($path);
        return bot('sendDocument', ['chat_id'=>$chatId, 'document'=>new CURLFile(realpath($path), $mime, $name), 'caption'=>$caption]);
    }
    $link = function_exists('bot_admin_tools_backup_link') ? bot_admin_tools_backup_link($path) : '';
    if($link !== ''){
        SM($chatId, "فایل آماده شد، اما ارسال مستقیم فایل روی هاست فعال نیست. از لینک زیر دانلود کن:\n{$link}", 'MarkDown');
        return true;
    }
    SM($chatId, "فایل ساخته شد ولی ارسال فایل در هاست فعال نیست:\n{$path}", 'MarkDown');
    return false;
}}

if(!function_exists('bot_admin_tools_download_telegram_file')){
function bot_admin_tools_download_telegram_file($fileId, $targetPath){
    $fileId = (string)$fileId;
    $targetPath = (string)$targetPath;
    if($fileId === '') return ['ok'=>false,'error'=>'file_id خالی است.'];
    $info = bot('getFile', ['file_id'=>$fileId]);
    if(empty($info->ok) || empty($info->result->file_path)) return ['ok'=>false,'error'=>'دریافت مسیر فایل از تلگرام انجام نشد.'];
    $url = (defined('TELEGRAM_FILE_BASE') ? TELEGRAM_FILE_BASE : 'https://api.telegram.org/file/bot') . API_KEY . '/' . $info->result->file_path;
    $dir = dirname($targetPath); if($dir && $dir !== '.' && !is_dir($dir)) @mkdir($dir, 0775, true);
    if(function_exists('curl_init')){
        $fh = @fopen($targetPath, 'wb');
        if(!$fh) return ['ok'=>false,'error'=>'امکان نوشتن فایل روی هاست وجود ندارد.'];
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_FILE, $fh);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
        curl_setopt($ch, CURLOPT_TIMEOUT, 120);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
        $ok = curl_exec($ch);
        $err = curl_error($ch);
        $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch); fclose($fh);
        if(!$ok || $code < 200 || $code >= 300){ @unlink($targetPath); return ['ok'=>false,'error'=>'دانلود فایل از تلگرام ناموفق بود: '.($err ?: 'HTTP '.$code)]; }
    }else{
        $raw = @file_get_contents($url);
        if($raw === false) return ['ok'=>false,'error'=>'دانلود فایل از تلگرام انجام نشد.'];
        if(@file_put_contents($targetPath, $raw, LOCK_EX) === false) return ['ok'=>false,'error'=>'ذخیره فایل روی هاست انجام نشد.'];
    }
    return ['ok'=>true,'file'=>$targetPath,'size'=>is_file($targetPath)?filesize($targetPath):0];
}}

if(!function_exists('bot_admin_tools_pending_db_restore_map')){
function bot_admin_tools_pending_db_restore_map($newMap = null){
    $file = 'cache/pending_db_restores.json';
    if($newMap !== null){ @file_put_contents($file, json_encode($newMap, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT), LOCK_EX); return $newMap; }
    $map = is_file($file) ? json_decode((string)@file_get_contents($file), true) : [];
    if(!is_array($map)) $map=[];
    $now = time();
    foreach($map as $tok=>$row){ if(!is_array($row) || (int)($row['expires'] ?? 0) < $now){ if(!empty($row['file'])) @unlink($row['file']); unset($map[$tok]); } }
    @file_put_contents($file, json_encode($map, JSON_UNESCAPED_UNICODE|JSON_PRETTY_PRINT), LOCK_EX);
    return $map;
}}

if(!function_exists('bot_admin_tools_stage_db_restore')){
function bot_admin_tools_stage_db_restore($file, $actor){
    $map = bot_admin_tools_pending_db_restore_map();
    $token = substr(bin2hex(random_bytes(12)), 0, 24);
    $map[$token] = ['file'=>(string)$file, 'actor'=>(string)$actor, 'created'=>time(), 'expires'=>time()+1800];
    bot_admin_tools_pending_db_restore_map($map);
    return $token;
}}



if(!function_exists('bot_admin_tools_user_text')){
function bot_admin_tools_user_text($uid){
    $u = function_exists('bot_data_get_json') ? bot_data_get_json('melat/'.$uid.'.json', []) : [];
    if(!is_array($u) || !$u) return "❌ کاربر **{$uid}** پیدا نشد.";
    $name = $u['first_name'] ?? $u['first'] ?? '';
    $points = (int)($u['Points'] ?? 0);
    $panel = $u['type-panel'] ?? '-';
    $step = $u['step'] ?? '-';
    $orders = is_array($u['ads-id'] ?? null) ? count($u['ads-id']) : 0;
    return "👤 **مدیریت کاربر**\n\n🆔 آیدی: **{$uid}**\n👤 نام: **{$name}**\n💎 موجودی: **{$points}**\n🎫 پنل: **{$panel}**\n📍 مرحله: **{$step}**\n📦 تعداد سفارش‌ها: **{$orders}**";
}}

if(!function_exists('bot_admin_tools_user_keyboard')){
function bot_admin_tools_user_keyboard($uid){
    return json_encode(['inline_keyboard'=>[
        [['text'=>'➕ افزایش موجودی','callback_data'=>'admtools_user_add:'.$uid], ['text'=>'➖ کاهش موجودی','callback_data'=>'admtools_user_deduct:'.$uid]],
        [['text'=>'📒 دفتر حساب','callback_data'=>'admtools_user_ledger:'.$uid], ['text'=>'📦 سفارش‌ها','callback_data'=>'admtools_user_orders:'.$uid]],
        [['text'=>'⛔️ بلاک','callback_data'=>'admtools_user_ban:'.$uid], ['text'=>'✅ آنبلاک','callback_data'=>'admtools_user_unban:'.$uid]],
    ]], JSON_UNESCAPED_UNICODE);
}}

if(!function_exists('bot_admin_tools_ledger_text')){
function bot_admin_tools_ledger_text($uid, $limit=10){
    bot_admin_tools_install_schema();
    $rows=[];
    if(function_exists('bot_sql_pdo') && ($pdo=bot_sql_pdo())){
        try{
            $st=$pdo->prepare('SELECT delta_amount,balance_before,balance_after,reason,actor_id,created_at FROM bot_wallet_ledger WHERE user_id=? ORDER BY id DESC LIMIT '.(int)$limit);
            $st->execute([(string)$uid]); $rows=$st->fetchAll(PDO::FETCH_ASSOC);
        }catch(Throwable $e){}
    }
    if(!$rows) return "📒 **دفتر حساب کاربر {$uid}**\n\nگردشی ثبت نشده است.";
    $out="📒 **دفتر حساب کاربر {$uid}**\n\n";
    foreach($rows as $r){
        $sign=((int)$r['delta_amount']>=0?'+':'');
        $out .= "• {$r['created_at']} | **{$sign}{$r['delta_amount']}** | {$r['balance_before']} → {$r['balance_after']} | {$r['reason']}";
        if(!empty($r['actor_id'])) $out .= " | ادمین: {$r['actor_id']}";
        $out .= "\n";
    }
    return $out;
}}

if(!function_exists('bot_admin_tools_orders_text')){
function bot_admin_tools_orders_text($uid, $limit=8){
    $out="📦 **سفارش‌های اخیر کاربر {$uid}**\n\n";
    $found=0;
    if(function_exists('bot_sql_pdo') && ($pdo=bot_sql_pdo())){
        try{
            $st=$pdo->prepare('SELECT order_id,status,target_count,received_count,created_at FROM bot_orders WHERE owner_id=? ORDER BY created_ts DESC, created_at DESC LIMIT '.(int)$limit);
            $st->execute([(string)$uid]);
            foreach($st as $r){ $found++; $out.="• {$r['order_id']} | {$r['status']} | {$r['received_count']}/{$r['target_count']} | {$r['created_at']}\n"; }
        }catch(Throwable $e){}
    }
    if(!$found) $out.='سفارشی پیدا نشد.';
    return $out;
}}

if(!function_exists('bot_admin_tools_cancel_keyboard')){
function bot_admin_tools_cancel_keyboard(){
    return json_encode(['keyboard'=>[
        [['text'=>'❌ لغو عملیات'], ['text'=>'🔙بازگشت به منوی ادمین']],
    ], 'resize_keyboard'=>true], JSON_UNESCAPED_UNICODE);
}}

if(!function_exists('bot_admin_tools_is_cancel_text')){
function bot_admin_tools_is_cancel_text($msg){
    $m = trim((string)$msg);
    return in_array($m, ['❌ لغو','❌ لغو عملیات','لغو','انصراف','کنسل','cancel','Cancel'], true);
}}

if(!function_exists('bot_admin_tools_is_panel_nav_text')){
function bot_admin_tools_is_panel_nav_text($msg){
    $m = trim((string)$msg);
    if($m === '') return false;
    if(function_exists('bot_is_menu_back_text') && bot_is_menu_back_text($m)) return true;
    if(function_exists('bot_is_admin_menu_back_text') && bot_is_admin_menu_back_text($m)) return true;
    $known = [
        '📈 آمار ربات','👤 جستجوی کاربر','📦 سفارش‌ها','📨 ارسال پیام','🎫 تیکت‌ها','🪙 سکه همگانی',
        '📌ثبت سفارش','📌 ثبت سفارش','🛍 مدیریت فروشگاه','📊 وضعیت ارسال همگانی','▶️ اجرای فوری صف همگانی',
        '💾 بکاپ دیتابیس','🧾 بکاپ تنظیمات','📥 ورود بکاپ','⚙️ تنظیمات وب','🌐 پنل وب',
        '🧰 ابزارهای سریع','👥 مشاهده پنل کاربران عادی','👥 آمار رفرال','📊 آمار رفرال',
        '🚀 امکانات پیشرفته','📊 داشبورد پیشرفته','📦 مدیریت سفارش‌ها','👥 سطح‌بندی کاربران','📣 اطلاعیه‌ها',
        '📜 قوانین و راهنما','🧪 تست نصب','🔄 آپدیت و بازگشت','🧹 پاکسازی دیتابیس',
        'پیام همگانی','فوروارد همگانی','👥 رفرال‌گیری','⚙️ زیرمجموعه گیری','⚙️ زیرمجموعه‌گیری',
    ];
    return in_array($m, $known, true);
}}

if(!function_exists('bot_admin_tools_clear_step')){
function bot_admin_tools_clear_step(&$user, $userID){
    if(is_array($user)){
        $user['step']='none';
        unset($user['pending_mass_coin_amount']);
        if(function_exists('saveJson')) saveJson('melat/'.$userID.'.json',$user);
        elseif(function_exists('bot_data_put_json')) bot_data_put_json('melat/'.$userID.'.json',$user);
    }
}}

if(!function_exists('bot_admin_tools_runtime_router')){
function bot_admin_tools_runtime_router(){
    global $msg,$data,$Tc,$isAdmin,$chatID,$userID,$msg_id,$user,$Button_Panel,$data_id;
    if(empty($isAdmin) || (isset($Tc) && (string)$Tc !== 'private')) return false;
    bot_admin_tools_install_schema();
    $currentStep = is_array($user) ? (string)($user['step'] ?? '') : '';
    if($currentStep === 'admtools_mass_coin_amount'){
        if(bot_admin_tools_is_cancel_text($msg) || (function_exists('bot_is_menu_back_text') && bot_is_menu_back_text($msg)) || (function_exists('bot_is_admin_menu_back_text') && bot_is_admin_menu_back_text($msg))){
            bot_admin_tools_clear_step($user, $userID);
            SM($chatID,'✅ عملیات سکه همگانی لغو شد.','html',$msg_id,$Button_Panel);
            return true;
        }
        // If admin taps another panel button while a numeric step is open, clear the old step and let the normal menu router handle that button.
        if(bot_admin_tools_is_panel_nav_text($msg) && $msg !== '🪙 سکه همگانی'){
            bot_admin_tools_clear_step($user, $userID);
            return false;
        }
    }
    if($msg === '🪙 سکه همگانی'){
        if(!bot_admin_tools_is_owner($userID)) return bot_admin_tools_sensitive_denied('سکه همگانی');
        $user['step']='admtools_mass_coin_amount'; saveJson('melat/'.$userID.'.json',$user);
        SM($chatID,"🪙 <b>سکه/الماس همگانی</b>\n\nمقداری که می‌خواهی به همه کاربران اضافه شود را عددی ارسال کن.\nمثال: <code>5</code>\n\nاین عملیات از طریق صف کرون انجام می‌شود و یک‌جا به هاست فشار نمی‌آورد.",'html',$msg_id,(function_exists('bot_state_cancel_keyboard')?bot_state_cancel_keyboard(true):bot_admin_tools_cancel_keyboard()));
        return true;
    }
    if(($user['step'] ?? '') === 'admtools_mass_coin_amount' && !bot_is_menu_back_text($msg) && !bot_is_admin_menu_back_text($msg)){
        if(!bot_admin_tools_is_owner($userID)) return bot_admin_tools_sensitive_denied('سکه همگانی');
        $amount=(int)preg_replace('/[^0-9]/','',(string)$msg);
        if($amount <= 0 || $amount > 100000000){
            SM($chatID,'❌ مقدار معتبر نیست. فقط عدد مثبت ارسال کن یا برای خروج دکمه <b>❌ لغو عملیات</b> را بزن.','html',$msg_id,bot_admin_tools_cancel_keyboard()); return true;
        }
        $user['step']='none'; $user['pending_mass_coin_amount']=$amount; saveJson('melat/'.$userID.'.json',$user);
        $ids = function_exists('bot_broadcast_user_ids') ? bot_broadcast_user_ids(true) : [];
        $count = is_array($ids) ? count($ids) : 0;
        $kb=json_encode(['inline_keyboard'=>[
            [['text'=>'✅ شروع شارژ همگانی','callback_data'=>'admtools_mass_coin_confirm:'.$amount]],
            [['text'=>'❌ لغو','callback_data'=>'admtools_mass_coin_cancel']],
        ]],JSON_UNESCAPED_UNICODE);
        SM($chatID,"⚠️ تأیید نهایی\n\nمقدار: <b>{$amount}</b>\nتعداد کاربران صف: <b>{$count}</b>\n\nبا تأیید، این شارژ به صف کرون اضافه می‌شود و هر دقیقه مرحله‌ای اجرا می‌شود.",'html',$msg_id,$kb);
        return true;
    }
    if(preg_match('/^admtools_mass_coin_confirm:(\d+)$/',(string)$data,$m)){
        if(!bot_admin_tools_is_owner($userID)) return bot_admin_tools_sensitive_denied('سکه همگانی');
        $amount=(int)$m[1];
        $job = function_exists('bot_broadcast_enqueue_coin') ? bot_broadcast_enqueue_coin($userID,$amount,'') : ['ok'=>false,'error'=>'queue_missing'];
        bot('answercallbackquery',['callback_query_id'=>$data_id,'text'=>empty($job['error'])?'به صف اضافه شد':'خطا','show_alert'=>false]);
        if(empty($job['error'])){
            bot_admin_log($userID,'mass_coin_enqueue','broadcast',$job['id'] ?? null,'افزودن سکه همگانی',['amount'=>$amount,'job'=>$job]);
            bot('editMessageText',['chat_id'=>$chatID,'message_id'=>$msg_id,'text'=>"✅ سکه همگانی به صف اضافه شد.\n\n🆔 صف: <code>".htmlspecialchars((string)$job['id'],ENT_QUOTES,'UTF-8')."</code>\nمقدار: <b>{$amount}</b>\nکاربران: <b>".(int)($job['total'] ?? 0)."</b>\n\nکرون جاب <code>task=broadcast</code> این صف را مرحله‌ای اجرا می‌کند.",'parse_mode'=>'HTML','reply_markup'=>function_exists('bot_broadcast_status_keyboard')?bot_broadcast_status_keyboard():null]);
        }else{
            bot('editMessageText',['chat_id'=>$chatID,'message_id'=>$msg_id,'text'=>'❌ خطا در ساخت صف سکه همگانی: '.htmlspecialchars((string)($job['error'] ?? 'unknown'),ENT_QUOTES,'UTF-8'),'parse_mode'=>'HTML']);
        }
        return true;
    }
    if((string)$data === 'admtools_mass_coin_cancel'){
        if(is_array($user)){ $user['step']='none'; unset($user['pending_mass_coin_amount']); saveJson('melat/'.$userID.'.json',$user); }
        bot('answercallbackquery',['callback_query_id'=>$data_id,'text'=>'لغو شد','show_alert'=>false]);
        bot('editMessageText',['chat_id'=>$chatID,'message_id'=>$msg_id,'text'=>'❌ سکه همگانی لغو شد.','parse_mode'=>'HTML']);
        return true;
    }
    if($msg === '🧰 ابزارهای مدیریتی'){
        if(is_array($user)){ $user['step']='none'; if(function_exists('saveJson')) saveJson('melat/'.$userID.'.json',$user); }
        bot_admin_log($userID,'open_admin_tools','panel',null,'باز کردن ابزارهای مدیریتی');
        SM($chatID, bot_admin_tools_panel_text(), 'MarkDown', $msg_id, bot_admin_tools_keyboard()); return true;
    }
    if($msg === '🧪 تست سلامت'){
        bot_admin_log($userID,'health_check'); SM($chatID, bot_admin_tools_health_text(), 'MarkDown', $msg_id, bot_admin_tools_keyboard()); return true;
    }
    if($msg === '📋 خطاهای اخیر'){
        bot_admin_log($userID,'view_errors'); SM($chatID, bot_admin_tools_errors_text(), 'MarkDown', $msg_id, json_encode(['inline_keyboard'=>[[['text'=>'🧹 پاکسازی خطاها','callback_data'=>'admtools_clear_errors']]]], JSON_UNESCAPED_UNICODE)); return true;
    }
    if($data === 'admtools_clear_errors'){
        foreach(['debug.log','cache/modular_fatal.log'] as $f) if(is_file($f)) @file_put_contents($f,'',LOCK_EX);
        bot_admin_log($userID,'clear_errors'); bot('answercallbackquery',['callback_query_id'=>$data_id,'text'=>'پاک شد','show_alert'=>false]);
        bot('editmessagetext',['chat_id'=>$chatID,'message_id'=>$msg_id,'text'=>'✅ خطاهای اخیر پاکسازی شد.','parse_mode'=>'MarkDown']); return true;
    }
    if($msg === '🔒 بکاپ دیتابیس'){
        return bot_admin_tools_sensitive_denied('بکاپ دیتابیس');
    }
    if($msg === '💾 بکاپ دیتابیس'){
        if(!bot_admin_tools_is_owner($userID)) return bot_admin_tools_sensitive_denied('بکاپ دیتابیس');
        $r=bot_admin_tools_make_backup(true); bot_admin_log($userID,'manual_backup',null,null, !empty($r['ok'])?'backup_ok':'backup_failed', $r);
        if(!empty($r['ok'])){
            bot_admin_tools_send_file($chatID, $r['file'], '💾 بکاپ دیتابیس ربات | فرمت: '.($r['format'] ?? 'sql'));
            SM($chatID,'✅ بکاپ دیتابیس ساخته و به صورت فایل واقعی ارسال شد.

نام فایل: **'.basename($r['file'])."**\nحجم: **".number_format((int)$r['size'])." بایت**",'MarkDown',null,bot_admin_tools_keyboard());
        }
        else SM($chatID,'❌ خطا در بکاپ: '.($r['error']??'نامشخص'),'MarkDown',null,bot_admin_tools_keyboard()); return true;
    }
    if($msg === '🔒 ورود بکاپ'){
        return bot_admin_tools_sensitive_denied('ورود بکاپ');
    }
    if($msg === '📥 ورود بکاپ'){
        if(!bot_admin_tools_is_owner($userID)) return bot_admin_tools_sensitive_denied('ورود بکاپ');
        if(is_array($user)){ $user['step']='admin_restore_backup_wait_file'; if(function_exists('saveJson')) saveJson('melat/'.$userID.'.json',$user); }
        SM($chatID,"📥 **ورود بکاپ**\n\nفایل بکاپ را همینجا ارسال کن:\n• بکاپ تنظیمات: `.json`\n• بکاپ دیتابیس: `.sql` یا `.sql.gz`\n\nفایل PHP، ZIP ناشناس و فایل‌های کانفیگ برای امنیت رد می‌شوند.",'MarkDown',$msg_id,bot_admin_tools_keyboard());
        return true;
    }
    if($msg === '🧯 حالت تعمیرات'){
        $s=bot_admin_tools_settings();
        $kb=json_encode(['inline_keyboard'=>[
            [['text'=>'✅ فعال کردن','callback_data'=>'admtools_maint:on'], ['text'=>'❌ غیرفعال کردن','callback_data'=>'admtools_maint:off']],
            [['text'=>'✏️ تنظیم متن تعمیرات','callback_data'=>'admtools_maint_text']],
        ]], JSON_UNESCAPED_UNICODE);
        SM($chatID,"🧯 **حالت تعمیرات**\n\nوضعیت فعلی: **".($s['maintenance_enabled']??'❌غیر فعال')."**\n\nوقتی فعال باشد کاربران عادی متوقف می‌شوند اما ادمین‌ها دسترسی دارند.",'MarkDown',$msg_id,$kb); return true;
    }
    if(preg_match('/^admtools_maint:(on|off)$/',(string)$data,$m)){
        $s=bot_admin_tools_settings(); $s['maintenance_enabled']=$m[1]==='on'?'✅فعال':'❌غیر فعال'; bot_admin_tools_save_settings($s);
        bot_admin_log($userID,'maintenance_'.$m[1]); bot('answercallbackquery',['callback_query_id'=>$data_id,'text'=>'ثبت شد','show_alert'=>false]);
        bot('editmessagetext',['chat_id'=>$chatID,'message_id'=>$msg_id,'text'=>'✅ حالت تعمیرات: **'.$s['maintenance_enabled'].'**','parse_mode'=>'MarkDown']); return true;
    }
    if($data === 'admtools_maint_text'){
        $user['step']='admtools_maint_text'; saveJson('melat/'.$userID.'.json',$user); bot('answercallbackquery',['callback_query_id'=>$data_id,'text'=>'متن را ارسال کن','show_alert'=>false]); SM($chatID,'متن حالت تعمیرات را ارسال کن:','MarkDown',null,bot_admin_tools_keyboard()); return true;
    }
    if(($user['step']??'') === 'admtools_maint_text' && !bot_is_menu_back_text($msg)){
        $s=bot_admin_tools_settings(); $s['maintenance_text']=(string)$msg; bot_admin_tools_save_settings($s); $user['step']='none'; saveJson('melat/'.$userID.'.json',$user); bot_admin_log($userID,'maintenance_text_update'); SM($chatID,'✅ متن تعمیرات ذخیره شد.','MarkDown',$msg_id,bot_admin_tools_keyboard()); return true;
    }
    if($msg === '👤 مدیریت کاربران'){
        $user['step']='admtools_user_lookup'; saveJson('melat/'.$userID.'.json',$user); SM($chatID,'🆔 آیدی عددی کاربر را ارسال کن:','MarkDown',$msg_id,bot_admin_tools_keyboard()); return true;
    }
    if(($user['step']??'') === 'admtools_user_lookup' && !bot_is_menu_back_text($msg)){
        $uid=preg_replace('/[^0-9]/','',(string)$msg); $user['step']='none'; saveJson('melat/'.$userID.'.json',$user);
        SM($chatID, bot_admin_tools_user_text($uid), 'MarkDown', $msg_id, bot_admin_tools_user_keyboard($uid)); return true;
    }
    if(preg_match('/^admtools_user_(add|deduct):(\d+)$/',(string)$data,$m)){
        $user['step']='admtools_user_'.$m[1].':'.$m[2]; saveJson('melat/'.$userID.'.json',$user); bot('answercallbackquery',['callback_query_id'=>$data_id,'text'=>'مبلغ را ارسال کن','show_alert'=>false]); SM($chatID,'مقدار الماس را عددی ارسال کن:','MarkDown'); return true;
    }
    if(preg_match('/^admtools_user_(add|deduct):(\d+)$/',(string)($user['step']??''),$m) && !bot_is_menu_back_text($msg)){
        $amount=abs((int)preg_replace('/[^0-9]/','',(string)$msg)); if($m[1]==='deduct') $amount=-$amount;
        $res=function_exists('bot_wallet_add')?bot_wallet_add($m[2],$amount,'admin_tools_adjust',['admin_id'=>$userID,'actor_id'=>$userID]):['ok'=>false,'error'=>'wallet_missing'];
        $user['step']='none'; saveJson('melat/'.$userID.'.json',$user); bot_admin_log($userID,'user_balance_adjust','user',$m[2],'تغییر موجودی از ابزار مدیریتی',['delta'=>$amount,'result'=>$res]);
        SM($chatID, !empty($res['ok'])?'✅ انجام شد. موجودی جدید: **'.($res['after']??'-').'**':'❌ خطا در تغییر موجودی', 'MarkDown', $msg_id, bot_admin_tools_user_keyboard($m[2])); return true;
    }
    if(preg_match('/^admtools_user_(ban|unban):(\d+)$/',(string)$data,$m)){
        $list=bot_data_get_json('lib/kodam/list.json',[]); if(!is_array($list))$list=[]; if(!isset($list['ban'])||!is_array($list['ban']))$list['ban']=[];
        if($m[1]==='ban' && !in_array($m[2],array_map('strval',$list['ban']),true)) $list['ban'][]=$m[2];
        if($m[1]==='unban') $list['ban']=array_values(array_filter($list['ban'],fn($x)=>(string)$x!==$m[2]));
        saveJson('lib/kodam/list.json',$list);
        if(function_exists('bot_adv_user_level_set')) bot_adv_user_level_set($m[2], $m[1]==='ban' ? 'blocked' : 'normal', $userID);
        bot_admin_log($userID,'user_'.$m[1],'user',$m[2]);
        bot('answercallbackquery',['callback_query_id'=>$data_id,'text'=>'ثبت شد','show_alert'=>false]); bot('editmessagetext',['chat_id'=>$chatID,'message_id'=>$msg_id,'text'=>'✅ وضعیت کاربر بروزرسانی شد.','parse_mode'=>'MarkDown']); return true;
    }
    if(preg_match('/^admtools_user_ledger:(\d+)$/',(string)$data,$m)){
        bot('answercallbackquery',['callback_query_id'=>$data_id,'text'=>'دفتر حساب','show_alert'=>false]); bot('editmessagetext',['chat_id'=>$chatID,'message_id'=>$msg_id,'text'=>bot_admin_tools_ledger_text($m[1]),'parse_mode'=>'MarkDown','reply_markup'=>bot_admin_tools_user_keyboard($m[1])]); return true;
    }
    if(preg_match('/^admtools_user_orders:(\d+)$/',(string)$data,$m)){
        bot('answercallbackquery',['callback_query_id'=>$data_id,'text'=>'سفارش‌ها','show_alert'=>false]); bot('editmessagetext',['chat_id'=>$chatID,'message_id'=>$msg_id,'text'=>bot_admin_tools_orders_text($m[1]),'parse_mode'=>'MarkDown','reply_markup'=>bot_admin_tools_user_keyboard($m[1])]); return true;
    }
    if($msg === '📒 دفتر حساب'){
        $user['step']='admtools_ledger_lookup'; saveJson('melat/'.$userID.'.json',$user); SM($chatID,'آیدی عددی کاربر را برای نمایش دفتر حساب ارسال کن:','MarkDown',$msg_id,bot_admin_tools_keyboard()); return true;
    }
    if(($user['step']??'') === 'admtools_ledger_lookup' && !bot_is_menu_back_text($msg)){
        $uid=preg_replace('/[^0-9]/','',(string)$msg); $user['step']='none'; saveJson('melat/'.$userID.'.json',$user); SM($chatID,bot_admin_tools_ledger_text($uid,15),'MarkDown',$msg_id,bot_admin_tools_keyboard()); return true;
    }
    if($msg === '🧾 لاگ مدیران'){
        $text="🧾 **آخرین لاگ مدیران**\n\n"; $found=0;
        if(function_exists('bot_sql_pdo') && ($pdo=bot_sql_pdo())){ try{ foreach($pdo->query('SELECT actor_id,action_type,target_type,target_id,description,created_at FROM bot_admin_logs ORDER BY id DESC LIMIT 15') as $r){ $found++; $text.="• {$r['created_at']} | {$r['actor_id']} | {$r['action_type']} | {$r['target_type']}:{$r['target_id']}\n"; } }catch(Throwable $e){} }
        if(!$found) $text.='لاگی ثبت نشده است.'; SM($chatID,$text,'MarkDown',$msg_id,bot_admin_tools_keyboard()); return true;
    }
    if($msg === '🔒 خروجی CSV'){
        return bot_admin_tools_sensitive_denied('خروجی CSV');
    }
    if($msg === '📤 خروجی CSV'){
        if(!bot_admin_tools_is_owner($userID)) return bot_admin_tools_sensitive_denied('خروجی CSV');
        $kb=json_encode(['inline_keyboard'=>[
            [['text'=>'کاربران','callback_data'=>'admtools_export:users'], ['text'=>'سفارش‌ها','callback_data'=>'admtools_export:orders']],
            [['text'=>'دفتر حساب','callback_data'=>'admtools_export:ledger'], ['text'=>'کدهای هدیه','callback_data'=>'admtools_export:gift_codes']],
            [['text'=>'لاگ مدیران','callback_data'=>'admtools_export:admin_logs'], ['text'=>'گزارش تخلف','callback_data'=>'admtools_export:reports']],
        ]], JSON_UNESCAPED_UNICODE);
        SM($chatID,'📤 کدام خروجی CSV را می‌خواهی؟','MarkDown',$msg_id,$kb); return true;
    }
    if(preg_match('/^admtools_export:([a-z_]+)$/',(string)$data,$m)){
        if(!bot_admin_tools_is_owner($userID)) return bot_admin_tools_sensitive_denied('خروجی CSV');
        bot('answercallbackquery',['callback_query_id'=>$data_id,'text'=>'در حال ساخت خروجی','show_alert'=>false]);
        $r=bot_admin_tools_export_csv($m[1]); bot_admin_log($userID,'export_csv',$m[1],null,'خروجی CSV',$r);
        if(!empty($r['ok'])) bot_admin_tools_send_file($chatID,$r['file'],'📤 خروجی CSV '.$m[1]);
        else SM($chatID,'❌ خطا در خروجی: '.($r['error']??'نامشخص'),'MarkDown'); return true;
    }
    return false;
}}

if(!function_exists('bot_admin_tools_cron_backup')){
function bot_admin_tools_cron_backup(){
    $s=bot_admin_tools_settings();
    if((string)($s['backup_enabled']??'✅فعال') !== '✅فعال') return ['ok'=>true,'skipped'=>true,'reason'=>'backup_disabled'];
    $last=(int)($s['last_backup_ts']??0);
    if($last>0 && time()-$last < 86400) return ['ok'=>true,'skipped'=>true,'reason'=>'not_due'];
    return bot_admin_tools_make_backup(false);
}}

?>
