<?php
namespace App\Controllers\Admin;

use App\Controllers\Controller;

/** 后台基控制器：需要登录 */
class AdminController extends Controller
{
    protected $layout = 'layouts/admin';

    public function __construct()
    {
        admin_required();
    }

    /** 当前路由片段，用于侧栏高亮 */
    protected function seg(): string
    {
        $u = trim(parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH), '/');
        $parts = explode('/', $u);
        return $parts[1] ?? '';
    }

    /**
     * 处理上传文件，返回相对站点根的路径（如 assets/uploads/x.jpg），无上传则返回 null。
     * 安全红线：
     *  1) 仅凭扩展名不可信 —— 光栅图用 getimagesize 校验真实图像内容；
     *  2) 文件体积上限 8MB，防止磁盘打满 / DoS；
     *  3) SVG 可内嵌 <script>/on* 事件 → 存储型 XSS，落盘前强制消毒；
     *  4) 落盘文件名随机化，杜绝路径穿越与覆盖。
     */
    protected function uploadFile(string $key): ?string
    {
        if (empty($_FILES[$key]['tmp_name'])) return null;
        $f = $_FILES[$key];
        if ($f['error'] !== UPLOAD_ERR_OK) return null;
        if (!is_uploaded_file($f['tmp_name'])) return null;
        if (($f['size'] ?? 0) > 8 * 1024 * 1024) return null; // 8MB 上限

        $ext = strtolower(pathinfo($f['name'], PATHINFO_EXTENSION));
        $allowed = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg'];
        if (!in_array($ext, $allowed, true)) return null;

        // 真实类型校验（不信任扩展名与浏览器提交的 MIME）
        $realMime = function_exists('finfo_open')
            ? (finfo_file(($fi = finfo_open(FILEINFO_MIME_TYPE)), $f['tmp_name']) ?: '') : '';
        if (isset($fi) && $fi) { finfo_close($fi); }

        $isSvg = ($ext === 'svg');
        if (!$isSvg) {
            // 光栅图：必须能被 GD 识别为真实图像，且 MIME 属于图片类
            $info = @getimagesize($f['tmp_name']);
            $okMimes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
            if ($info === false) return null;
            if ($realMime && !in_array($realMime, $okMimes, true)) return null;
        } else {
            // SVG：类型须为 svg/xml/text，随后消毒内容
            $svgMimes = ['image/svg+xml', 'text/plain', 'text/xml', 'application/xml'];
            if ($realMime && !in_array($realMime, $svgMimes, true)) return null;
        }

        $dir = BASE_PATH . '/public/assets/uploads';
        if (!is_dir($dir)) mkdir($dir, 0755, true);
        $name = uniqid('u_') . '.' . $ext;
        $dest = $dir . '/' . $name;

        if ($isSvg) {
            // 读取 → 消毒 → 写入（不使用 move_uploaded_file，因内容已被改写）
            $raw = @file_get_contents($f['tmp_name']);
            if ($raw === false) return null;
            $clean = $this->sanitizeSvg($raw);
            if ($clean === '' || @file_put_contents($dest, $clean) === false) return null;
        } else {
            if (!move_uploaded_file($f['tmp_name'], $dest)) return null;
        }
        return 'assets/uploads/' . $name;
    }

    /**
     * 处理多文件上传（name="gallery[]"），返回相对站点根路径数组。
     * 逐张复用 uploadFile 的校验逻辑（真实图像/体积上限/类型白名单/SVG 消毒），
     * 任一文件失败不影响其余文件。
     */
    protected function uploadFiles(string $key): array
    {
        if (empty($_FILES[$key]['tmp_name']) || !is_array($_FILES[$key]['tmp_name'])) return [];
        $names  = $_FILES[$key]['name']  ?? [];
        $errors = $_FILES[$key]['error'] ?? [];
        $sizes  = $_FILES[$key]['size']  ?? [];
        $out = [];
        foreach ($_FILES[$key]['tmp_name'] as $i => $tmp) {
            if (empty($tmp)) continue;
            // 桥接到单文件校验逻辑
            $_FILES['_multi_tmp'] = [
                'name'     => $names[$i]  ?? 'x.bin',
                'type'     => '',
                'tmp_name' => $tmp,
                'error'    => $errors[$i] ?? UPLOAD_ERR_OK,
                'size'     => $sizes[$i]  ?? 0,
            ];
            $rel = $this->uploadFile('_multi_tmp');
            unset($_FILES['_multi_tmp']);
            if ($rel) $out[] = $rel;
        }
        return $out;
    }

    /** 消毒 SVG：移除脚本、事件处理器与危险协议，阻断存储型 XSS */
    protected function sanitizeSvg(string $svg): string
    {
        // 去除 <script>...</script>
        $svg = preg_replace('#<script[^>]*>.*?</script>#is', '', $svg);
        // 去除 <foreignObject>（可嵌 HTML/脚本）
        $svg = preg_replace('#<foreignObject[^>]*>.*?</foreignObject>#is', '', $svg);
        // 去除内联事件处理器 on*="..."
        $svg = preg_replace('#\son\w+\s*=\s*("[^"]*"|\'[^\']*\'|[^\s>]+)#i', '', $svg);
        // 去除 javascript: / data:text 等危险协议引用
        $svg = preg_replace('#(href|xlink:href)\s*=\s*("|\')?\s*javascript:[^"\'>]*#i', '', $svg);
        // 去除 <use> 外部引用与 <a> 标签，避免脚本跳转
        $svg = preg_replace('#<a[^>]*>|</a>#i', '', $svg);
        return trim((string)$svg);
    }
}
