const express = require('express');
const multer = require('multer');
const path = require('path');
const fs = require('fs');
const crypto = require('crypto');
const sharp = require('sharp');
const AdmZip = require('adm-zip');

const User = require('../models/User');
const Asset = require('../models/Asset');
const Scene = require('../models/Scene');
const Hotspot = require('../models/Hotspot'); // Giả định bạn đã tạo model mới
const Setting = require('../models/Setting');

const { protect, optionalAuth } = require('../middlewares/authMiddleware');
const { verifyReferer, setNoCacheHeaders } = require('../middlewares/securityMiddleware');
const { checkQuota, ROLE_QUOTAS } = require('../middlewares/quotaMiddleware');
const { resizeTo8K } = require('../utils/imageHelper');
const { getGPSCoordinates, injectGPSCoordinates } = require('../utils/exifHelper');
const { imageQueue } = require('./imageQueue');

const router = express.Router();

// Ensure upload directories exist
const uploadDir = path.join(__dirname, '../uploads');
const tempDir = path.join(uploadDir, 'temp');
if (!fs.existsSync(uploadDir)) fs.mkdirSync(uploadDir, { recursive: true });
if (!fs.existsSync(tempDir)) fs.mkdirSync(tempDir, { recursive: true });

// Configure Multer for temp uploads
const storage = multer.diskStorage({
    destination: (req, file, cb) => {
        cb(null, tempDir);
    },
    filename: (req, file, cb) => {
        cb(null, `${Date.now()}_${crypto.randomBytes(4).toString('hex')}${path.extname(file.originalname)}`);
    }
});

const upload = multer({
    storage: storage,
    fileFilter: (req, file, cb) => {
        // Chỉ chấp nhận các định dạng ảnh phổ biến đã được xử lý (Stitched)
        const filetypes = /jpeg|jpg|png/;
        const extname = filetypes.test(path.extname(file.originalname).toLowerCase());
        const mimetype = filetypes.test(file.mimetype) || 
                         file.mimetype === 'application/octet-stream'; // Đôi khi trình duyệt gửi JPG dưới dạng octet-stream

        if (mimetype && extname) {
            cb(null, true);
        } else {
            cb(new Error('Chỉ chấp nhận các định dạng ảnh JPEG, PNG, DNG hoặc INSP!'), false);
        }
    }
});

/**
 * @route   POST /api/admin/backup
 * @desc    Tạo bản sao lưu toàn bộ hệ thống (DB + Uploads)
 * @access  Private (Admin)
 */
