<?php
/**
 * 文件站 - 上传
 */
require_once __DIR__ . '/includes/config.php';
require_once __DIR__ . '/includes/header.php';

$pdo = portal_pdo();

if (!is_logged_in()) redirect_to_login();
if (!has_perm('upload_file')) {
    http_response_code(403);
    echo '<div class="fs-wrap fs-denied">⚠️ 您没有上传权限</div>';
    require __DIR__ . '/includes/footer.php';
    exit;
}

// ========== 处理上传 ==========
$msg = '';
$msgType = ''; // success / error

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $category  = (string)($_POST['category'] ?? 'default');
    $categoryNew = trim((string)($_POST['category_new'] ?? ''));
    // 如果填了新分类，用它 + 自动加入 file_categories
    if ($categoryNew !== '') {
        $category = $categoryNew;
        try {
            $pdo->prepare("INSERT IGNORE INTO file_categories (name, sort) VALUES (?, 0)")
                ->execute([$categoryNew]);
        } catch (Exception $e) {}
    }
    $visibility = (string)($_POST['visibility'] ?? 'member');

    if (!in_array($visibility, ['public', 'member', 'admin'], true)) $visibility = 'member';

    $upDir = $GLOBALS['FS_UPLOAD_DIR'];
    if (!is_dir($upDir) || !is_writable($upDir)) {
        $msg = '上传目录不存在或不可写，请联系管理员';
        $msgType = 'error';
    } else {
        $files = $_FILES['files'] ?? [];
        $count = 0; $failures = [];

        if (!is_array($files['name'] ?? null)) {
            $files = [
                'name'     => [$files['name'] ?? ''],
                'type'     => [$files['type'] ?? ''],
                'tmp_name' => [$files['tmp_name'] ?? ''],
                'error'    => [$files['error'] ?? UPLOAD_ERR_NO_FILE],
                'size'     => [$files['size'] ?? 0],
            ];
        }

        $names     = $files['name']     ?? [];
        $types     = $files['type']     ?? [];
        $tmpNames  = $files['tmp_name'] ?? [];
        $errors    = $files['error']    ?? [];
        $sizes     = $files['size']     ?? [];

        $allowed = $GLOBALS['FS_ALLOWED_MIME'];
        $maxSize = $GLOBALS['FS_MAX_SIZE'];
        $userId  = (int)($_SESSION['user']['id'] ?? 0);

        for ($i = 0; $i < count($names); $i++) {
            $name    = (string)($names[$i] ?? '');
            $mime    = (string)($types[$i] ?? '');
            $tmp     = (string)($tmpNames[$i] ?? '');
            $err     = (int)($errors[$i] ?? UPLOAD_ERR_NO_FILE);
            $size    = (int)($sizes[$i] ?? 0);

            if ($err === UPLOAD_ERR_NO_FILE || $name === '') continue;

            if ($err !== UPLOAD_ERR_OK) {
                $failures[] = $name . ' (上传错误码: ' . $err . ')';
                continue;
            }

            // 大小检查
            if ($size <= 0 || $size > $maxSize) {
                $failures[] = $name . ' (超过大小限制 ' . round($maxSize/1024/1024) . 'MB)';
                continue;
            }

            // 文件名安全
            $name = safe_filename($name);
            if ($name === '') {
                $failures[] = '(非法文件名)';
                continue;
            }

            // MIME 白名单：finfo（最准）→ mime_content_type（备选）→ 扩展名兜底
            // 三种方法都拿不到 MIME 时拒绝上传，保守安全
            $realMime = '';
            if (function_exists('finfo_open')) {
                $finfo = finfo_open(FILEINFO_MIME_TYPE);
                if ($finfo) {
                    $realMime = finfo_file($finfo, $tmp);
                    finfo_close($finfo);
                }
            }
            if ($realMime === '' && function_exists('mime_content_type')) {
                $realMime = mime_content_type($tmp);
            }
            if ($realMime === '' || !in_array($realMime, $allowed, true)) {
                // 扩展名兜底映射（覆盖常见类型）
                $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
                $extMap = [
                    'pdf'  => 'application/pdf',
                    'doc'  => 'application/msword',
                    'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
                    'xls'  => 'application/vnd.ms-excel',
                    'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
                    'ppt'  => 'application/vnd.ms-powerpoint',
                    'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
                    'txt'  => 'text/plain',
                    'csv'  => 'text/csv',
                    'json' => 'application/json',
                    'xml'  => 'application/xml',
                    'jpg'  => 'image/jpeg',
                    'jpeg' => 'image/jpeg',
                    'png'  => 'image/png',
                    'gif'  => 'image/gif',
                    'webp' => 'image/webp',
                    'bmp'  => 'image/bmp',
                    'svg'  => 'image/svg+xml',
                    'mp4'  => 'video/mp4',
                    'webm' => 'video/webm',
                    'mov'  => 'video/quicktime',
                    'mp3'  => 'audio/mpeg',
                    'wav'  => 'audio/wav',
                    'flac' => 'audio/flac',
                    'zip'  => 'application/zip',
                    'rar'  => 'application/x-rar-compressed',
                    '7z'   => 'application/x-7z-compressed',
                    'gz'   => 'application/gzip',
                ];
                $realMime = $extMap[$ext] ?? '';
            }

            if ($realMime === '' || !in_array($realMime, $allowed, true)) {
                $failures[] = $name . ' (类型不允许)';
                continue;
            }

            // 扩展名 + 文件头双重校验：防止伪装成图片的 PHP 脚本
            $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
            $header = file_get_contents($tmp, false, null, 0, 8);
            $isPhp = preg_match('/^<\?(php|=|\s)/i', $header);
            if ($isPhp || in_array($ext, ['php','phtml','phar','htaccess','user.ini','jsp','asp','aspx','cgi','sh'])
                || str_contains($name, '.php.') || str_ends_with($name, '.php')) {
                $failures[] = $name . ' (文件类型被禁止上传)';
                continue;
            }

            // 存储文件名（防重名 + 隐藏原始文件名）
            $stored = date('Ymd') . '_' . bin2hex(random_bytes(4)) . '.' . $ext;
            $target = $upDir . $stored;

            if (!move_uploaded_file($tmp, $target)) {
                $failures[] = $name . ' (保存失败)';
                continue;
            }
            @chmod($target, 0644);

            // 写数据库
            $stmt = $pdo->prepare("INSERT INTO files (filename, stored_name, mime, size, category, visibility, uploader_id)
                                   VALUES (?, ?, ?, ?, ?, ?, ?)");
            $stmt->execute([$name, $stored, $realMime, $size, $category, $visibility, $userId]);
            $count++;

            write_log('文件上传', ['filename' => $name, 'size' => $size, 'category' => $category]);
        }

        if ($count > 0) {
            $msg = "✅ 成功上传 $count 个文件";
            if (!empty($failures)) $msg .= '，失败：' . implode('；', $failures);
            $msgType = 'success';
        } elseif (!empty($failures)) {
            $msg = '❌ 全部失败：' . implode('；', $failures);
            $msgType = 'error';
        } else {
            $msg = '⚠️ 请先选择文件';
            $msgType = 'error';
        }
    }
}

