<?php
date_default_timezone_set('Europe/Istanbul');

define('DATA_DIR',    __DIR__ . '/../data/');
define('UPLOADS_DIR', __DIR__ . '/../uploads/');
define('TOTAL_STORAGE', 10 * 1024 * 1024 * 1024); // 10 GB toplam
define('MAX_STORAGE',   5  * 1024 * 1024 * 1024); // 5 GB ortak alan
define('POOL_STORAGE',  5  * 1024 * 1024 * 1024); // 5 GB kullanıcı havuzu
define('MAX_FILE',      500 * 1024 * 1024); // 500 MB per file
define('EXPIRE_DAYS',   5);
define('SHARE_CODE_LEN', 8);

// ── Kullanıcı İşlemleri ──────────────────────────────────────────────────────

function getUsers(): array {
    $path = DATA_DIR . 'users.json';
    if (!file_exists($path)) return [];
    return json_decode(file_get_contents($path), true)['users'] ?? [];
}

function saveUsers(array $users): void {
    file_put_contents(DATA_DIR . 'users.json', json_encode(['users' => $users], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
}

function authenticateUser(string $username, string $password): ?array {
    foreach (getUsers() as $user) {
        if (strtolower($user['username']) === strtolower($username)) {
            if (($user['active'] ?? true) === false) return null;
            if (password_verify($password, $user['password_hash'])) {
                updateUserLastLogin($user['id']);
                return getUserById($user['id']);
            }
        }
    }
    return null;
}

function getUserById(string $id): ?array {
    foreach (getUsers() as $user) {
        if ($user['id'] === $id) return $user;
    }
    return null;
}

function getUserByEmail(string $email): ?array {
    foreach (getUsers() as $user) {
        if (strtolower($user['username']) === strtolower($email)) return $user;
    }
    return null;
}

function updateUserPassword(string $userId, string $newPassword): bool {
    $users = getUsers();
    foreach ($users as &$user) {
        if ($user['id'] === $userId) {
            $user['password_hash'] = password_hash($newPassword, PASSWORD_BCRYPT);
            saveUsers($users);
            return true;
        }
    }
    return false;
}

function createUser(string $username, string $password, string $displayName, int $storageLimit, string $role = 'user'): array {
    if (getPoolRemaining() < $storageLimit) {
        return ['msg' => 'Havuzda yeterli alan yok. Kullanılabilir: ' . formatBytes(getPoolRemaining()), 'type' => 'error'];
    }
    $users = getUsers();
    $id = 'user' . (count($users) + 1);
    $users[] = [
        'id'            => $id,
        'username'      => $username,
        'password_hash' => password_hash($password, PASSWORD_BCRYPT),
        'display_name'  => $displayName,
        'role'          => $role,
        'storage_limit' => $storageLimit,
        'active'        => true,
        'created_at'    => date('Y-m-d'),
    ];
    saveUsers($users);
    return ['id' => $id, 'msg' => 'Kullanıcı oluşturuldu.', 'type' => 'success'];
}

function deleteUser(string $userId): array {
    $users = getUsers();
    $found = false;
    foreach ($users as $i => $u) {
        if ($u['id'] === $userId) {
            // Kullanıcının özel dosyalarını sil
            $files = getFiles();
            foreach ($files as $f) {
                if (($f['owner_id'] ?? '') === $userId || ($f['recipient_id'] ?? '') === $userId) {
                    $disk = UPLOADS_DIR . $f['stored_name'];
                    if (file_exists($disk)) unlink($disk);
                    removeSharesByFileId($f['id']);
                }
            }
            $files = array_values(array_filter(getFiles(), fn($f) =>
                ($f['owner_id'] ?? '') !== $userId && ($f['recipient_id'] ?? '') !== $userId
            ));
            saveFiles($files);
            array_splice($users, $i, 1);
            saveUsers($users);
            $found = true;
            break;
        }
    }
    return $found
        ? ['msg' => 'Kullanıcı silindi.', 'type' => 'success']
        : ['msg' => 'Kullanıcı bulunamadı.', 'type' => 'error'];
}

function isUserAdmin(?array $user): bool {
    return $user ? ($user['role'] ?? 'user') === 'admin' : false;
}

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

// ── Dosya İşlemleri ──────────────────────────────────────────────────────────

function getFiles(): array {
    $path = DATA_DIR . 'files.json';
    if (!file_exists($path)) return [];
    return json_decode(file_get_contents($path), true)['files'] ?? [];
}

function saveFiles(array $files): void {
    file_put_contents(DATA_DIR . 'files.json', json_encode(['files' => $files], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
}

function addFile(array $meta): void {
    $files = getFiles();
    $files[] = $meta;
    saveFiles($files);
}

function getFileById(string $id): ?array {
    foreach (getFiles() as $f) {
        if ($f['id'] === $id) return $f;
    }
    return null;
}

function deleteFile(string $id): bool {
    $files = getFiles();
    foreach ($files as $i => $f) {
        if ($f['id'] === $id) {
            $disk = UPLOADS_DIR . $f['stored_name'];
            if (file_exists($disk)) unlink($disk);
            array_splice($files, $i, 1);
            saveFiles($files);
            removeSharesByFileId($id);

            $who = isset($_SESSION['username']) ? $_SESSION['username'] : 'Sistem';
            addSystemLog('file_delete', $who . ' dosya sildi: ' . ($f['original_name'] ?? 'Bilinmiyor'), $_SESSION['user_id'] ?? null);

            return true;
        }
    }
    return false;
}

function renameFile(string $id, string $newName): bool {
    $files = getFiles();
    foreach ($files as &$f) {
        if ($f['id'] === $id) {
            $old = $f['original_name'];
            $f['original_name'] = $newName;
            saveFiles($files);

            $who = isset($_SESSION['username']) ? $_SESSION['username'] : 'Sistem';
            addSystemLog('file_rename', $who . ' dosya adını değiştirdi: ' . $old . ' → ' . $newName, $_SESSION['user_id'] ?? null);

            return true;
        }
    }
    return false;
}

function getCommonFiles(): array {
    $files = getFiles();
    $active = array_filter($files, fn($f) => $f['area'] === 'common' && !isExpired($f));
    return array_values($active);
}

function getPrivateFiles(string $userId): array {
    $files = getFiles();
    $active = array_filter($files, fn($f) =>
        $f['area'] === 'private'
        && ($f['owner_id'] ?? '') === $userId
        && empty($f['recipient_id'])
        && !isExpired($f)
    );
    return array_values($active);
}

function getInboxFiles(string $userId): array {
    $files = getFiles();
    $active = array_filter($files, fn($f) =>
        in_array($f['area'], ['private', 'inbox'], true)
        && ($f['recipient_id'] ?? '') === $userId
        && !isExpired($f)
    );
    return array_values($active);
}

function getFileVersions(string $fileId): array {
    $file = getFileById($fileId);
    if (!$file || empty($file['version_group'])) return [];
    $all = getFiles();
    $versions = array_filter($all, function($f) use ($file) {
        return ($f['version_group'] ?? '') === $file['version_group']
            && !isExpired($f);
    });
    usort($versions, fn($a, $b) => ((int)($a['version'] ?? 1)) - ((int)($b['version'] ?? 1)));
    return array_values($versions);
}

function isExpired(array $file): bool {
    $expire = strtotime($file['uploaded_at']) + (EXPIRE_DAYS * 86400);
    return time() > $expire;
}

function getRemainingSeconds(array $file): int {
    $expire = strtotime($file['uploaded_at']) + (EXPIRE_DAYS * 86400);
    return max(0, $expire - time());
}

function formatExpire(array $file): string {
    $secs = getRemainingSeconds($file);
    if ($secs <= 0) return 'Süresi doldu';
    $days = floor($secs / 86400);
    $hours = floor(($secs % 86400) / 3600);
    if ($days > 0) return $days . ' gün ' . $hours . ' saat';
    return $hours . ' saat';
}

function getCommonUsedBytes(): int {
    $total = 0;
    foreach (getFiles() as $f) {
        if ($f['area'] === 'common' && !isExpired($f)) {
            $total += (int)($f['size'] ?? 0);
        }
    }
    return $total;
}

function getUserStorageUsed(string $userId): int {
    $total = 0;
    foreach (getFiles() as $f) {
        if ($f['area'] === 'private'
            && (($f['owner_id'] ?? '') === $userId || ($f['recipient_id'] ?? '') === $userId)
            && !isExpired($f)) {
            $total += (int)($f['size'] ?? 0);
        }
    }
    return $total;
}

function getUserStorageLimit(string $userId): int {
    $user = getUserById($userId);
    return $user ? (int)($user['storage_limit'] ?? 0) : 0;
}

function getPoolTotal(): int {
    return POOL_STORAGE;
}

function getPoolAssigned(): int {
    $total = 0;
    foreach (getUsers() as $u) {
        $total += (int)($u['storage_limit'] ?? 0);
    }
    return $total;
}

function getPoolRemaining(): int {
    return POOL_STORAGE - getPoolAssigned();
}

function updateUserLastLogin(string $userId): void {
    $users = getUsers();
    foreach ($users as &$u) {
        if ($u['id'] === $userId) {
            $u['last_login'] = date('Y-m-d H:i:s');
            saveUsers($users);
            return;
        }
    }
}

function updateUserStorageLimit(string $userId, int $newLimit): array {
    $users = getUsers();
    foreach ($users as &$u) {
        if ($u['id'] === $userId) {
            $oldLimit = (int)($u['storage_limit'] ?? 0);
            $diff = $newLimit - $oldLimit;
            if ($diff > 0 && getPoolRemaining() < $diff) {
                return ['msg' => 'Havuzda yeterli alan yok. Kullanılabilir: ' . formatBytes(getPoolRemaining()), 'type' => 'error'];
            }
            $u['storage_limit'] = $newLimit;
            saveUsers($users);
            return ['msg' => 'Depolama limiti güncellendi.', 'type' => 'success'];
        }
    }
    return ['msg' => 'Kullanıcı bulunamadı.', 'type' => 'error'];
}

function deactivateUser(string $userId): array {
    $users = getUsers();
    foreach ($users as &$u) {
        if ($u['id'] === $userId) {
            $u['active'] = !($u['active'] ?? true);
            saveUsers($users);
            $status = $u['active'] ? 'aktifleştirildi' : 'devre dışı bırakıldı';
            return ['msg' => "Kullanıcı $status.", 'type' => 'success'];
        }
    }
    return ['msg' => 'Kullanıcı bulunamadı.', 'type' => 'error'];
}

function getMimeIcon(string $mime, string $ext): string {
    $ext = strtolower($ext);
    $icons = [
        'pdf' => '📄', 'doc' => '📝', 'docx' => '📝', 'xls' => '📊', 'xlsx' => '📊',
        'ppt' => '📊', 'pptx' => '📊', 'txt' => '📃', 'csv' => '📊',
        'jpg' => '🖼️', 'jpeg' => '🖼️', 'png' => '🖼️', 'gif' => '🎞️', 'webp' => '🖼️', 'svg' => '🎨',
        'mp4' => '🎬', 'avi' => '🎬', 'mov' => '🎬', 'mkv' => '🎬',
        'mp3' => '🎵', 'wav' => '🎵', 'ogg' => '🎵',
        'zip' => '📦', 'rar' => '📦', '7z' => '📦', 'tar' => '📦', 'gz' => '📦',
        'php' => '💻', 'js' => '💻', 'ts' => '💻', 'jsx' => '💻', 'tsx' => '💻',
        'py' => '💻', 'html' => '💻', 'htm' => '💻', 'css' => '💻', 'scss' => '💻',
        'rb' => '💻', 'go' => '💻', 'rs' => '💻', 'java' => '💻', 'kt' => '💻',
        'swift' => '💻', 'c' => '💻', 'cpp' => '💻', 'h' => '💻', 'cs' => '💻',
        'sh' => '💻', 'bash' => '💻', 'ps1' => '💻', 'bat' => '💻', 'cmd' => '💻',
        'pl' => '💻', 'lua' => '💻', 'r' => '💻',
        'json' => '⚙️', 'xml' => '⚙️', 'sql' => '🗄️',
    ];
    return $icons[$ext] ?? '📁';
}

function isPreviewable(string $mime): bool {
    $previewable = ['image/', 'video/', 'audio/', 'text/plain', 'application/pdf'];
    foreach ($previewable as $p) {
        if (str_starts_with($mime, $p)) return true;
    }
    return false;
}

// ── Yükleme ──────────────────────────────────────────────────────────────────

function handleGuestUpload(array $post, array $files): array {
    if (empty($files['file']['tmp_name'])) {
        return ['msg' => 'Lütfen bir dosya seçiniz.', 'type' => 'error'];
    }

    $recipientEmail = trim($post['recipient_email'] ?? '');
    if (!filter_var($recipientEmail, FILTER_VALIDATE_EMAIL)) {
        return ['msg' => 'Geçersiz alıcı e-posta adresi.', 'type' => 'error'];
    }

    $recipient = getUserByEmail($recipientEmail);
    if (!$recipient) {
        return ['msg' => 'Bu e-posta adresinde kayıtlı kullanıcı bulunamadı.', 'type' => 'error'];
    }

    return uploadFile($files['file'], 'private', null, $recipient['id'], $post['sender_email'] ?? '', $post['message'] ?? '');
}

function handleUserUpload(array $fileData, string $uploaderUserId, string $area = 'common'): array {
    return uploadFile($fileData, $area, $uploaderUserId, null, '', '');
}

function uploadFile(array $fileData, string $area, ?string $ownerId, ?string $recipientId, string $senderEmail, string $message): array {
    if ($fileData['error'] !== UPLOAD_ERR_OK) {
        return ['msg' => 'Yükleme hatası: ' . $fileData['error'], 'type' => 'error'];
    }

    $size = $fileData['size'];

    if ($size > MAX_FILE) {
        return ['msg' => '500 MB üzeri dosya yüklenemez.', 'type' => 'error'];
    }

    if ($area === 'common') {
        if (getCommonUsedBytes() + $size > MAX_STORAGE) {
            return ['msg' => 'Ortak depolama alanı (5 GB) doldu.', 'type' => 'error'];
        }
    } elseif ($area === 'private') {
        $limit = $ownerId ? getUserStorageLimit($ownerId) : 0;
        $used = $ownerId ? getUserStorageUsed($ownerId) : 0;
        if ($limit > 0 && $used + $size > $limit) {
            return ['msg' => 'Özel depolama alanınız doldu (' . formatBytes($limit) . ').', 'type' => 'error'];
        }
    }

    $originalName = basename($fileData['name']);
    $ext = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));
    $mime = function_exists('mime_content_type') ? (mime_content_type($fileData['tmp_name']) ?: 'application/octet-stream') : 'application/octet-stream';

    // Uzantı kontrolü (sadece izin verilenler)
    if ($ext && !isExtensionAllowed($ext)) {
        return ['msg' => 'Bu dosya türü (.'. $ext .') sistem tarafından engellenmiştir.', 'type' => 'error'];
    }

    $isCode = false;
    $originalExt = $ext;
    if ($ext && isCodeExtension($ext)) {
        $isCode = true;
    }

    $id = bin2hex(random_bytes(8));
    $storedName = $id . ($ext ? '.' . $ext : '');

    // Versiyonlama: aynı isim, alan ve sahip varsa versiyon oluştur
    $version = 1;
    $versionGroup = '';
    $existingFiles = getFiles();
    // İlk eşleşen dosyayı bul (original_name, area, owner_id aynı olan)
    $matchedVersionGroup = '';
    $maxVersion = 0;
    foreach ($existingFiles as $ef) {
        if ($ef['original_name'] === $originalName
            && ($ef['area'] ?? '') === $area
            && ($ef['owner_id'] ?? '') === $ownerId
            && !isExpired($ef)) {
            $matchedVersionGroup = $ef['version_group'] ?? '';
            $v = (int)($ef['version'] ?? 1);
            if ($v > $maxVersion) $maxVersion = $v;
        }
    }
    // Aynı version_group içindeki tüm dosyaları da tara
    if ($matchedVersionGroup) {
        foreach ($existingFiles as $ef) {
            if (($ef['version_group'] ?? '') === $matchedVersionGroup && !isExpired($ef)) {
                $v = (int)($ef['version'] ?? 1);
                if ($v > $maxVersion) $maxVersion = $v;
            }
        }
    }
    if ($maxVersion > 0) {
        $versionGroup = $matchedVersionGroup;
        $version = $maxVersion + 1;
        // İlk eşleşen dosyada version_group yoksa ata
        if (empty($matchedVersionGroup)) {
            $versionGroup = $id;
            foreach ($existingFiles as &$ef2) {
                if ($ef2['original_name'] === $originalName
                    && ($ef2['area'] ?? '') === $area
                    && ($ef2['owner_id'] ?? '') === $ownerId) {
                    $ef2['version_group'] = $versionGroup;
                    $ef2['version'] = 1;
                    break;
                }
            }
            saveFiles($existingFiles);
        }
        $nameNoExt = pathinfo($originalName, PATHINFO_FILENAME);
        $originalName = $nameNoExt . '_v' . $version . ($ext ? '.' . $ext : '');
        $storedName = $id . '_v' . $version . ($ext ? '.' . $ext : '');
    } else {
        $versionGroup = $id;
    }

    if (!is_dir(UPLOADS_DIR)) mkdir(UPLOADS_DIR, 0755, true);

    if ($isCode) {
        if (!class_exists('ZipArchive')) {
            return ['msg' => 'Sunucuda ZIP desteği bulunmuyor. Kod dosyası yüklenemez.', 'type' => 'error'];
        }
        // Kod dosyasını zip'le
        $zip = new ZipArchive();
        $zipPath = UPLOADS_DIR . $storedName . '.zip';
        $storedNameZip = $storedName . '.zip';
        if ($zip->open($zipPath, ZipArchive::CREATE) !== true) {
            return ['msg' => 'ZIP oluşturulamadı.', 'type' => 'error'];
        }
        $zip->addFile($fileData['tmp_name'], $originalName);
        $zip->close();
        $storedName = $storedNameZip;
        $ext = 'zip';
        $mime = 'application/zip';
        $size = filesize($zipPath);
    } else {
        if (!move_uploaded_file($fileData['tmp_name'], UPLOADS_DIR . $storedName)) {
            return ['msg' => 'Dosya kaydedilemedi.', 'type' => 'error'];
        }
    }

    $meta = [
        'id'            => $id,
        'original_name' => $originalName,
        'stored_name'   => $storedName,
        'size'          => $size,
        'mime'          => $mime,
        'ext'           => $ext,
        'area'          => $area,
        'owner_id'      => $ownerId,
        'recipient_id'  => $recipientId,
        'sender_email'  => $senderEmail,
        'message'       => $message,
        'uploaded_at'   => date('Y-m-d H:i:s'),
        'version'       => $version,
        'version_group' => $versionGroup,
    ];
    if ($isCode) {
        $meta['code_zipped'] = true;
        $meta['original_ext'] = $originalExt;
    }
    addFile($meta);

    $who = $ownerId ? (getUserById($ownerId)['username'] ?? 'Bilinmiyor') : ($senderEmail ?: 'Misafir');
    addSystemLog('file_upload', $who . ' dosya yükledi: ' . $originalName . ' (' . formatBytes($size) . ', ' . $area . ')', $ownerId);

    return ['msg' => 'Dosya başarıyla yüklendi!', 'type' => 'success', 'file_id' => $id];
}

// ── Paylaşım Kodları ─────────────────────────────────────────────────────────

function getShares(): array {
    $path = DATA_DIR . 'shares.json';
    if (!file_exists($path)) return [];
    return json_decode(file_get_contents($path), true)['shares'] ?? [];
}

function saveShares(array $shares): void {
    file_put_contents(DATA_DIR . 'shares.json', json_encode(['shares' => $shares], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
}

function createShare(string $fileId, ?string $userId = null, int $maxAccess = 0, int $hoursUntilExpire = 0): string {
    $shares = getShares();

    foreach ($shares as $s) {
        if ($s['file_id'] === $fileId && !isShareExpired($s)) {
            return $s['code'];
        }
    }

    do {
        $code = strtoupper(substr(bin2hex(random_bytes(4)), 0, SHARE_CODE_LEN));
        $exists = false;
        foreach ($shares as $s) {
            if ($s['code'] === $code) { $exists = true; break; }
        }
    } while ($exists);

    if ($hoursUntilExpire > 0) {
        $expireAt = date('Y-m-d H:i:s', time() + ($hoursUntilExpire * 3600));
    } else {
        $file = getFileById($fileId);
        $expireAt = $file ? date('Y-m-d H:i:s', strtotime($file['uploaded_at']) + (EXPIRE_DAYS * 86400)) : date('Y-m-d H:i:s', time() + (EXPIRE_DAYS * 86400));
    }

    $shares[] = [
        'code'             => $code,
        'file_id'          => $fileId,
        'created_by'       => $userId,
        'created_at'       => date('Y-m-d H:i:s'),
        'expires_at'       => $expireAt,
        'max_access_count' => $maxAccess,
        'access_count'     => 0,
    ];
    saveShares($shares);

    $who = $userId ? (getUserById($userId)['username'] ?? 'Bilinmiyor') : (isset($_SESSION['username']) ? $_SESSION['username'] : 'Sistem');
    $file = getFileById($fileId);
    $fname = $file ? $file['original_name'] : 'Bilinmiyor';
    addSystemLog('share_create', $who . ' paylaşım oluşturdu: ' . $fname . ' (Kod: ' . $code . ')', $userId);

    return $code;
}

function getShareByCode(string $code): ?array {
    foreach (getShares() as $s) {
        if ($s['code'] === strtoupper($code) && !isShareExpired($s)) {
            if (isShareAccessExceeded($s)) return null;
            return $s;
        }
    }
    return null;
}

function isShareExpired(array $share): bool {
    return time() > strtotime($share['expires_at']);
}

function isShareAccessExceeded(array $share): bool {
    $max = $share['max_access_count'] ?? 0;
    return $max > 0 && ($share['access_count'] ?? 0) >= $max;
}

function incrementShareAccess(string $code): void {
    $shares = getShares();
    foreach ($shares as &$s) {
        if ($s['code'] === $code) {
            $s['access_count'] = ($s['access_count'] ?? 0) + 1;
            break;
        }
    }
    saveShares($shares);
}

function removeSharesByFileId(string $fileId): void {
    $shares = getShares();
    $shares = array_values(array_filter($shares, fn($s) => $s['file_id'] !== $fileId));
    saveShares($shares);
}

function getSharesByUser(string $userId): array {
    return array_values(array_filter(getShares(), fn($s) => ($s['created_by'] ?? '') === $userId));
}

function deleteShare(string $code): bool {
    $shares = getShares();
    $count = count($shares);
    $shares = array_values(array_filter($shares, fn($s) => $s['code'] !== $code));
    if (count($shares) < $count) {
        saveShares($shares);

        $who = isset($_SESSION['username']) ? $_SESSION['username'] : 'Sistem';
        addSystemLog('share_delete', $who . ' paylaşım kaldırdı: ' . $code, $_SESSION['user_id'] ?? null);

        return true;
    }
    return false;
}

// ── Share Access Log ─────────────────────────────────────────────────────────

function logShareAccess(string $code): void {
    $path = DATA_DIR . 'share_access_logs.json';
    $logs = [];
    if (file_exists($path)) {
        $content = @file_get_contents($path);
        if ($content !== false) {
            $data = json_decode($content, true);
            if (is_array($data)) $logs = $data;
        }
    }
    $logs[] = [
        'code'       => $code,
        'ip'         => getClientIP(),
        'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
        'time'       => time(),
    ];
    if (count($logs) > 2000) $logs = array_slice($logs, -2000);
    file_put_contents($path, json_encode($logs, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), LOCK_EX);
}

function getShareAccessLogs(string $code, int $limit = 50): array {
    $path = DATA_DIR . 'share_access_logs.json';
    if (!file_exists($path)) return [];
    $content = @file_get_contents($path);
    if ($content === false) return [];
    $data = json_decode($content, true);
    if (!is_array($data)) return [];
    $logs = array_values(array_filter($data, fn($l) => ($l['code'] ?? '') === $code));
    $logs = array_reverse($logs);
    return $limit ? array_slice($logs, 0, $limit) : $logs;
}

function getAllSharesWithCreator(): array {
    $shares = getShares();
    $users = getUsers();
    $userMap = [];
    foreach ($users as $u) {
        $userMap[$u['id']] = $u['display_name'];
    }
    foreach ($shares as &$s) {
        $s['creator_name'] = $userMap[$s['created_by'] ?? ''] ?? 'Bilinmiyor';
    }
    usort($shares, fn($a, $b) => strtotime($b['created_at']) - strtotime($a['created_at']));
    return $shares;
}

// ── Share Rate Limiting ──────────────────────────────────────────────────────

function checkShareLockout(): ?array {
    $key = 'share_' . getClientIP();
    $data = getLoginAttempts();
    $now = time();
    $record = $data[$key] ?? null;
    if (!$record) return null;
    if (($record['lockout_until'] ?? 0) > $now) {
        $remaining = $record['lockout_until'] - $now;
        $total = getLockoutDuration($record['lockout_level'] ?? 1);
        return ['remaining' => $remaining, 'level' => $record['lockout_level'] ?? 1, 'total' => $total > 0 ? $total : $remaining];
    }
    return null;
}

function recordShareAttempt(): ?array {
    $key = 'share_' . getClientIP();
    $data = getLoginAttempts();
    $now = time();
    $record = $data[$key] ?? ['attempts' => [], 'lockout_level' => 0, 'lockout_until' => 0];
    if (($record['lockout_until'] ?? 0) > $now) return null;
    $record['attempts'] = array_values(array_filter($record['attempts'], function($t) use ($now) { return $t > $now - 60; }));
    $record['attempts'][] = $now;
    $result = null;
    if (count($record['attempts']) > 5) {
        $record['lockout_level'] = min(($record['lockout_level'] ?? 0) + 1, 3);
        $duration = getLockoutDuration($record['lockout_level']);
        $record['lockout_until'] = $now + $duration;
        $record['attempts'] = [];
        $result = ['remaining' => $duration, 'level' => $record['lockout_level'], 'total' => $duration];
    }
    $data[$key] = $record;
    saveLoginAttempts($data);
    return $result;
}

// ── Temizlik ─────────────────────────────────────────────────────────────────

function cleanExpiredFiles(): int {
    $files = getFiles();
    $deleted = 0;
    foreach ($files as $f) {
        if (isExpired($f)) {
            $disk = UPLOADS_DIR . $f['stored_name'];
            if (file_exists($disk)) unlink($disk);
            removeSharesByFileId($f['id']);
            $deleted++;
        }
    }
    $active = array_values(array_filter($files, fn($f) => !isExpired($f)));
    saveFiles($active);
    return $deleted;
}

// ── Auth Guard ───────────────────────────────────────────────────────────────

function requireLogin(): void {
    if (!isset($_SESSION['user_id'])) {
        header('Location: index.php');
        exit;
    }
}

function currentUser(): ?array {
    return isset($_SESSION['user_id']) ? getUserById($_SESSION['user_id']) : null;
}

// ── Login Rate Limiting ──────────────────────────────────────────────────────

function getClientIP(): string {
    if (!empty($_SERVER['HTTP_CLIENT_IP'])) return $_SERVER['HTTP_CLIENT_IP'];
    if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
        return trim($ips[0]);
    }
    return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
}

function getLoginAttempts(): array {
    $path = DATA_DIR . 'login_attempts.json';
    if (!file_exists($path)) return [];
    $content = @file_get_contents($path);
    if ($content === false) return [];
    $data = json_decode($content, true);
    return is_array($data) ? $data : [];
}

function saveLoginAttempts(array $data): void {
    file_put_contents(DATA_DIR . 'login_attempts.json', json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), LOCK_EX);
}

function formatLockoutTime(int $seconds): string {
    if ($seconds >= 60) {
        return ceil($seconds / 60) . ' dakika';
    }
    return $seconds . ' saniye';
}

function checkLoginLockout(): ?array {
    $ip = getClientIP();
    $data = getLoginAttempts();
    $now = time();
    $record = $data[$ip] ?? null;
    if (!$record) return null;
    if (($record['lockout_until'] ?? 0) > $now) {
        $remaining = $record['lockout_until'] - $now;
        return [
            'remaining' => $remaining,
            'total'     => 600,
        ];
    }
    return null;
}

function recordFailedAttempt(): ?array {
    $ip = getClientIP();
    $data = getLoginAttempts();
    $now = time();
    $record = $data[$ip] ?? ['attempts' => [], 'lockout_until' => 0];
    if (($record['lockout_until'] ?? 0) > $now) return null;
    $record['attempts'] = array_values(array_filter($record['attempts'], function($t) use ($now) {
        return $t > $now - 60;
    }));
    $record['attempts'][] = $now;
    $result = null;
    if (count($record['attempts']) > 10) {
        $record['lockout_until'] = $now + 600;
        $record['attempts'] = [];
        $result = ['remaining' => 600, 'total' => 600];
    }
    $data[$ip] = $record;
    saveLoginAttempts($data);
    return $result;
}

function resetLoginAttempts(): void {
    $ip = getClientIP();
    $data = getLoginAttempts();
    unset($data[$ip]);
    saveLoginAttempts($data);
}

// ── Login Log ────────────────────────────────────────────────────────────────

function addLoginLog(string $username, ?string $userId, string $status, bool $lockout = false, int $lockoutLevel = 0): void {
    $path = DATA_DIR . 'login_log.json';
    $logs = [];
    if (file_exists($path)) {
        $content = @file_get_contents($path);
        if ($content !== false) {
            $data = json_decode($content, true);
            if (is_array($data)) $logs = $data;
        }
    }
    $logs[] = [
        'id'            => 'log_' . bin2hex(random_bytes(6)),
        'username'      => $username,
        'user_id'       => $userId,
        'ip'            => getClientIP(),
        'time'          => time(),
        'status'        => $status,
        'lockout'       => $lockout,
        'lockout_level' => $lockoutLevel,
    ];
    if (count($logs) > 500) $logs = array_slice($logs, -500);
    file_put_contents($path, json_encode($logs, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), LOCK_EX);
}

function getLoginLogs(?int $limit = 50): array {
    $path = DATA_DIR . 'login_log.json';
    if (!file_exists($path)) return [];
    $content = @file_get_contents($path);
    if ($content === false) return [];
    $data = json_decode($content, true);
    if (!is_array($data)) return [];
    $data = array_reverse($data);
    return $limit ? array_slice($data, 0, $limit) : $data;
}

function getLoginLogsByUser(string $userId, ?int $limit = 20): array {
    $logs = getLoginLogs(null);
    $logs = array_values(array_filter($logs, fn($l) => ($l['user_id'] ?? '') === $userId));
    return $limit ? array_slice($logs, 0, $limit) : $logs;
}

// ── Notifications ────────────────────────────────────────────────────────────

function getNotificationsData(): array {
    $path = DATA_DIR . 'notifications.json';
    if (!file_exists($path)) return [];
    $content = @file_get_contents($path);
    if ($content === false) return [];
    $data = json_decode($content, true);
    return is_array($data) ? $data : [];
}

function saveNotificationsData(array $data): void {
    file_put_contents(DATA_DIR . 'notifications.json', json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), LOCK_EX);
}

function addNotification(string $userId, string $type, string $message): void {
    $data = getNotificationsData();
    if (!isset($data[$userId])) $data[$userId] = [];
    $data[$userId][] = [
        'id'      => 'notif_' . bin2hex(random_bytes(6)),
        'type'    => $type,
        'message' => $message,
        'time'    => time(),
    ];
    if (count($data[$userId]) > 4) {
        $data[$userId] = array_slice($data[$userId], -4);
    }
    saveNotificationsData($data);
}

function getNotifications(string $userId, int $limit = 4): array {
    $data = getNotificationsData();
    $notifs = array_reverse($data[$userId] ?? []);
    return array_slice($notifs, 0, $limit);
}

function getUnreadNotificationCount(string $userId): int {
    return count(getNotificationsData()[$userId] ?? []);
}

function timeAgo(int $timestamp): string {
    $diff = time() - $timestamp;
    if ($diff < 60) return 'Az önce';
    if ($diff < 3600) return floor($diff / 60) . ' dk önce';
    if ($diff < 86400) return floor($diff / 3600) . ' saat önce';
    return floor($diff / 86400) . ' gün önce';
}

// ── Active Session (tek cihaz) ──────────────────────────────────────────────

function getActiveSessions(): array {
    $path = DATA_DIR . 'active_sessions.json';
    if (!file_exists($path)) return [];
    $content = @file_get_contents($path);
    if ($content === false) return [];
    $data = json_decode($content, true);
    return is_array($data) ? $data : [];
}

function saveActiveSessions(array $data): void {
    file_put_contents(DATA_DIR . 'active_sessions.json', json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), LOCK_EX);
}