router.post('/admin/backup', protect, async (req, res) => {
    if (req.user.role !== 'admin' && req.user.role !== 'Chủ sở hữu') {
        return res.status(403).json({ message: 'Forbidden' });
    }
    try {
        const zip = new AdmZip();
        // 1. Export Database
        const dbData = {
            users: await User.find().lean(),
            assets: await Asset.find().lean(),
            scenes: await Scene.find().lean(),
            hotspots: await Hotspot.find().lean(),
            settings: await Setting.find().lean()
        };
        zip.addFile("database.json", Buffer.from(JSON.stringify(dbData, null, 2), "utf8"));

        // 2. Add Uploads folder
        if (fs.existsSync(uploadDir)) {
            zip.addLocalFolder(uploadDir, "uploads");
        }

        const buffer = zip.toBuffer();
        res.set({
            'Content-Type': 'application/zip',
            'Content-Disposition': 'attachment; filename="backup_3dtour.zip"'
        });
        res.send(buffer);
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
});

/**
 * @route   POST /api/admin/restore
 * @desc    Khôi phục hệ thống từ file backup.zip
 * @access  Private (Admin)
 */
router.post('/admin/restore', protect, upload.single('backupFile'), async (req, res) => {
    if (req.user.role !== 'admin' && req.user.role !== 'Chủ sở hữu') {
        if (req.file) fs.unlinkSync(req.file.path);
        return res.status(403).json({ message: 'Forbidden' });
    }
    if (!req.file) return res.status(400).json({ message: 'Vui lòng upload file backup.zip' });

    try {
        const zip = new AdmZip(req.file.path);
        const dbEntry = zip.getEntry("database.json");
        if (!dbEntry) throw new Error("File backup không hợp lệ (thiếu database.json)");

        const dbData = JSON.parse(dbEntry.getData().toString('utf8'));

        // Khôi phục Database (Xóa cũ - Ghi mới)
        await Promise.all([
            User.deleteMany({}), Asset.deleteMany({}), Scene.deleteMany({}),
            Hotspot.deleteMany({}), Setting.deleteMany({})
        ]);
        await Promise.all([
            User.insertMany(dbData.users), Asset.insertMany(dbData.assets),
            Scene.insertMany(dbData.scenes), Hotspot.insertMany(dbData.hotspots),
            Setting.insertMany(dbData.settings)
        ]);

        // Khôi phục Files
        zip.extractEntryTo("uploads/", uploadDir, false, true);
        fs.unlinkSync(req.file.path);
        res.json({ message: 'Khôi phục dữ liệu thành công' });
    } catch (error) {
        if (req.file && fs.existsSync(req.file.path)) fs.unlinkSync(req.file.path);
        res.status(500).json({ message: error.message });
    }
});

/**
 * Wrapper for Multer middleware to catch "Request aborted" and other upload errors gracefully.
 */
const uploadSinglePanorama = (req, res, next) => {
    const multerUpload = upload.single('panorama');
    multerUpload(req, res, (err) => {
        if (err) {
            // Bắt lỗi khi client ngắt kết nối đột ngột
            if (err.message === 'Request aborted') {
                console.warn(`[Multer Warning]: Upload aborted by client at ${req.method} ${req.originalUrl}`);
                return res.status(499).json({ message: 'Client aborted the request' });
            }
            if (err instanceof multer.MulterError) {
                return res.status(400).json({ message: `Multer error: ${err.message}` });
            }
            return res.status(400).json({ message: err.message });
        }
        next();
    });
};

/**
 * @route   POST /api/scenes
 * @desc    Create a new 3D scene (with 360 photo, 8K resize, EXIF injection)
 * @access  Private (Registered Users)
 */
router.post('/scenes', protect, uploadSinglePanorama, checkQuota, async (req, res) => {
    try {
        const { title, lat, lng, privacy, sharedWithUsers } = req.body;
        
        if (!req.file) {
            return res.status(400).json({ message: 'Please upload a panorama image' });
        }

        // Đảm bảo ép kiểu Number tuyệt đối trước khi lưu DB
        const latitude = Number(lat) || 0;
        const longitude = Number(lng) || 0;

        if (isNaN(latitude) || isNaN(longitude)) {
            // Cleanup uploaded file on validation error
            fs.unlinkSync(req.file.path);
            return res.status(400).json({ message: 'Valid lat and lng are required' });
        }

        const tempFilePath = req.file.path;
        const processedFileName = `processed_${req.file.filename}.jpg`;
        const processedFilePath = path.join(uploadDir, processedFileName);
        
        // Lấy tọa độ GPS gốc từ metadata
        const originalGPS = await getGPSCoordinates(tempFilePath);
        const ext = path.extname(req.file.originalname).toLowerCase();

        // 5. Save Asset to DB
        const asset = new Asset({
            filePath: tempFilePath, // Tạm thời dùng file gốc cho đến khi worker xử lý xong
            fileSize: req.file.size,
            uploadedBy: req.user._id,
            coordinates: originalGPS ? { lat: originalGPS.lat, lng: originalGPS.lng } : { lat: latitude, lng: longitude }
        });
        await asset.save();

        // 6. Handle share token if privacy is 'shared'
        let shareToken = undefined;
        if (privacy === 'shared') {
            shareToken = crypto.randomBytes(24).toString('hex');
        }

        // Handle sharedWith User IDs
        let parsedSharedWith = [];
        if (sharedWithUsers) {
            try {
                parsedSharedWith = JSON.parse(sharedWithUsers);
            } catch (e) {
                // Ignore parse error
            }
        }

        // 7. Save Scene to DB
        const scene = new Scene({
            name: title,
            assetId: asset._id,
            scene_url: tempFilePath, // Tạm thời
            gps: {
                lat: latitude,
                lng: longitude
            },
            createdBy: req.user._id,
            privacy: privacy || 'private',
            shareToken,
            sharedWith: parsedSharedWith,
            status: 'processing' // Đánh dấu đang xử lý
        });
        await scene.save();

        // Đẩy tác vụ xử lý ảnh (Stitch + Resize) vào hàng đợi BullMQ
        // Loại bỏ needsStitch và rotation vì người dùng đã stitch ảnh thủ công
        await imageQueue.add('process-panorama', {
            tempFilePath,
            processedFilePath,
            latitude,
            longitude,
            assetId: asset._id,
            sceneId: scene._id
        });

        res.status(201).json({
            message: 'Scene đã được tạo! Ảnh đang được xử lý 8K ngầm...',
            scene
        });

    } catch (error) {
        // Dọn dẹp các file rác nếu quá trình xử lý thất bại
        if (req.file && fs.existsSync(req.file.path)) {
            try { fs.unlinkSync(req.file.path); } catch (e) {}
        }
        res.status(500).json({ message: "Xử lý ảnh 360 thất bại. Vui lòng đảm bảo bạn upload ảnh Panorama đã được stitch (JPEG). Chi tiết: " + error.message });
    }
});

/**
 * @route   GET /api/users/search
 * @desc    Tìm kiếm người dùng theo username hoặc email để chia sẻ
 * @access  Private
 */
router.get('/users/search', protect, async (req, res) => {
    const query = req.query.q;
    if (!query || query.length < 2) return res.json([]);

    try {
        const users = await User.find({
            $and: [
                { _id: { $ne: req.user._id } }, // Không tìm chính mình
                {
                    $or: [
                        { username: { $regex: query, $options: 'i' } },
                        { email: { $regex: query, $options: 'i' } }
                    ]
                }
            ]
        }).select('username email').limit(10);
        res.json(users);
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
});

/**
 * @route   GET /api/scenes
 * @desc    Get all accessible scenes for the map (respecting privacy rules)
 * @access  Public / Private
 */
router.get('/scenes', optionalAuth, async (req, res) => {
    try {
        console.log(`[Data Load] Bắt đầu truy vấn scenes cho: ${req.user ? req.user._id : 'Khách'}`);
        let query = {};

        if (req.user) {
            // Logged in: See public, member-only, owned, or shared-with-me scenes
            query = {
                $or: [
                    { privacy: 'public' },
                    { createdBy: req.user._id },
                    { sharedWith: req.user._id },
                    { sharedEmails: req.user.email }
                ]
            };
        } else {
            // Guests: See only public scenes
            query = { privacy: 'public' };
        }

        const scenes = await Scene.find(query)
            .populate('createdBy', 'username')
            .lean();

        console.log(`[Data Load] Đã tìm thấy ${scenes.length} scenes. Gửi phản hồi về Frontend...`);
        res.json(scenes);
    } catch (error) {
        console.error(`[Data Load Error]: ${error.stack}`);
        res.status(500).json({ message: error.message });
    }
});

/**
 * @route   GET /api/share/:sceneId
 * @desc    Endpoint tạo trang trung gian hỗ trợ hiển thị ảnh thumbnail trên Facebook/Zalo (Open Graph)
 */
router.get('/share/:sceneId', optionalAuth, async (req, res) => {
    try {
        const scene = await Scene.findById(req.params.sceneId).populate('assetId');
        if (!scene) return res.status(404).send('Không tìm thấy Scene');

        // Kiểm tra quyền truy cập (sử dụng logic đồng bộ với các route khác)
        const isTokenValid = scene.shareToken && (!scene.shareTokenExpires || new Date() < scene.shareTokenExpires);
        const userEmail = req.user ? req.user.email : null;
        const hasAccess = 
            scene.privacy === 'public' ||
            (scene.privacy === 'member' && req.user && (scene.sharedWith.includes(req.user._id) || (userEmail && scene.sharedEmails.includes(userEmail)))) ||
            (req.user && scene.createdBy.toString() === req.user._id.toString()) ||
            (scene.privacy === 'shared' && req.query.token === scene.shareToken && isTokenValid);

        if (!hasAccess) return res.status(403).send('Bạn không có quyền xem liên kết này hoặc liên kết đã hết hạn');

        // Xây dựng các thông số Open Graph
        const protocol = req.headers['x-forwarded-proto'] || req.protocol;
        const host = req.get('host');
        const siteUrl = `${protocol}://${host}`;
        const assetId = scene.assetId?._id || scene.assetId;
        // Thêm tham số watermark=true để ép hệ thống vẽ thêm icon cho mạng xã hội
        const thumbUrl = `${siteUrl}/api/assets/view/${assetId}?watermark=true${req.query.token ? '&token=' + req.query.token : ''}`;

        // Trả về HTML chứa Meta Tags và Script chuyển hướng
        const html = `
<!DOCTYPE html>
<html lang="vi">
<head>
    <meta charset="UTF-8">
    <title>${scene.name}</title>
    <meta property="og:title" content="${scene.name}" />
    <meta property="og:description" content="${scene.description || 'Khám phá tour 3D thực tế ảo sinh động'}" />
    <meta property="og:image" content="${thumbUrl}" />
    <meta property="og:image:secure_url" content="${thumbUrl}" />
    <meta property="og:image:width" content="1200" />
    <meta property="og:image:height" content="1200" />
    <meta property="og:image:type" content="image/jpeg" />
    <meta property="og:url" content="${siteUrl}${req.originalUrl}" />
    <meta property="og:type" content="website" />
    <meta property="fb:app_id" content="your_facebook_app_id_if_any" />
    
    <meta name="twitter:card" content="summary_large_image" />
    <meta name="twitter:image" content="${thumbUrl}" />

    <script>
        // Tự động chuyển hướng người dùng về ứng dụng chính (SPA) kèm tham số
        const urlParams = new URLSearchParams(window.location.search);
        const token = urlParams.get('token');
        window.location.href = "/?sceneId=${scene._id}" + (token ? "&token=" + token : "");
    </script>
</head>
<body style="background:#000; color:#fff; display:flex; align-items:center; justify-content:center; height:100vh; font-family:sans-serif;">
    <p>Đang mở tour 3D của bạn...</p>
</body>
</html>`;
        res.send(html);
    } catch (error) {
        res.status(500).send('Internal Server Error');
    }
});

/**
 * @route   GET /api/scenes/:id
 * @desc    Get single scene detail (respecting privacy rules)
 * @access  Public / Private
 */
router.get('/scenes/:id', optionalAuth, async (req, res) => {
    try {
        const scene = await Scene.findById(req.params.id)
            .populate('createdBy', 'username')
            .populate('assetId');

        if (!scene) {
            return res.status(404).json({ message: 'Scene not found' });
        }

        // Kiểm tra Token hết hạn
        const isTokenValid = scene.shareToken && 
                            (!scene.shareTokenExpires || new Date() < scene.shareTokenExpires);

        const userEmail = req.user ? req.user.email : null;
        const hasAccess = 
            scene.privacy === 'public' ||
            (scene.privacy === 'member' && req.user && (scene.sharedWith.includes(req.user._id) || (userEmail && scene.sharedEmails.includes(userEmail)))) ||
            (req.user && scene.createdBy._id.toString() === req.user._id.toString()) ||
            (req.user && scene.sharedWith.includes(req.user._id)) ||
            (scene.privacy === 'shared' && req.query.token === scene.shareToken && isTokenValid);

        if (!hasAccess) {
            return res.status(403).json({ message: 'Access denied to this scene' });
        }

        res.json(scene);
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
});

/**
 * @route   GET /api/hotspots/:scene_id
 * @desc    Lấy toàn bộ danh sách hotspot của scene hiện tại
 */
router.get('/hotspots/:scene_id', async (req, res) => {
    try {
        const hotspots = await Hotspot.find({ parent_scene_id: req.params.scene_id })
            .populate({
                path: 'target_scene_id',
                select: 'name title assetId privacy shareToken',
                populate: { path: 'assetId', select: '_id' }
            })
            .lean();
        
        res.json(hotspots);
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
});

/**
 * @route   POST /api/hotspots/create
 * @desc    Tạo mới Hotspot + Tự động tạo liên kết ngược
 */
router.post('/hotspots/create', protect, async (req, res) => {
    try {
        const { parent_scene_id, target_scene_id, title, description, coordinates } = req.body;
        
        const parentScene = await Scene.findById(parent_scene_id);
        if (!parentScene || parentScene.createdBy.toString() !== req.user._id.toString()) {
            return res.status(403).json({ message: 'Không có quyền tạo hotspot cho scene này' });
        }

        const hotspot = new Hotspot({
            parent_scene_id,
            target_scene_id,
            title,
            description,
            coordinates: {
                yaw: Number(coordinates?.yaw) || 0,
                pitch: Number(coordinates?.pitch) || 0
            },
            is_auto_return: false
        });
        await hotspot.save();

        if (target_scene_id) {
            const targetScene = await Scene.findById(target_scene_id);
            if (targetScene) {
                const reverseYaw = coordinates.yaw > 0 ? coordinates.yaw - 180 : coordinates.yaw + 180;
                const reverseHotspot = new Hotspot({
                    parent_scene_id: target_scene_id,
                    target_scene_id: parent_scene_id,
                    title: `Quay lại ${parentScene.name}`,
                    coordinates: { yaw: reverseYaw, pitch: 0 },
                    is_auto_return: true
                });
                await reverseHotspot.save();
            }
        }

        res.status(201).json(hotspot);
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
});

/**
 * @route   PUT /api/hotspots/update/:id
 * @desc    Cập nhật hotspot
 */
router.put('/hotspots/update/:id', protect, async (req, res) => {
    try {
        const { title, description, coordinates } = req.body;
        const hotspot = await Hotspot.findById(req.params.id);
        if (!hotspot) return res.status(404).json({ message: 'Hotspot không tồn tại' });

        const parentScene = await Scene.findById(hotspot.parent_scene_id);
        if (parentScene.createdBy.toString() !== req.user._id.toString()) {
            return res.status(403).json({ message: 'Không có quyền cập nhật' });
        }

        if (title) hotspot.title = title;
        if (description) hotspot.description = description;
        if (coordinates) hotspot.coordinates = coordinates;

        await hotspot.save();
        res.json(hotspot);
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
});

/**
 * @route   DELETE /api/hotspots/delete/:id
 * @desc    Xóa hotspot + Xóa luôn hotspot ngược tương ứng
 */
router.delete('/hotspots/delete/:id', protect, async (req, res) => {
    try {
        const hotspot = await Hotspot.findById(req.params.id);
        if (!hotspot) return res.status(404).json({ message: 'Hotspot không tồn tại' });

        const parentScene = await Scene.findById(hotspot.parent_scene_id);
        if (parentScene.createdBy.toString() !== req.user._id.toString()) {
            return res.status(403).json({ message: 'Không có quyền xóa' });
        }

        if (hotspot.target_scene_id) {
            await Hotspot.deleteOne({
                parent_scene_id: hotspot.target_scene_id,
                target_scene_id: hotspot.parent_scene_id,
                is_auto_return: true
            });
        }

        await Hotspot.findByIdAndDelete(req.params.id);
        res.json({ message: 'Hotspot deleted successfully' });
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
});

/**
 * @route   GET /api/assets/view/:assetId
 * @desc    Securely stream panorama images (prevents direct link / unauthorized downloads)
 * @access  Public / Private + Referer Verification + Token validation
 */
router.get('/assets/view/:assetId', verifyReferer, optionalAuth, async (req, res) => {
    try {
        const asset = await Asset.findById(req.params.assetId);
        if (!asset) {
            return res.status(404).json({ message: 'Asset not found' });
        }

        // Find associated scene to verify privacy
        const scene = await Scene.findOne({ assetId: asset._id });
        if (!scene) {
            // Orphaned asset, only owner can view
            if (!req.user || req.user._id.toString() !== asset.uploadedBy.toString()) {
                return res.status(403).json({ message: 'Access denied' });
            }
        } else {
            // Kiểm tra Token hết hạn
            const isTokenValid = scene.shareToken && 
                                (!scene.shareTokenExpires || new Date() < scene.shareTokenExpires);

            const userEmail = req.user ? req.user.email : null;
            const hasAccess = 
                scene.privacy === 'public' ||
                (scene.privacy === 'member' && req.user && (scene.sharedWith.includes(req.user._id) || (userEmail && scene.sharedEmails.includes(userEmail)))) ||
                (req.user && scene.createdBy.toString() === req.user._id.toString()) ||
                (req.user && scene.sharedWith.includes(req.user._id)) ||
                (scene.privacy === 'shared' && req.query.token === scene.shareToken && isTokenValid);

            if (!hasAccess) {
                return res.status(403).json({ message: 'Access denied: You do not have permission to view this asset' });
            }
        }

        if (!fs.existsSync(asset.filePath)) {
            return res.status(404).json({ message: 'Physical file not found on disk' });
        }

        const resolvedPath = path.resolve(asset.filePath);
        
        // Kiểm tra xem có cần chèn watermark 360° hay không (dành cho Social Bot hoặc yêu cầu thủ công)
        const userAgent = req.headers['user-agent'] || '';
        const isSocialBot = /facebookexternalhit|Facebot|ZaloBot|Twitterbot|Slackbot|LinkedInBot|Embedly/i.test(userAgent);
        const wantWatermark = isSocialBot || req.query.watermark === 'true';

        if (wantWatermark) {
            const iconPath = path.join(__dirname, '../assets/static/360-badge.png');
            if (fs.existsSync(iconPath)) {
                try {
                    // Resize ảnh về kích thước vuông (1200x1200) trước khi chèn icon
                    // Việc này giúp icon 360 hiển thị rõ ràng và tỷ lệ hợp lý hơn
                    const imageBuffer = await sharp(resolvedPath)
                        .resize(1200, 1200, {
                            fit: 'cover'
                        })
                        .composite([{
                            input: iconPath,
                            gravity: 'center'
                        }])
                        .jpeg({ quality: 90 })
                        .toBuffer();

                    res.set({
                        'Content-Type': 'image/jpeg',
                        'Cache-Control': 'public, max-age=2592000',
                        'Content-Disposition': 'inline; filename="preview_360.jpg"'
                    });
                    return res.send(imageBuffer);
                } catch (sharpError) {
                    console.error("[Sharp Error]:", sharpError.message);
                    // Nếu lỗi sharp, fallback xuống sendFile bình thường ở dưới
                }
            }
        }

        // Sử dụng res.sendFile để tối ưu hóa việc truyền tải file lớn và hỗ trợ Caching (ETag)
        res.sendFile(resolvedPath, {
            maxAge: 2592000000, // 30 ngày (tính bằng ms)
            lastModified: true,
            headers: {
                'Content-Type': 'image/jpeg',
                'Content-Disposition': 'inline; filename="panorama.jpg"',
                'Cache-Control': 'public, max-age=2592000, immutable' // Buộc trình duyệt lấy từ cache mà không cần hỏi lại server
            }
        });

    } catch (error) {
        res.status(500).json({ message: error.message });
    }
});

/**
 * @route   PUT /api/scenes/:id
 * @desc    Update an existing scene
 * @access  Private (Owner only)
 */
router.put('/scenes/:id', protect, uploadSinglePanorama, async (req, res) => {
    try {
        const { title, description, privacy, sharedWithUsers, sharedEmails, shareExpireDays, lat, lng } = req.body;
        const scene = await Scene.findById(req.params.id);

        if (!scene || scene.createdBy.toString() !== req.user._id.toString()) {
            return res.status(403).json({ message: 'Not authorized' });
        }

        const oldPrivacy = scene.privacy;

        // Update basic info
        scene.name = title || scene.name;
        scene.description = description !== undefined ? description : scene.description;
        scene.privacy = privacy || scene.privacy;
        if (lat) scene.gps.lat = parseFloat(lat);
        if (lng) scene.gps.lng = parseFloat(lng);

        // Cập nhật danh sách chia sẻ
        if (sharedWithUsers) {
            try {
                scene.sharedWith = JSON.parse(sharedWithUsers);
            } catch (e) {
                console.error("Lỗi parse sharedWithUsers:", e);
            }
        }
        if (sharedEmails) {
            try {
                scene.sharedEmails = JSON.parse(sharedEmails);
            } catch (e) {
                console.error("Lỗi parse sharedEmails:", e);
            }
        }

        // LOGIC ĐỒNG BỘ QUYỀN RIÊNG TƯ (CASCADING PRIVACY)
        // Nếu quyền chia sẻ thay đổi, cập nhật toàn bộ các Scene con liên kết trực tiếp
        if (privacy && privacy !== oldPrivacy) {
            try {
                const linkedHotspots = await Hotspot.find({ parent_scene_id: scene._id });
                const targetSceneIds = linkedHotspots
                    .map(h => h.target_scene_id)
                    .filter(id => id && id.toString() !== scene._id.toString());

                if (targetSceneIds.length > 0) {
                    for (const targetId of targetSceneIds) {
                        const updateData = { privacy: privacy };
                        
                        // Nếu chuyển sang 'shared', đảm bảo scene con cũng có token riêng
                        if (privacy === 'shared') {
                            const target = await Scene.findById(targetId);
                            if (target && !target.shareToken) {
                                updateData.shareToken = crypto.randomBytes(24).toString('hex');
                            }
                        }
                        await Scene.updateOne({ _id: targetId }, { $set: updateData });
                    }
                    console.log(`[Privacy Sync] Cascaded ${privacy} status to ${targetSceneIds.length} linked scenes.`);
                }
            } catch (err) {
                console.error("Lỗi khi đồng bộ quyền riêng tư cho các scene con:", err.message);
            }
        }

        if (privacy === 'shared') {
            if (!scene.shareToken) {
                scene.shareToken = crypto.randomBytes(24).toString('hex');
            }
            // Thiết lập ngày hết hạn nếu có truyền lên
            if (shareExpireDays && shareExpireDays !== 'never') {
                const expires = new Date();
                expires.setDate(expires.getDate() + parseInt(shareExpireDays));
                scene.shareTokenExpires = expires;
            } else if (shareExpireDays === 'never') {
                scene.shareTokenExpires = null;
            }
        }

        // Update image if new one is uploaded
        if (req.file) {
            const processedFileName = `processed_${req.file.filename}.jpg`;
            const processedFilePath = path.join(uploadDir, processedFileName);

            await resizeTo8K(req.file.path, processedFilePath);
            await injectGPSCoordinates(processedFilePath, scene.gps.lat, scene.gps.lng);
            
            const asset = new Asset({
                filePath: processedFilePath,
                uploadedBy: req.user._id
            });
            await asset.save();
            scene.assetId = asset._id;

            if (fs.existsSync(req.file.path)) fs.unlinkSync(req.file.path);
        }

        await scene.save();
        res.json({ message: 'Scene updated', scene });
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
});

/**
 * @route   DELETE /api/scenes/:id
 * @desc    Delete a scene and its assets
 * @access  Private (Owner only)
 */
router.delete('/scenes/:id', protect, async (req, res) => {
    try {
        const rootSceneId = req.params.id;
        const rootScene = await Scene.findById(rootSceneId);

        if (!rootScene) {
            return res.status(404).json({ message: 'Scene không tồn tại' });
        }

        // Kiểm tra quyền: Người tạo hoặc Admin
        const isAdmin = req.user.role === 'admin';
        const isOwner = rootScene.createdBy.toString() === req.user._id.toString();

        if (!isAdmin && !isOwner) {
            return res.status(403).json({ message: 'Bạn không có quyền xóa scene này' });
        }

        // 1. Tìm tất cả scene con dây chuyền (BFS)
        let scenesToDelete = [rootSceneId.toString()];
        let queue = [rootSceneId.toString()];

        while (queue.length > 0) {
            const parentId = queue.shift();
            const childHotspots = await Hotspot.find({ parent_scene_id: parentId });
            for (const hs of childHotspots) {
                if (hs.target_scene_id) {
                    const targetIdStr = hs.target_scene_id.toString();
                    if (!scenesToDelete.includes(targetIdStr)) {
                        scenesToDelete.push(targetIdStr);
                        queue.push(targetIdStr);
                    }
                }
            }
        }

        // 2. Xử lý xóa Asset và File vật lý cho toàn bộ danh sách
        const scenes = await Scene.find({ _id: { $in: scenesToDelete } });
        const assetIds = scenes.map(s => s.assetId).filter(id => id);
        const assets = await Asset.find({ _id: { $in: assetIds } });

        for (const asset of assets) {
            if (asset.filePath && fs.existsSync(asset.filePath)) {
                try { fs.unlinkSync(asset.filePath); } catch (e) { console.error(e); }
            }
        }

        // 3. Xóa Hotspot: Cả hotspot xuất phát từ và trỏ đến các scene bị xóa
        await Hotspot.deleteMany({
            $or: [
                { parent_scene_id: { $in: scenesToDelete } },
                { target_scene_id: { $in: scenesToDelete } }
            ]
        });

        // 4. Xóa dữ liệu trong DB
        await Asset.deleteMany({ _id: { $in: assetIds } });
        await Scene.deleteMany({ _id: { $in: scenesToDelete } });

        res.json({ 
            message: scenesToDelete.length > 1 
                ? `Đã xóa vĩnh viễn scene và ${scenesToDelete.length - 1} scene con liên quan.` 
                : 'Đã xóa scene thành công.' 
        });
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
});

/**
 * @route   GET /api/me/profile
 * @desc    Lấy thông tin hồ sơ người dùng hiện tại
 * @access  Private
 */
router.get('/me/profile', protect, async (req, res) => {
    try {
        const user = await User.findById(req.user._id).select('-password').lean();

        // Tính toán dung lượng thực tế của người dùng
        const usageResult = await Asset.aggregate([
            { $match: { uploadedBy: req.user._id } },
            { 
                $group: { 
                    _id: null, 
                    totalUsage: { $sum: { $ifNull: ["$fileSize", 0] } } 
                } 
            }
        ]);

        const currentUsage = usageResult.length > 0 ? usageResult[0].totalUsage : 0;
        const quota = ROLE_QUOTAS[user.role] || ROLE_QUOTAS['Thành viên'];

        res.json({
            ...user,
            storage: {
                used: currentUsage,
                quota: quota === Infinity ? -1 : quota // -1 đại diện cho không giới hạn
            }
        });
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
});

/**
 * @route   GET /api/me/assets/top-large
 * @desc    Lấy danh sách 5 tệp tin chiếm dung lượng lớn nhất của người dùng
 * @access  Private
 */
router.get('/me/assets/top-large', protect, async (req, res) => {
    try {
        const topAssets = await Asset.aggregate([
            { $match: { uploadedBy: req.user._id } },
            { $sort: { fileSize: -1 } },
            { $limit: 5 },
            {
                $lookup: {
                    from: 'scenes',
                    localField: '_id',
                    foreignField: 'assetId',
                    as: 'scene'
                }
            },
            { $unwind: { path: '$scene', preserveNullAndEmptyArrays: true } },
            {
                $project: {
                    fileSize: 1,
                    createdAt: 1,
                    'scene.name': 1,
                    'scene.title': 1,
                    'scene._id': 1
                }
            }
        ]);
        res.json(topAssets);
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
});

/**
 * @route   PUT /api/me/profile
 * @desc    Cập nhật hồ sơ (đổi tên, mật khẩu)
 * @access  Private
 */
router.put('/me/profile', protect, async (req, res) => {
    try {
        const user = await User.findById(req.user._id);
        if (!user) return res.status(404).json({ message: 'User not found' });

        if (req.body.username) user.username = req.body.username;
        if (req.body.password) user.password = req.body.password; // Hook pre-save sẽ tự hash

        await user.save();
        res.json({
            message: 'Hồ sơ đã được cập nhật',
            user: { id: user._id, username: user.username, role: user.role }
        });
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
});

/**
 * @route   GET /api/me/scenes
 * @desc    Lấy danh sách các cảnh mẹ do người dùng tạo
 * @access  Private
 */
router.get('/me/scenes', protect, async (req, res) => {
    try {
        const scenes = await Scene.find({ createdBy: req.user._id })
            .populate('createdBy', 'username')
            .populate('assetId')
            .sort({ createdAt: -1 });
        res.json(scenes);
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
});

/**
 * @route   GET /api/me/assets
 * @desc    Lấy danh sách media của người dùng
 * @access  Private
 */
router.get('/me/assets', protect, async (req, res) => {
    try {
        // Sử dụng Aggregation để lấy Asset kèm thông tin Scene và Parent Scene
        const query = (req.user.role === 'admin' || req.user.role === 'Chủ sở hữu') ? {} : { uploadedBy: req.user._id };
        
        const assets = await Asset.aggregate([
            { $match: query },
            {
                $lookup: {
                    from: 'scenes', // Tên collection trong DB (thường là số nhiều)
                    localField: '_id',
                    foreignField: 'assetId',
                    as: 'linkedScene'
                }
            },
            { $unwind: { path: '$linkedScene', preserveNullAndEmptyArrays: true } },
            {
                $lookup: {
                    from: 'hotspots',
                    localField: 'linkedScene._id',
                    foreignField: 'target_scene_id',
                    as: 'incomingHotspots'
                }
            },
            {
                $lookup: {
                    from: 'scenes',
                    localField: 'incomingHotspots.parent_scene_id',
                    foreignField: '_id',
                    as: 'parentScenes'
                }
            },
            {
                $project: {
                    filePath: 0 // Bảo mật: Không trả về đường dẫn vật lý đầy đủ
                }
            },
            { $sort: { createdAt: -1 } }
        ]);

        res.json(assets);
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
});

/**
 * @route   DELETE /api/assets/:id
 * @desc    Xóa Asset + Xóa Scene liên quan (nếu có) + Xóa file vật lý
 * @access  Private (Chỉ người upload hoặc Admin)
 */
router.delete('/assets/:id', protect, async (req, res) => {
    try {
        const asset = await Asset.findById(req.params.id);
        if (!asset) return res.status(404).json({ message: 'Ảnh không tồn tại' });

        // Kiểm tra quyền: Người upload hoặc Admin (Chủ sở hữu)
        const isOwner = asset.uploadedBy && asset.uploadedBy.toString() === req.user._id.toString();
        const isAdmin = req.user.role === 'admin' || req.user.role === 'Chủ sở hữu';

        if (!isOwner && !isAdmin) {
            return res.status(403).json({ message: 'Bạn không có quyền xóa tập tin này' });
        }

        // 1. Tìm và xóa Scene liên quan nếu có
        const linkedScene = await Scene.findOne({ assetId: asset._id });
        if (linkedScene) {
            // Xóa toàn bộ hotspot trỏ đến hoặc xuất phát từ scene này
            await Hotspot.deleteMany({
                $or: [
                    { parent_scene_id: linkedScene._id },
                    { target_scene_id: linkedScene._id }
                ]
            });
            await Scene.findByIdAndDelete(linkedScene._id);
        }

        // 2. Xóa file vật lý trên disk
        if (asset.filePath && fs.existsSync(asset.filePath)) {
            fs.unlinkSync(asset.filePath);
        }

        // 3. Xóa Asset trong DB
        await Asset.findByIdAndDelete(req.params.id);

        res.json({ message: 'Đã xóa ảnh và các dữ liệu liên quan thành công' });
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
});

/**
 * @route   GET /api/admin/users
 * @desc    Lấy toàn bộ danh sách người dùng (Chỉ Admin)
 * @access  Private (Admin)
 */
router.get('/admin/users', protect, async (req, res) => {
    if (req.user.role !== 'admin' && req.user.role !== 'Chủ sở hữu') {
        return res.status(403).json({ message: 'Bạn không có quyền truy cập quản trị' });
    }
    try {
        const page = parseInt(req.query.page) || 1;
        const limit = parseInt(req.query.limit) || 10;
        const skip = (page - 1) * limit;

        const totalUsers = await User.countDocuments();
        
        // Sử dụng Aggregation để sắp xếp Role ưu tiên và Phân trang
        const users = await User.aggregate([
            {
                $addFields: {
                    roleOrder: {
                        $switch: {
                            branches: [
                                { case: { $eq: ["$role", "admin"] }, then: 0 },
                                { case: { $eq: ["$role", "Chủ sở hữu"] }, then: 1 }
                            ],
                            default: 2
                        }
                    }
                }
            },
            { $sort: { roleOrder: 1, createdAt: -1 } },
            { $skip: skip },
            { $limit: limit }
        ]);

        res.json({
            users,
            totalPages: Math.ceil(totalUsers / limit),
            currentPage: page,
            totalUsers
        });
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
});

/**
 * @route   PUT /api/admin/users/:id
 * @desc    Admin cập nhật thông tin người dùng (Quyền, Mật khẩu, Email, Họ tên)
 */
router.put('/admin/users/:id', protect, async (req, res) => {
    if (req.user.role !== 'admin' && req.user.role !== 'Chủ sở hữu') return res.status(403).json({ message: 'Forbidden' });
    try {
        const { fullName, email, role, password } = req.body;
        const user = await User.findById(req.params.id);
        if (!user) return res.status(404).json({ message: 'Người dùng không tồn tại' });

        if (user.role === 'admin') {
            // Theo yêu cầu: Admin tối cao chỉ được sửa Họ tên và Email
            if (fullName) user.fullName = fullName;
            if (email) user.email = email;
            // Bỏ qua role và password để bảo vệ tài khoản root
        } else {
            // User bình thường được sửa tất cả
            if (fullName) user.fullName = fullName;
            if (email) user.email = email;
            if (role) user.role = role;
            
            if (password && password.trim() !== '') {
                user.password = password;
            }
        }

        await user.save();
        res.json({ message: 'Cập nhật người dùng thành công' });
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
});

/**
 * @route   DELETE /api/admin/users/:id
 * @desc    Admin xóa vĩnh viễn người dùng
 */
router.delete('/admin/users/:id', protect, async (req, res) => {
    if (req.user.role !== 'admin' && req.user.role !== 'Chủ sở hữu') return res.status(403).json({ message: 'Forbidden' });
    try {
        const user = await User.findById(req.params.id);
        if (!user) return res.status(404).json({ message: 'Người dùng không tồn tại' });
        
        if (user.role === 'admin') {
            return res.status(400).json({ message: 'Không thể xóa tài khoản Admin tối cao' });
        }

        // Lưu ý: Trong thực tế bạn có thể muốn xóa cả các Scene của user này
        await User.findByIdAndDelete(req.params.id);
        res.json({ message: 'Đã xóa người dùng vĩnh viễn' });
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
});

/**
 * @route   GET/PUT /api/system/settings
 * @desc    Quản lý thiết lập hệ thống
 * @access  Private (Admin)
 */
router.get('/system/settings', optionalAuth, async (req, res) => {
    try {
        let settings = await Setting.findOne();
        if (!settings) settings = await Setting.create({});
        res.json(settings);
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
});

router.put('/system/settings', protect, async (req, res) => {
    if (req.user.role !== 'admin' && req.user.role !== 'Chủ sở hữu') return res.status(403).json({ message: 'Forbidden' });
    try {
        const settings = await Setting.findOneAndUpdate({}, req.body, { new: true, upsert: true });
        res.json(settings);
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
});

/**
 * @route   POST /api/maintenance/reset-all
 * @desc    Wipe all scenes, assets, and physical files (DANGEROUS: For dev reset only)
 * @access  Private (Owner only)
 */
router.post('/maintenance/reset-all', protect, async (req, res) => {
    try {
        if (req.user.role !== 'admin' && req.user.role !== 'Chủ sở hữu') {
            return res.status(403).json({ message: 'Chỉ Admin tối cao mới có quyền thực hiện thao tác này' });
        }

        // 1. Xóa toàn bộ dữ liệu trong Database
        await Scene.deleteMany({});
        await Asset.deleteMany({});
        // Lưu ý: Không xóa Users trừ khi bạn muốn reset cả tài khoản

        // 2. Dọn dẹp thư mục uploads (trừ các file .gitkeep hoặc thư mục temp)
        const files = fs.readdirSync(uploadDir);
        for (const file of files) {
            const fullPath = path.join(uploadDir, file);
            if (fs.lstatSync(fullPath).isFile()) {
                fs.unlinkSync(fullPath);
            }
        }

        // 3. Dọn dẹp thư mục temp
        const tempFiles = fs.readdirSync(tempDir);
        for (const file of tempFiles) {
            const fullPath = path.join(tempDir, file);
            if (fs.lstatSync(fullPath).isFile()) {
                fs.unlinkSync(fullPath);
            }
        }

        console.warn(`[Maintenance]: Toàn bộ dữ liệu tour đã bị xóa bởi ${req.user.username}`);
        res.json({ message: 'Dữ liệu đã được xóa sạch. Hãy clear localStorage ở trình duyệt để bắt đầu lại.' });
    } catch (error) {
        res.status(500).json({ message: error.message });
    }
});

module.exports = router;