// 分类列表
$catStmt = $pdo->query("SELECT name FROM file_categories ORDER BY sort ASC");
$catList = $catStmt->fetchAll(PDO::FETCH_COLUMN);
?>
<div class="fs-wrap fs-wrap-upload">

  <div class="fs-upload-card">
    <h1>⬆️ 上传文件</h1>
    <p class="fs-upload-hint">
      允许类型：PDF / Word / Excel / PPT / 图片 / 视频 / 音频 / 压缩包<br>
      单文件最大 <?= round($GLOBALS['FS_MAX_SIZE'] / 1024 / 1024) ?> MB
    </p>

    <?php if ($msg): ?>
      <div class="fs-alert fs-alert-<?= $msgType ?>"><?= e($msg) ?></div>
    <?php endif; ?>

    <form method="post" enctype="multipart/form-data" id="fsUploadForm">
      <div class="fs-upload-grid">
        <div class="fs-field">
          <label>📁 分类 <button type="button" id="btnNewCat" style="font-size:12px;color:var(--primary);background:none;border:0;cursor:pointer;padding:0;margin-left:6px">+ 新增</button></label>
          <select name="category" id="fsCatSelect">
            <?php foreach ($catList as $c): ?>
              <option value="<?= e($c) ?>" <?= $c === 'default' ? 'selected' : '' ?>><?= e($c) ?></option>
            <?php endforeach; ?>
          </select>
          <input type="text" id="fsCatNew" placeholder="新分类名..." style="display:none;margin-top:6px;padding:8px;border:1px solid var(--border);border-radius:8px;font-size:13px;width:100%">
          <input type="hidden" name="category_new" id="fsCatNewHidden">
        </div>
        <div class="fs-field">
          <label>👁️ 可见性</label>
          <select name="visibility">
            <option value="member">👥 所有登录用户</option>
            <?php if (is_admin()): ?>
              <option value="admin">🔒 仅管理员组</option>
            <?php endif; ?>
          </select>
        </div>
      </div>

      <div class="fs-dropzone" id="fsDropzone">
        <div class="fs-dropzone-icon">📦</div>
        <div><b>点击选择文件</b> 或拖拽到这里</div>
        <input type="file" name="files[]" id="fsFileInput" multiple hidden>
        <div class="fs-dropzone-hint">支持批量上传，一次最多 20 个文件</div>
      </div>

      <div id="fsFileList" class="fs-file-list"></div>

      <button type="submit" class="fs-btn-primary fs-upload-submit">🚀 开始上传</button>
    </form>
  </div>