function setActiveSession(string $userId): void {
    $sessions = getActiveSessions();
    $sessions[$userId] = session_id();
    saveActiveSessions($sessions);
}

function checkActiveSession(string $userId): bool {
    $sessions = getActiveSessions();
    return ($sessions[$userId] ?? '') === session_id();
}

// ── Sistem Logları ────────────────────────────────────────────────────────────

function getSystemLogsData(): array {
    $path = DATA_DIR . 'system_log.json';
    if (!file_exists($path)) return [];
    $content = @file_get_contents($path);
    if ($content === false) return [];
    $data = json_decode($content, true);
    return is_array($data) ? $data : [];
}

function saveSystemLogsData(array $data): void {
    file_put_contents(DATA_DIR . 'system_log.json', json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE), LOCK_EX);
}

function addSystemLog(string $type, string $message, ?string $userId = null): void {
    $data = getSystemLogsData();
    $data[] = [
        'id'      => 'syslog_' . bin2hex(random_bytes(6)),
        'type'    => $type,
        'message' => $message,
        'user_id' => $userId,
        'ip'      => getClientIP(),
        'time'    => time(),
    ];
    if (count($data) > 500) $data = array_slice($data, -500);
    saveSystemLogsData($data);
}

function getSystemLogs(?int $limit = 100): array {
    $data = getSystemLogsData();
    $data = array_reverse($data);
    return $limit ? array_slice($data, 0, $limit) : $data;
}

