<?php
/**
 * GEM ACC runtime cleanup utilities.
 * پاکسازی فایل‌های اضافه runtime بدون حذف سفارش، کاربر، پلن یا دیتابیس.
 */

declare(strict_types=1);

if (!function_exists('gemacc_format_bytes')) {
    function gemacc_format_bytes(int $bytes): string {
        if ($bytes >= 1073741824) return round($bytes / 1073741824, 2) . ' GB';
        if ($bytes >= 1048576) return round($bytes / 1048576, 2) . ' MB';
        if ($bytes >= 1024) return round($bytes / 1024, 2) . ' KB';
        return $bytes . ' B';
    }
}

if (!function_exists('gemacc_delete_file_count')) {
    function gemacc_delete_file_count(string $file, array &$stats): void {
        if (!is_file($file)) return;
        $size = (int)@filesize($file);
        if (@unlink($file)) {
            $stats['deleted_files']++;
            $stats['freed_bytes'] += $size;
        }
    }
}

if (!function_exists('gemacc_collect_receipt_refs')) {
    function gemacc_collect_receipt_refs(?PDO $pdo): array {
        $refs = ['pending' => [], 'done' => []];
        if (!$pdo) return $refs;
        $queries = [
            "SELECT receipt_file_id,status FROM orders WHERE receipt_file_id IS NOT NULL AND receipt_file_id <> ''",
            "SELECT receipt_file_id,status FROM wallet_transactions WHERE receipt_file_id IS NOT NULL AND receipt_file_id <> ''",
        ];
        foreach ($queries as $sql) {
            try {
                foreach ($pdo->query($sql) as $row) {
                    $name = basename((string)($row['receipt_file_id'] ?? ''));
                    if ($name === '' || !preg_match('/^(receipt|deposit)_\d+_\d+\.(jpg|jpeg|png|gif|webp)$/i', $name)) continue;
                    $status = strtoupper((string)($row['status'] ?? ''));
                    if ($status === 'PENDING' || $status === 'WAITING' || $status === 'QUEUED') {
                        $refs['pending'][$name] = true;
                    } else {
                        $refs['done'][$name] = true;
                    }
                }
            } catch (Throwable $e) {
                // Some old installs may not have all columns/tables yet.
            }
        }
        return $refs;
    }
}

if (!function_exists('gemacc_clear_runtime_cache')) {
    /**
     * @param PDO|null $pdo دیتابیس برای تشخیص رسیدهای در انتظار؛ اگر null باشد فقط فایل‌های orphan/log/temp پاک می‌شوند.
     * @param array $options
     *   - receipt_retention_days: رسیدهای سفارش/واریز تایید/رد شده بعد از این تعداد روز پاک شوند. پیش‌فرض 3 روز.
     *   - delete_finished_receipts: اگر true باشد رسیدهای غیر pending بعد از retention پاک می‌شوند.
     */
    function gemacc_clear_runtime_cache(?PDO $pdo = null, array $options = []): array {
        $base = defined('BASE_PATH') ? BASE_PATH : dirname(__DIR__);
        $stats = [
            'deleted_files' => 0,
            'freed_bytes' => 0,
            'state_items' => 0,
            'uploads_deleted' => 0,
            'logs_deleted' => 0,
            'temp_deleted' => 0,
            'skipped_pending_receipts' => 0,
            'skipped_safe_files' => 0,
        ];
        $startDeleted = 0;

        // Bot conversation state cache: DB-backed state is authoritative from v27.46.
        if ($pdo) {
            try {
                $cnt = $pdo->query("SELECT COUNT(*) FROM bot_user_states WHERE expires_at IS NOT NULL AND expires_at < NOW()")->fetchColumn();
                $stats['state_items'] += (int)$cnt;
                $pdo->exec("DELETE FROM bot_user_states WHERE expires_at IS NOT NULL AND expires_at < NOW()");
            } catch (Throwable $ignored) {}
        }
        $stateFile = $base . '/src/legacy/state-cache.json';
        if (is_file($stateFile)) {
            $states = json_decode((string)@file_get_contents($stateFile), true);
            if (is_array($states)) $stats['state_items'] += count($states);
            @file_put_contents($stateFile, '{}');
        }

        // Temporary files
        $tempDirs = [$base . '/temp', $base . '/storage/cache', $base . '/storage/tmp'];
        foreach ($tempDirs as $dir) {
            if (!is_dir($dir)) continue;
            foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST) as $item) {
                $path = $item->getPathname();
                if ($item->isFile()) {
                    $before = $stats['deleted_files'];
                    gemacc_delete_file_count($path, $stats);
                    if ($stats['deleted_files'] > $before) $stats['temp_deleted']++;
                } elseif ($item->isDir()) {
                    @rmdir($path);
                }
            }
        }

        // Logs: remove .log/.txt runtime logs, keep placeholders/security files.
        $logDir = $base . '/storage/logs';
        if (is_dir($logDir)) {
            foreach (glob($logDir . '/*') ?: [] as $file) {
                $bn = basename($file);
                if (!is_file($file) || in_array($bn, ['.gitkeep', 'index.html', '.htaccess'], true)) continue;
                if (preg_match('/\.(log|txt)$/i', $bn)) {
                    $before = $stats['deleted_files'];
                    gemacc_delete_file_count($file, $stats);
                    if ($stats['deleted_files'] > $before) $stats['logs_deleted']++;
                }
            }
        }

        // Uploads: delete orphan old receipts and finished receipts after retention. Never delete pending receipts.
        $uploadDir = $base . '/public/mini_app/uploads';
        $refs = gemacc_collect_receipt_refs($pdo);
        $retentionDays = max(0, (int)($options['receipt_retention_days'] ?? 3));
        $deleteFinished = (bool)($options['delete_finished_receipts'] ?? true);
        $cutoff = time() - ($retentionDays * 86400);
        if (is_dir($uploadDir)) {
            foreach (glob($uploadDir . '/*') ?: [] as $file) {
                $bn = basename($file);
                if (is_dir($file)) continue;
                if (in_array($bn, ['.gitkeep', 'index.html', '.htaccess'], true)) { $stats['skipped_safe_files']++; continue; }
                if (!preg_match('/^(receipt|deposit)_\d+_\d+\.(jpg|jpeg|png|gif|webp)$/i', $bn)) continue;
                if (isset($refs['pending'][$bn])) { $stats['skipped_pending_receipts']++; continue; }
                $isDoneRef = isset($refs['done'][$bn]);
                $isOldEnough = @filemtime($file) ? @filemtime($file) <= $cutoff : true;
                if (!$isDoneRef || ($deleteFinished && $isOldEnough)) {
                    $before = $stats['deleted_files'];
                    gemacc_delete_file_count($file, $stats);
                    if ($stats['deleted_files'] > $before) $stats['uploads_deleted']++;
                }
            }
            // Broadcast uploads are cache-like; remove files older than 1 day.
            $broadcastDir = $uploadDir . '/broadcasts';
            if (is_dir($broadcastDir)) {
                foreach (glob($broadcastDir . '/*') ?: [] as $file) {
                    $bn = basename($file);
                    if (!is_file($file) || in_array($bn, ['.gitkeep', 'index.html', '.htaccess'], true)) continue;
                    if ((@filemtime($file) ?: 0) <= time() - 86400) {
                        $before = $stats['deleted_files'];
                        gemacc_delete_file_count($file, $stats);
                        if ($stats['deleted_files'] > $before) $stats['uploads_deleted']++;
                    }
                }
            }
        }
        $stats['freed_human'] = gemacc_format_bytes((int)$stats['freed_bytes']);
        return $stats;
    }
}