</div>

<script>
(function(){
  var dz = document.getElementById('fsDropzone');
  var fi = document.getElementById('fsFileInput');
  var fl = document.getElementById('fsFileList');
  if (!dz || !fi) return;

  dz.addEventListener('click', function(){ fi.click(); });
  ['dragenter','dragover'].forEach(function(e){
    dz.addEventListener(e, function(ev){ ev.preventDefault(); dz.classList.add('hover'); });
  });
  ['dragleave','drop'].forEach(function(e){
    dz.addEventListener(e, function(ev){ ev.preventDefault(); dz.classList.remove('hover'); });
  });
  dz.addEventListener('drop', function(ev){
    fi.files = ev.dataTransfer.files;
    updateList();
  });
  fi.addEventListener('change', updateList);

  function updateList(){
    fl.innerHTML = '';
    var files = fi.files;
    if (!files || files.length === 0) return;
    for (var i = 0; i < files.length; i++) {
      var f = files[i];
      var size = f.size >= 1024*1024 ? (f.size/1024/1024).toFixed(2)+' MB' : (f.size/1024).toFixed(1)+' KB';
      var div = document.createElement('div');
      div.className = 'fs-file-item';
      div.innerHTML = '<span class="fs-fname">' + f.name + '</span><span class="fs-fsize">' + size + '</span>';
      fl.appendChild(div);
    }
  }
})();
</script>

<script>
// 新增分类交互
(function(){
  var btn = document.getElementById('btnNewCat');
  if (!btn) return;
  var sel = document.getElementById('fsCatSelect');
  var inp = document.getElementById('fsCatNew');
  var hid = document.getElementById('fsCatNewHidden');
  btn.addEventListener('click', function(){
    var show = inp.style.display === 'none';
    inp.style.display = show ? 'block' : 'none';
    btn.textContent = show ? '✕ 取消' : '+ 新增';
    if (show) { inp.focus(); } else { inp.value = ''; hid.value = ''; sel.disabled = false; }
  });
  inp.addEventListener('input', function(){
    hid.value = inp.value.trim();
    sel.disabled = inp.value.trim() !== '';
  });
})();
</script>

<?php require __DIR__ . '/includes/footer.php'; ?>
