<?php
declare(strict_types=1);

/**
 * /functional/files/upload.php
 * Приймає multipart upload:
 * - files[] (можна кілька)
 * - folder_id (0 = root)
 * Обмеження:
 * - 50MB / файл
 * - квота 1GB на користувача
 * Зберігає у /upload/{username}/myfiles/(fld_{folder_id}/) + запис у БД
 *
 * SEO: службовий endpoint => noindex
 */

require_once dirname(__DIR__, 2) . '/includes/start.php';

/** @var PDO|null $pdo */
/** @var array|null $me */
$pdo = (isset($pdo) && $pdo instanceof PDO) ? $pdo : db();
$me  = $me ?? current_user();

/* SEO / robots */
if (!headers_sent()) {
    header('X-Robots-Tag: noindex, nofollow, noarchive, nosnippet', true);
    header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0', true);
    header('Pragma: no-cache', true);
    header('Expires: 0', true);
    header('X-Content-Type-Options: nosniff', true);
    header('Referrer-Policy: same-origin', true);
}

/* fallbacks */
if (!function_exists('redirect')) {
    function redirect(string $to): void { header('Location: ' . $to, true, 303); exit; }
}
if (!function_exists('csrf_check')) {
    function csrf_check(?string $t): bool {
        return !empty($t) && !empty($_SESSION['csrf']) && hash_equals((string)$_SESSION['csrf'], (string)$t);
    }
}
if (!function_exists('is_post')) {
    function is_post(): bool { return (string)($_SERVER['REQUEST_METHOD'] ?? '') === 'POST'; }
}

/* flash compatible with includes/header.php */
if (!function_exists('mf_flash_set')) {
    function mf_flash_set(string $k, string $v): void {
        if (!isset($_SESSION)) return;
        if (!isset($_SESSION['_flash']) || !is_array($_SESSION['_flash'])) $_SESSION['_flash'] = [];
        $_SESSION['_flash'][$k] = $v;
    }
}

/* small i18n helper for messages */
function mf_lang(): string {
    $l = (string)($_SESSION['lang'] ?? '');
    $l = strtolower(trim($l));
    if ($l === 'ua') $l = 'uk';
    if (!in_array($l, ['uk','ru','en'], true)) $l = 'uk';
    return $l;
}
function mf_msg(string $uk, string $ru, string $en): string {
    return match (mf_lang()) {
        'ru' => $ru,
        'en' => $en,
        default => $uk,
    };
}

function mf_is_dangerous_ext(string $name): bool {
    $name = strtolower($name);
    $ext = pathinfo($name, PATHINFO_EXTENSION);
    $bad = [
        'php','phtml','php3','php4','php5','phar',
        'cgi','pl','asp','aspx',
        'js','mjs',
        'sh','bat','cmd','exe','com','dll','msi',
        'htaccess','ini'
    ];
    return $ext !== '' && in_array($ext, $bad, true);
}

function mf_safe_username(string $username): string {
    $username = trim($username);
    $username = preg_replace('~[^\pL\pN_\-\.]~u', '_', $username) ?? 'user';
    $username = trim($username, "._- \t\n\r\0\x0B");
    if ($username === '') $username = 'user';
    if (mb_strlen($username, 'UTF-8') > 64) $username = mb_substr($username, 0, 64, 'UTF-8');
    return $username;
}

function mf_safe_orig_name(string $name): string {
    $name = str_replace(["\0", "\r", "\n"], '', $name);
    $name = preg_replace('~[\x00-\x1F\x7F]+~u', ' ', $name) ?? '';
    $name = trim($name);
    if ($name === '') $name = 'file';
    $name = str_replace(['"', "'"], '', $name);
    $name = rtrim($name, '.');
    if ($name === '') $name = 'file';
    if (mb_strlen($name, 'UTF-8') > 255) $name = mb_substr($name, 0, 255, 'UTF-8');
    return $name;
}

/* login required */
if (!$me || (int)($me['id'] ?? 0) <= 0) {
    redirect('/login.php');
}

/* method */
if (!is_post()) {
    http_response_code(405);
    exit('Method Not Allowed');
}

/* CSRF */
if (!csrf_check((string)($_POST['csrf'] ?? ''))) {
    mf_flash_set('err', mf_msg('Помилка CSRF', 'Ошибка CSRF', 'CSRF error'));
    redirect('/functional/files/');
}