// ── CSRF Koruması ────────────────────────────────────────────────────────────

function generateCsrfToken(): string {
    if (empty($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }
    return $_SESSION['csrf_token'];
}

function csrfField(): string {
    return '<input type="hidden" name="csrf_token" value="' . generateCsrfToken() . '">';
}

function verifyCsrfToken(): void {
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        $token = $_POST['csrf_token'] ?? '';
        if (empty($_SESSION['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $token)) {
            die('CSRF doğrulaması başarısız. Lütfen sayfayı yenileyip tekrar deneyin.');
        }
    }
}

// ── İzin Verilen Dosya Uzantıları ────────────────────────────────────────────

function getDefaultExtensions(): array {
    return [
        // ── Resim / Görsel ──
        'jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp', 'ico', 'tiff', 'tif',
        'heic', 'heif', 'avif', 'raw', 'cr2', 'nef', 'orf', 'dng', 'psd', 'ai', 'eps',
        // ── Ofis / Belge ──
        'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'ppsx', 'potx', 'xlsm',
        'txt', 'csv', 'rtf', 'odt', 'ods', 'odp', 'pages', 'numbers', 'key',
        'epub', 'mobi', 'cbr', 'cbz',
        // ── Arşiv / Sıkıştırma ──
        'zip', 'rar', '7z', 'tar', 'gz', 'bz2', 'xz', 'zst', 'lz', 'lzma',
        // ── Video ──
        'mp4', 'avi', 'mov', 'mkv', 'wmv', 'flv', 'webm', 'm4v', 'mpg', 'mpeg',
        '3gp', 'ogv', 'ts', 'mts', 'm2ts',
        // ── Ses ──
        'mp3', 'wav', 'ogg', 'flac', 'aac', 'wma', 'm4a', 'opus', 'ac3', 'dts',
        'mid', 'midi', 'aiff', 'au',
        // ── Veri / Kod / Yapılandırma ──
        'json', 'xml', 'sql', 'md', 'yaml', 'yml', 'toml', 'ini', 'cfg', 'conf',
        'log', 'csv',
        // ── Disk / Sanal Makine ──
        'dmg', 'iso', 'vdi', 'vmdk', 'ova', 'ovf', 'vhd', 'vhdx',
        // ── CAD / 3D / Model ──
        'dwg', 'dxf', 'skp', '3dm', 'stl', 'obj', 'fbx', 'blend', '3ds', 'max',
        // ── Yazı Tipi ──
        'ttf', 'otf', 'woff', 'woff2', 'eot',
        // ── Çalıştırılabilir / Yükleyici ──
        'exe', 'msi', 'apk', 'dll', 'deb', 'rpm', 'appimage', 'run',
        // ── Alt Yazı / Playlist ──
        'srt', 'vtt', 'ass', 'ssa', 'sub', 'idx', 'm3u', 'm3u8', 'pls',
        // ── Torrent / İndirme ──
        'torrent', 'nzb',
        // ── Diğer ──
        'vcf', 'ics', 'rdp', 'dat', 'bin', 'cue', 'gpx', 'kml', 'kmz',
    ];
}

function getDefaultCodeExtensions(): array {
    return [
        // ── Web ──
        'php', 'py', 'js', 'ts', 'jsx', 'tsx', 'vue', 'svelte', 'astro', 'ejs',
        'hbs', 'pug', 'jade', 'haml', 'mustache', 'njk',
        'html', 'htm', 'xhtml', 'css', 'scss', 'sass', 'less', 'styl',
        // ── Backend / Sistem ──
        'rb', 'go', 'rs', 'java', 'kt', 'kts', 'swift', 'scala', 'sc',
        'c', 'cpp', 'cc', 'cxx', 'h', 'hpp', 'hxx', 'cs',
        'dart', 'groovy', 'kotlin',
        // ── Shell / Script ──
        'sh', 'bash', 'zsh', 'fish', 'ps1', 'psm1', 'psd1',
        'bat', 'cmd', 'awk', 'sed',
        // ── Veritabanı / Veri ──
        'sql', 'mysql', 'pgsql', 'sqlite', 'graphql', 'gql', 'prisma',
        // ── Fonksiyonel / Akademik ──
        'pl', 'pm', 'lua', 'r', 'rdata', 'rds',
        'hs', 'lhs', 'ml', 'mli', 'clj', 'cljs', 'cljc', 'edn',
        'ex', 'exs', 'erl', 'hrl', 'elm',
        // ── Alt Seviye / Derleme ──
        'asm', 's', 'pas', 'pp', 'm', 'mm',
        'zig', 'nim', 'cr', 'v', 'hack', 'purs',
        // ── Yapılandırma / Infra ──
        'tf', 'hcl', 'dockerfile', 'cmake', 'gradle', 'makefile', 'mk',
        'yaml', 'yml', 'toml', 'ini', 'cfg', 'conf',
        'env', 'editorconfig', 'gitignore',
        // ── Diğer ──
        'coffee', 'litcoffee',
        'fs', 'fsx', 'fsi',
        'erl', 'app', 'rel',
        'proto', 'thrift',
        'wgsl', 'glsl', 'hlsl', 'metal',
    ];
}

function isCodeExtension(string $ext): bool {
    return in_array(strtolower($ext), getDefaultCodeExtensions(), true);
}

function getAllowedExtensions(): array {
    $path = DATA_DIR . 'allowed_extensions.json';
    if (!file_exists($path)) return getDefaultExtensions();
    $content = @file_get_contents($path);
    if ($content === false) return getDefaultExtensions();
    $data = json_decode($content, true);
    if (!is_array($data) || !isset($data['extensions'])) return getDefaultExtensions();
    return $data['extensions'];
}

function saveAllowedExtensions(array $extensions): void {
    $extensions = array_values(array_unique(array_filter(array_map('strtolower', $extensions))));
    file_put_contents(
        DATA_DIR . 'allowed_extensions.json',
        json_encode(['extensions' => $extensions], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE),
        LOCK_EX
    );
}

function isExtensionAllowed(string $ext): bool {
    return in_array(strtolower($ext), getAllowedExtensions(), true);
}

function getCodeExtensionStats(): array {
    $files = getFiles();
    $stats = [];
    foreach ($files as $f) {
        if (!empty($f['code_zipped']) && !empty($f['original_ext'])) {
            $e = strtolower($f['original_ext']);
            $stats[$e] = ($stats[$e] ?? 0) + 1;
        }
    }
    arsort($stats);
    return $stats;
}