$folderId = (int)($_POST['folder_id'] ?? 0);
if ($folderId < 0) $folderId = 0;
$back = '/functional/files/' . ($folderId > 0 ? ('?folder=' . $folderId) : '');

if (empty($_FILES['files'])) {
    mf_flash_set('err', mf_msg('Файли не вибрані', 'Файлы не выбраны', 'No files selected'));
    redirect($back);
}

/* verify folder belongs */
if ($folderId > 0) {
    $st = $pdo->prepare("SELECT id FROM myfile_folders WHERE id=? AND user_id=? LIMIT 1");
    $st->execute([$folderId, (int)$me['id']]);
    if (!$st->fetchColumn()) {
        mf_flash_set('err', mf_msg('Папку не знайдено', 'Папка не найдена', 'Folder not found'));
        redirect('/functional/files/');
    }
}

/* limits */
$quotaMax   = 1024 * 1024 * 1024;      // 1GB
$maxPerFile = 50 * 1024 * 1024;        // 50MB
$maxFilesPerRequest = 20;

/* quota used */
$st = $pdo->prepare("SELECT COALESCE(SUM(size_bytes),0) FROM myfiles WHERE user_id=?");
$st->execute([(int)$me['id']]);
$usedBytes = (int)$st->fetchColumn();

if ($usedBytes >= $quotaMax) {
    mf_flash_set('err', mf_msg('Квота вичерпана', 'Квота исчерпана', 'Quota exceeded'));
    redirect($back);
}

/* project root + uploads root */
$root = realpath(dirname(__DIR__, 2));
if ($root === false) $root = dirname(__DIR__, 2);

$uploadsRoot = realpath($root . '/upload');
if ($uploadsRoot === false) {
    @mkdir($root . '/upload', 0755, true);
    $uploadsRoot = realpath($root . '/upload');
}
if ($uploadsRoot === false) {
    mf_flash_set('err', mf_msg('Не вдалося підготувати /upload', 'Не удалось подготовить /upload', 'Cannot prepare /upload'));
    redirect($back);
}

/* username */
$username = mf_safe_username((string)($me['username'] ?? 'user'));

/* IMPORTANT: rel_path in DB should be WITHOUT leading "/" */
$baseRel = 'upload/' . $username . '/myfiles';
$baseAbs = $root . '/' . $baseRel;

/* ensure dirs + protect direct access */
if (!is_dir($baseAbs)) {
    @mkdir($baseAbs, 0755, true);
}
if (!is_dir($baseAbs)) {
    mf_flash_set('err', mf_msg('Не вдалося створити папку користувача', 'Не удалось создать папку пользователя', 'Cannot create user folder'));
    redirect($back);
}

/* extra safety: ensure baseAbs inside uploadsRoot */
$uploadsRootNorm = rtrim($uploadsRoot, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
$baseReal = realpath($baseAbs);
if ($baseReal === false) $baseReal = $baseAbs;
$baseNorm = rtrim($baseReal, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
if (strpos($baseNorm, $uploadsRootNorm) !== 0) {
    mf_flash_set('err', mf_msg('Помилка шляху upload', 'Ошибка пути upload', 'Upload path error'));
    redirect($back);
}

/* protect: index + htaccess (для Apache) */
$idx = $baseAbs . '/index.html';
if (!is_file($idx)) @file_put_contents($idx, "<!doctype html><meta charset=\"utf-8\">");

$ht = $baseAbs . '/.htaccess';
if (!is_file($ht)) {
    $rules =
        "Options -Indexes\n" .
        "<IfModule mod_authz_core.c>\n" .
        "  Require all denied\n" .
        "</IfModule>\n" .
        "<IfModule !mod_authz_core.c>\n" .
        "  Deny from all\n" .
        "</IfModule>\n";
    @file_put_contents($ht, $rules);
}

/* folder subdir */
$subRel = $baseRel;
$subAbs = $baseAbs;

if ($folderId > 0) {
    $subRel = $baseRel . '/fld_' . $folderId;
    $subAbs = $root . '/' . $subRel;
    if (!is_dir($subAbs)) @mkdir($subAbs, 0755, true);

    if (!is_dir($subAbs)) {
        mf_flash_set('err', mf_msg('Не вдалося створити папку', 'Не удалось создать папку', 'Cannot create folder'));
        redirect($back);
    }
}

/* normalize $_FILES for multiple */
$files = $_FILES['files'];
$count = is_array($files['name']) ? count($files['name']) : 1;
$count = min($count, $maxFilesPerRequest);

$finfo = null;
if (class_exists('finfo')) {
    try { $finfo = new finfo(FILEINFO_MIME_TYPE); } catch (Throwable $e) { $finfo = null; }
}

$added = 0;
$skipped = 0;
$stoppedByQuota = false;
$moved = [];

$ins = $pdo->prepare("
  INSERT INTO myfiles
    (user_id, folder_id, original_name, stored_name, rel_path, mime, size_bytes, is_public, share_token, created_at)
  VALUES
    (?, ?, ?, ?, ?, ?, ?, 0, ?, NOW())
");

$pdo->beginTransaction();
try {
    for ($i = 0; $i < $count; $i++) {
        $name = is_array($files['name']) ? (string)$files['name'][$i] : (string)$files['name'];
        $tmp  = is_array($files['tmp_name']) ? (string)$files['tmp_name'][$i] : (string)$files['tmp_name'];
        $err  = is_array($files['error']) ? (int)$files['error'][$i] : (int)$files['error'];
        $size = is_array($files['size']) ? (int)$files['size'][$i] : (int)$files['size'];

        if ($name === '' && $tmp === '') { $skipped++; continue; }
        if ($err === UPLOAD_ERR_NO_FILE) { $skipped++; continue; }
        if ($err !== UPLOAD_ERR_OK) { $skipped++; continue; }

        if ($size <= 0) { $skipped++; continue; }
        if ($size > $maxPerFile) { $skipped++; continue; }

        $origName = mf_safe_orig_name($name);

        if (mf_is_dangerous_ext($origName)) { $skipped++; continue; }
        if (!is_uploaded_file($tmp)) { $skipped++; continue; }

        if (($usedBytes + $size) > $quotaMax) {
            $stoppedByQuota = true;
            break;
        }

        $ext = strtolower((string)pathinfo($origName, PATHINFO_EXTENSION));
        $ext = preg_replace('~[^a-z0-9]+~', '', $ext) ?? '';
        if ($ext !== '') $ext = '.' . $ext;

        $stored = date('Ymd_His') . '_' . bin2hex(random_bytes(8)) . $ext;

        $targetAbs = $subAbs . '/' . $stored;
        $targetRel = $subRel . '/' . $stored;

        if (!@move_uploaded_file($tmp, $targetAbs)) { $skipped++; continue; }
        @chmod($targetAbs, 0644);

        $realSize = @filesize($targetAbs);
        if ($realSize === false || $realSize <= 0) {
            @unlink($targetAbs);
            $skipped++;
            continue;
        }

        $mime = 'application/octet-stream';
        if ($finfo) {
            $m = $finfo->file($targetAbs);
            if (is_string($m) && $m !== '') $mime = trim(explode(';', $m, 2)[0]);
        }

        $token = bin2hex(random_bytes(32));

        $ins->execute([
            (int)$me['id'],
            ($folderId > 0 ? $folderId : null),
            $origName,
            $stored,
            $targetRel,
            $mime,
            (int)$realSize,
            $token
        ]);

        $moved[] = $targetAbs;

        $usedBytes += (int)$realSize;
        $added++;
    }

    $pdo->commit();
} catch (Throwable $e) {
    $pdo->rollBack();
    foreach ($moved as $p) {
        if (is_string($p) && $p !== '' && is_file($p)) @unlink($p);
    }
    mf_flash_set('err', mf_msg('Помилка завантаження', 'Ошибка загрузки', 'Upload error'));
    redirect($back);
}

if ($added > 0) {
    $msg = mf_msg('Завантажено', 'Загружено', 'Uploaded') . ': ' . $added;
    if ($stoppedByQuota) $msg .= ' (' . mf_msg('квота обмежила', 'квота ограничила', 'quota limited') . ')';
    mf_flash_set('ok', $msg);
} else {
    mf_flash_set('err', mf_msg('Нічого не завантажено', 'Ничего не загружено', 'Nothing uploaded'));
}

redirect($back);