import { Router } from 'express';
import { promises as fs } from 'fs';
import * as fsSync from 'fs';
import path from 'path';
import multer from 'multer';
import { createReadStream, createWriteStream } from 'fs';
import archiver from 'archiver';
import * as tar from 'tar';
import * as zlib from 'zlib';
import mime from 'mime-types';
import { exec } from 'child_process';
import { promisify } from 'util';
import jschardet from 'jschardet';
import iconv from 'iconv-lite';
import { authenticateToken, authenticateTokenFlexible } from '../middleware/auth.js';
import { taskManager } from '../modules/task/taskManager.js';
import { compressionWorker } from '../modules/task/compressionWorker.js';
import { executeFileOperation } from '../modules/task/fileOperationWorker.js';
import { ChunkUploadManager } from '../modules/chunkUploadManager.js';
import { createTarSecurityFilter } from '../utils/tarSecurityFilter.js';
import { zipToolsManager } from '../utils/zipToolsManager.js';
const execAsync = promisify(exec);
const router = Router();
const fixChineseFilename = (filename) => {
    if (!filename)
        return filename;
    try {
        if (filename.includes('�') || /[\x80-\xFF]/.test(filename)) {
            const buffer = Buffer.from(filename, 'latin1');
            const utf8Name = buffer.toString('utf8');
            if (!utf8Name.includes('�') && utf8Name.length > 0) {
                return utf8Name;
            }
            const binaryBuffer = Buffer.from(filename, 'binary');
            const binaryUtf8 = binaryBuffer.toString('utf8');
            if (!binaryUtf8.includes('�') && binaryUtf8.length > 0) {
                return binaryUtf8;
            }
        }
        return filename;
    }
    catch (error) {
        console.warn('Failed to fix filename encoding:', error);
        return filename;
    }
};
const isSafeFilename = (filename) => {
    if (!filename || filename.length === 0)
        return false;
    const dangerousChars = /[<>:"/\\|?*\x00-\x1f]/;
    if (dangerousChars.test(filename))
        return false;
    const reservedNames = /^(CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(\.|$)/i;
    if (reservedNames.test(filename))
        return false;
    return true;
};
const upload = multer({
    storage: multer.diskStorage({
        destination: (req, file, cb) => {
            const uploadDir = path.join(process.cwd(), 'uploads');
            cb(null, uploadDir);
        },
        filename: (req, file, cb) => {
            let originalName = fixChineseFilename(file.originalname);
            console.log('Original filename:', file.originalname);
            console.log('Fixed filename:', originalName);
            if (!isSafeFilename(originalName)) {
                const timestamp = Date.now();
                const ext = path.extname(file.originalname) || '.tmp';
                originalName = `upload-${timestamp}${ext}`;
                console.log('Using fallback filename:', originalName);
            }
            cb(null, originalName);
        }
    }),
    fileFilter: (req, file, cb) => {
        file.originalname = fixChineseFilename(file.originalname);
        console.log('FileFilter - processed filename:', file.originalname);
        cb(null, true);
    }
});
const isValidPath = (filePath) => {
    if (!filePath || typeof filePath !== 'string') {
        return false;
    }
    const decodedPath = decodeURIComponent(filePath);
    const normalizedPath = path.normalize(decodedPath);
    if (normalizedPath.includes('..')) {
        return false;
    }
    const isAbsolute = path.isAbsolute(normalizedPath);
    const isWindowsDrive = process.platform === 'win32' && /^[A-Za-z]:[\\/]?$/.test(normalizedPath);
    return isAbsolute || isWindowsDrive;
};
const fixWindowsPath = (filePath) => {
    if (!filePath)
        return filePath;
    let decodedPath = decodeURIComponent(filePath);
    if (process.platform === 'win32' && /^[\/\\][A-Za-z]:/.test(decodedPath)) {
        decodedPath = decodedPath.substring(1);
    }
    if (process.platform === 'win32') {
        if (/^[A-Za-z]:$/.test(decodedPath)) {
            decodedPath = decodedPath + '\\';
        }
        else if (/^[A-Za-z]:\/+$/.test(decodedPath)) {
            decodedPath = decodedPath.charAt(0) + decodedPath.charAt(1) + '\\';
        }
    }
    return decodedPath;
};
const normalizeTextEncoding = (encoding) => {
    if (!encoding) {
        return 'utf-8';
    }
    const trimmedEncoding = encoding.toLowerCase().trim();
    const compactEncoding = trimmedEncoding.replace(/[_-]/g, '');
    if (compactEncoding.includes('utf16') || compactEncoding.includes('ucs2')) {
        if (compactEncoding.includes('be')) {
            return 'utf-16be';
        }
        return 'utf-16le';
    }
    if (compactEncoding.includes('utf8')) {
        return 'utf-8';
    }
    if (compactEncoding.includes('gbk') || compactEncoding.includes('gb2312') || compactEncoding.includes('cp936')) {
        return 'gbk';
    }
    if (compactEncoding.includes('big5')) {
        return 'big5';
    }
    if (compactEncoding.includes('ascii')) {
        return 'utf-8';
    }
    if (compactEncoding.includes('windows1252') || compactEncoding === 'cp1252') {
        return 'windows-1252';
    }
    if (compactEncoding.includes('iso88591') || compactEncoding === 'latin1') {
        return 'iso-8859-1';
    }
    return trimmedEncoding;
};
const detectBomEncoding = (buffer) => {
    if (buffer.length >= 3 &&
        buffer[0] === 0xEF &&
        buffer[1] === 0xBB &&
        buffer[2] === 0xBF) {
        return 'utf-8';
    }
    if (buffer.length >= 2) {
        if (buffer[0] === 0xFF && buffer[1] === 0xFE) {
            return 'utf-16le';
        }
        if (buffer[0] === 0xFE && buffer[1] === 0xFF) {
            return 'utf-16be';
        }
    }
    return null;
};
const detectUtf16EncodingByNullPattern = (buffer) => {
    if (buffer.length < 4) {
        return null;
    }
    const sampleLength = Math.min(buffer.length, 4096);
    let evenZeroCount = 0;
    let oddZeroCount = 0;
    let evenByteCount = 0;
    let oddByteCount = 0;
    for (let index = 0; index < sampleLength; index++) {
        if (index % 2 === 0) {
            evenByteCount++;
            if (buffer[index] === 0x00) {
                evenZeroCount++;
            }
        }
        else {
            oddByteCount++;
            if (buffer[index] === 0x00) {
                oddZeroCount++;
            }
        }
    }
    if (evenByteCount === 0 || oddByteCount === 0) {
        return null;
    }
    const evenZeroRatio = evenZeroCount / evenByteCount;
    const oddZeroRatio = oddZeroCount / oddByteCount;
    if (oddZeroRatio >= 0.6 && evenZeroRatio <= 0.2) {
        return 'utf-16le';
    }
    if (evenZeroRatio >= 0.6 && oddZeroRatio <= 0.2) {
        return 'utf-16be';
    }
    return null;
};
const prependEncodingBom = (buffer, encoding, bomEncoding) => {
    if (!bomEncoding || bomEncoding !== encoding) {
        return buffer;
    }
    if (encoding === 'utf-8') {
        if (buffer.length >= 3 &&
            buffer[0] === 0xEF &&
            buffer[1] === 0xBB &&
            buffer[2] === 0xBF) {
            return buffer;
        }
        return Buffer.concat([Buffer.from([0xEF, 0xBB, 0xBF]), buffer]);
    }
    if (encoding === 'utf-16le') {
        if (buffer.length >= 2 && buffer[0] === 0xFF && buffer[1] === 0xFE) {
            return buffer;
        }
        return Buffer.concat([Buffer.from([0xFF, 0xFE]), buffer]);
    }
    if (encoding === 'utf-16be') {
        if (buffer.length >= 2 && buffer[0] === 0xFE && buffer[1] === 0xFF) {
            return buffer;
        }
        return Buffer.concat([Buffer.from([0xFE, 0xFF]), buffer]);
    }
    return buffer;
};
router.get('/list', authenticateToken, async (req, res) => {
    try {
        const { path: dirPath = '/', page = '1', pageSize = '50' } = req.query;
        if (!isValidPath(dirPath)) {
            return res.status(400).json({
                status: 'error',
                message: '无效的路径'
            });
        }
        const fixedDirPath = fixWindowsPath(dirPath);
        const stats = await fs.stat(fixedDirPath);
        if (!stats.isDirectory()) {
            return res.status(400).json({
                status: 'error',
                message: '指定路径不是目录'
            });
        }
        const items = await fs.readdir(fixedDirPath);
        const allFiles = [];
        for (const item of items) {
            const itemPath = path.join(fixedDirPath, item);
            try {
                const itemStats = await fs.stat(itemPath);
                allFiles.push({
                    name: item,
                    path: itemPath,
                    type: itemStats.isDirectory() ? 'directory' : 'file',
                    size: itemStats.size,
                    modified: itemStats.mtime.toISOString()
                });
            }
            catch (error) {
                continue;
            }
        }
        allFiles.sort((a, b) => {
            if (a.type !== b.type) {
                return a.type === 'directory' ? -1 : 1;
            }
            return a.name.localeCompare(b.name);
        });
        const pageNum = parseInt(page, 10);
        const pageSizeNum = parseInt(pageSize, 10);
        const startIndex = (pageNum - 1) * pageSizeNum;
        const endIndex = startIndex + pageSizeNum;
        const paginatedFiles = allFiles.slice(startIndex, endIndex);
        res.json({
            status: 'success',
            data: {
                files: paginatedFiles,
                pagination: {
                    page: pageNum,
                    pageSize: pageSizeNum,
                    total: allFiles.length,
                    totalPages: Math.ceil(allFiles.length / pageSizeNum),
                    hasMore: endIndex < allFiles.length
                }
            }
        });
    }
    catch (error) {
        console.error('List request - Error occurred:', error);
        console.error('List request - Error stack:', error.stack);
        if (!res.headersSent) {
            res.status(500).json({
                status: 'error',
                message: error.message || '文件列表获取失败'
            });
        }
    }
});
router.get('/read', authenticateToken, async (req, res) => {
    try {
        const { path: filePath } = req.query;
        if (!isValidPath(filePath)) {
            return res.status(400).json({
                status: 'error',
                message: '无效的路径'
            });
        }
        const fixedFilePath = fixWindowsPath(filePath);
        const stats = await fs.stat(fixedFilePath);
        if (!stats.isFile()) {
            return res.status(400).json({
                status: 'error',
                message: '指定路径不是文件'
            });
        }
        const contentType = mime.lookup(fixedFilePath) || 'application/octet-stream';
        res.setHeader('Content-Type', contentType);
        res.setHeader('Content-Length', stats.size.toString());
        const stream = createReadStream(fixedFilePath);
        stream.pipe(res);
    }
    catch (error) {
        if (error.code === 'ENOENT') {
            return res.status(404).json({ status: 'error', message: '文件未找到' });
        }
        res.status(500).json({
            status: 'error',
            message: error.message
        });
    }
});
router.get('/read-content', authenticateToken, async (req, res) => {
    try {
        const { path: filePath } = req.query;
        if (!isValidPath(filePath)) {
            return res.status(400).json({
                status: 'error',
                message: '无效的路径'
            });
        }
        const fixedFilePath = fixWindowsPath(filePath);
        const stats = await fs.stat(fixedFilePath);
        if (!stats.isFile()) {
            return res.status(400).json({
                status: 'error',
                message: '指定路径不是文件'
            });
        }
        const buffer = await fs.readFile(fixedFilePath);
        const bomEncoding = detectBomEncoding(buffer);
        const nullPatternEncoding = bomEncoding ? null : detectUtf16EncodingByNullPattern(buffer);
        const detected = jschardet.detect(buffer);
        let detectedEncoding = 'utf-8';
        let confidence = detected.confidence || 0;
        if (bomEncoding) {
            detectedEncoding = bomEncoding;
            confidence = 1;
        }
        else if (nullPatternEncoding) {
            detectedEncoding = nullPatternEncoding;
            confidence = Math.max(confidence, 0.99);
        }
        else if (detected.encoding) {
            detectedEncoding = normalizeTextEncoding(detected.encoding);
        }
        const supportedEncodings = [
            'utf-8',
            'utf-16le',
            'utf-16be',
            'gbk',
            'big5',
            'ascii',
            'windows-1252',
            'iso-8859-1'
        ];
        const isIncompatible = !supportedEncodings.includes(detectedEncoding.toLowerCase());
        const decodingEncoding = detectedEncoding;
        let content;
        let finalEncoding = decodingEncoding;
        try {
            if (iconv.encodingExists(decodingEncoding)) {
                content = iconv.decode(buffer, decodingEncoding);
                if (content.charCodeAt(0) === 0xFEFF) {
                    content = content.substring(1);
                }
            }
            else {
                content = buffer.toString('utf-8');
                finalEncoding = 'utf-8';
            }
        }
        catch (decodeError) {
            try {
                content = buffer.toString('utf-8');
                finalEncoding = 'utf-8';
            }
            catch (utf8Error) {
                content = buffer.toString('hex');
                finalEncoding = 'binary';
            }
        }
        res.json({
            status: 'success',
            data: {
                content: content,
                encoding: finalEncoding,
                detectedEncoding: detectedEncoding,
                bomEncoding: bomEncoding,
                confidence: confidence,
                isIncompatible: isIncompatible,
                size: stats.size,
                modified: stats.mtime
            }
        });
    }
    catch (error) {
        if (error.code === 'ENOENT') {
            return res.status(404).json({ status: 'error', message: '文件未找到' });
        }
        res.status(500).json({
            status: 'error',
            message: error.message
        });
    }
});
router.get('/preview', authenticateToken, async (req, res) => {
    try {
        const { path: filePath } = req.query;
        console.log('Preview request - Original path:', filePath);
        if (!filePath) {
            return res.status(400).json({
                status: 'error',
                message: '缺少文件路径'
            });
        }
        const decodedFilePath = decodeURIComponent(filePath);
        console.log('Preview request - Decoded path:', decodedFilePath);
        let absoluteFilePath;
        if (path.isAbsolute(decodedFilePath)) {
            absoluteFilePath = decodedFilePath;
        }
        else {
            absoluteFilePath = path.resolve(process.cwd(), decodedFilePath);
        }
        console.log('Preview request - Absolute path:', absoluteFilePath);
        const normalizedPath = path.normalize(absoluteFilePath);
        if (normalizedPath.includes('..')) {
            console.log('Preview request - Path contains dangerous traversal');
            return res.status(400).json({
                status: 'error',
                message: '无效的路径'
            });
        }
        console.log('Preview request - Checking file stats for:', absoluteFilePath);
        try {
            await fs.access(absoluteFilePath);
        }
        catch (accessError) {
            return res.status(404).json({
                status: 'error',
                message: '文件不存在或无法访问'
            });
        }
        const stats = await fs.stat(absoluteFilePath);
        if (!stats.isFile()) {
            return res.status(400).json({
                status: 'error',
                message: '指定路径不是文件'
            });
        }
        const ext = path.extname(absoluteFilePath).toLowerCase();
        const mimeTypes = {
            '.jpg': 'image/jpeg',
            '.jpeg': 'image/jpeg',
            '.png': 'image/png',
            '.gif': 'image/gif',
            '.bmp': 'image/bmp',
            '.webp': 'image/webp',
            '.svg': 'image/svg+xml',
            '.ico': 'image/x-icon'
        };
        const contentType = mimeTypes[ext] || 'application/octet-stream';
        console.log('Preview request - Reading file:', absoluteFilePath);
        console.log('Preview request - Content type:', contentType);
        console.log('Preview request - File extension:', ext);
        const fileBuffer = await fs.readFile(absoluteFilePath);
        console.log('Preview request - File buffer length:', fileBuffer.length);
        console.log('Preview request - Buffer first 10 bytes:', fileBuffer.slice(0, 10));
        res.setHeader('Content-Type', contentType);
        res.setHeader('Content-Length', fileBuffer.length);
        res.setHeader('Cache-Control', 'public, max-age=3600');
        console.log('Preview request - Sending response with headers:', {
            'Content-Type': contentType,
            'Content-Length': fileBuffer.length
        });
        res.send(fileBuffer);
        console.log('Preview request - Response sent successfully');
    }
    catch (error) {
        res.status(500).json({
            success: false,
            status: 'error',
            message: error.message
        });
    }
});
router.post('/create', authenticateToken, async (req, res) => {
    try {
        const { path: filePath, content = '', encoding = 'utf-8' } = req.body;
        if (!filePath) {
            return res.status(400).json({
                status: 'error',
                message: '缺少文件路径'
            });
        }
        if (!isValidPath(filePath)) {
            return res.status(400).json({
                status: 'error',
                message: '无效的路径'
            });
        }
        const fixedFilePath = fixWindowsPath(filePath);
        try {
            await fs.access(fixedFilePath);
            return res.status(400).json({
                status: 'error',
                message: '文件已存在'
            });
        }
        catch {
        }
        const parentDir = path.dirname(fixedFilePath);
        await fs.mkdir(parentDir, { recursive: true });
        const fileWatchManager = global.fileWatchManager;
        if (fileWatchManager) {
            fileWatchManager.ignoreNextChange(fixedFilePath);
        }
        await fs.writeFile(fixedFilePath, content, encoding);
        res.json({
            status: 'success',
            message: '文件创建成功'
        });
    }
    catch (error) {
        res.status(500).json({
            success: false,
            status: 'error',
            message: error.message
        });
    }
});
router.post('/save', authenticateToken, async (req, res) => {
    try {
        const { path: filePath, content, encoding = 'utf-8', bomEncoding = null } = req.body;
        if (!isValidPath(filePath)) {
            return res.status(400).json({
                status: 'error',
                message: '无效的路径'
            });
        }
        const fixedFilePath = fixWindowsPath(filePath);
        const fileWatchManager = global.fileWatchManager;
        if (fileWatchManager) {
            fileWatchManager.ignoreNextChange(fixedFilePath);
        }
        const normalizedEncoding = normalizeTextEncoding(encoding);
        const normalizedBomEncoding = bomEncoding ? normalizeTextEncoding(bomEncoding) : null;
        let buffer;
        try {
            if (iconv.encodingExists(normalizedEncoding)) {
                buffer = iconv.encode(content, normalizedEncoding);
            }
            else {
                buffer = Buffer.from(content, 'utf-8');
            }
        }
        catch (encodeError) {
            buffer = Buffer.from(content, 'utf-8');
        }
        buffer = prependEncodingBom(buffer, normalizedEncoding, normalizedBomEncoding);
        await fs.writeFile(fixedFilePath, buffer);
        res.json({
            status: 'success',
            message: '文件保存成功',
            encoding: normalizedEncoding,
            bomEncoding: normalizedBomEncoding
        });
    }
    catch (error) {
        res.status(500).json({
            status: 'error',
            message: error.message
        });
    }
});
router.delete('/delete', authenticateToken, async (req, res) => {
    try {
        const { paths } = req.body;
        if (!Array.isArray(paths)) {
            return res.status(400).json({
                status: 'error',
                message: '路径必须是数组'
            });
        }
        for (const filePath of paths) {
            if (!isValidPath(filePath)) {
                return res.status(400).json({
                    status: 'error',
                    message: `无效的路径: ${filePath}`
                });
            }
            const fixedFilePath = fixWindowsPath(filePath);
            const stats = await fs.stat(fixedFilePath);
            if (stats.isDirectory()) {
                await fs.rm(fixedFilePath, { recursive: true, force: true });
            }
            else {
                await fs.unlink(fixedFilePath);
            }
        }
        res.json({
            status: 'success',
            message: '删除成功'
        });
    }
    catch (error) {
        res.status(500).json({
            status: 'error',
            message: error.message
        });
    }
});
router.post('/rename', authenticateToken, async (req, res) => {
    try {
        const { oldPath, newPath } = req.body;
        console.log('重命名请求:', { oldPath, newPath });
        console.log('oldPath验证:', isValidPath(oldPath));
        console.log('newPath验证:', isValidPath(newPath));
        if (!oldPath || !newPath) {
            return res.status(400).json({
                status: 'error',
                message: '缺少必要的路径参数'
            });
        }
        if (!isValidPath(oldPath) || !isValidPath(newPath)) {
            return res.status(400).json({
                status: 'error',
                message: '无效的路径'
            });
        }
        const fixedOldPath = fixWindowsPath(oldPath);
        const fixedNewPath = fixWindowsPath(newPath);
        await fs.rename(fixedOldPath, fixedNewPath);
        res.json({
            status: 'success',
            message: '重命名成功'
        });
    }
    catch (error) {
        res.status(500).json({
            status: 'error',
            message: error.message
        });
    }
});
router.post('/check-paste-conflict', authenticateToken, async (req, res) => {
    try {
        const { sourcePaths, targetPath } = req.body;
        if (!sourcePaths || !Array.isArray(sourcePaths) || sourcePaths.length === 0) {
            return res.status(400).json({
                success: false,
                message: '缺少源文件路径'
            });
        }
        if (!targetPath) {
            return res.status(400).json({
                success: false,
                message: '缺少目标路径'
            });
        }
        const fixedTargetPath = fixWindowsPath(targetPath);
        const conflicts = [];
        for (const sourcePath of sourcePaths) {
            const fixedSource = fixWindowsPath(sourcePath);
            const fileName = path.basename(fixedSource);
            const targetFilePath = path.join(fixedTargetPath, fileName);
            let sourceIsDir = false;
            try {
                const sourceStats = await fs.stat(fixedSource);
                sourceIsDir = sourceStats.isDirectory();
            }
            catch {
                continue;
            }
            try {
                const stats = await fs.stat(targetFilePath);
                conflicts.push({
                    fileName,
                    sourcePath: fixedSource,
                    exists: true,
                    sourceIsDir,
                    existingSize: stats.isFile() ? stats.size : undefined,
                    existingModified: stats.mtime.toISOString()
                });
            }
            catch {
                conflicts.push({
                    fileName,
                    sourcePath: fixedSource,
                    exists: false,
                    sourceIsDir
                });
            }
        }
        const hasConflicts = conflicts.some(c => c.exists);
        res.json({
            success: true,
            data: {
                hasConflicts,
                conflicts
            }
        });
    }
    catch (error) {
        console.error('检查粘贴冲突失败:', error);
        res.status(500).json({
            success: false,
            message: error.message
        });
    }
});
router.post('/copy', authenticateToken, async (req, res) => {
    try {
        const { sourcePath, targetPath, sourcePaths, conflictStrategy } = req.body;
        const sources = sourcePaths || [sourcePath];
        if (!sources || sources.length === 0) {
            return res.status(400).json({
                success: false,
                status: 'error',
                message: '缺少源文件路径'
            });
        }
        if (!targetPath) {
            return res.status(400).json({
                success: false,
                status: 'error',
                message: '缺少目标路径'
            });
        }
        for (const source of sources) {
            if (!isValidPath(source)) {
                return res.status(400).json({
                    success: false,
                    status: 'error',
                    message: `无效的源路径: ${source}`
                });
            }
        }
        if (!isValidPath(targetPath)) {
            return res.status(400).json({
                success: false,
                status: 'error',
                message: '无效的目标路径'
            });
        }
        const fixedSources = sources.map(source => fixWindowsPath(source));
        const fixedTargetPath = fixWindowsPath(targetPath);
        for (const source of fixedSources) {
            try {
                await fs.access(source);
            }
            catch (error) {
                return res.status(400).json({
                    success: false,
                    status: 'error',
                    message: `源文件不存在: ${source}`
                });
            }
        }
        await fs.mkdir(fixedTargetPath, { recursive: true });
        const taskId = taskManager.createTask('copy', {
            sourcePaths: fixedSources,
            targetPath: fixedTargetPath,
            operation: 'copy',
            conflictStrategy: conflictStrategy || 'replace'
        });
        setImmediate(() => {
            executeFileOperation(taskId);
        });
        res.json({
            success: true,
            status: 'success',
            message: '复制任务已创建',
            taskId
        });
    }
    catch (error) {
        res.status(500).json({
            status: 'error',
            message: error.message
        });
    }
});
router.post('/move', authenticateToken, async (req, res) => {
    try {
        const { sourcePath, targetPath, sourcePaths, conflictStrategy } = req.body;
        const sources = sourcePaths || [sourcePath];
        if (!sources || sources.length === 0) {
            return res.status(400).json({
                success: false,
                status: 'error',
                message: '缺少源文件路径'
            });
        }
        if (!targetPath) {
            return res.status(400).json({
                success: false,
                status: 'error',
                message: '缺少目标路径'
            });
        }
        for (const source of sources) {
            if (!isValidPath(source)) {
                return res.status(400).json({
                    success: false,
                    status: 'error',
                    message: `无效的源路径: ${source}`
                });
            }
        }
        if (!isValidPath(targetPath)) {
            return res.status(400).json({
                success: false,
                status: 'error',
                message: '无效的目标路径'
            });
        }
        const fixedSources = sources.map(source => fixWindowsPath(source));
        const fixedTargetPath = fixWindowsPath(targetPath);
        for (const source of fixedSources) {
            try {
                await fs.access(source);
            }
            catch (error) {
                return res.status(400).json({
                    success: false,
                    status: 'error',
                    message: `源文件不存在: ${source}`
                });
            }
        }
        await fs.mkdir(fixedTargetPath, { recursive: true });
        const taskId = taskManager.createTask('move', {
            sourcePaths: fixedSources,
            targetPath: fixedTargetPath,
            operation: 'move',
            conflictStrategy: conflictStrategy || 'replace'
        });
        setImmediate(() => {
            executeFileOperation(taskId);
        });
        res.json({
            success: true,
            status: 'success',
            message: '移动任务已创建',
            taskId
        });
    }
    catch (error) {
        res.status(500).json({
            status: 'error',
            message: error.message
        });
    }
});
router.get('/search', authenticateToken, async (req, res) => {
    try {
        const { path: searchPath = '/home', query, type = 'all', case_sensitive = false, max_results = 100 } = req.query;
        if (!query) {
            return res.status(400).json({
                status: 'error',
                message: '搜索关键词不能为空'
            });
        }
        if (!isValidPath(searchPath)) {
            return res.status(400).json({
                status: 'error',
                message: '无效的路径'
            });
        }
        const fixedSearchPath = fixWindowsPath(searchPath);
        const results = await searchFiles(fixedSearchPath, query, type, case_sensitive === 'true', parseInt(max_results));
        res.json({
            status: 'success',
            results,
            total_found: results.length,
            truncated: results.length >= parseInt(max_results)
        });
    }
    catch (error) {
        res.status(500).json({
            status: 'error',
            message: error.message
        });
    }
});
router.get('/download', authenticateTokenFlexible, async (req, res) => {
    try {
        const { path: filePath, withProgress } = req.query;
        if (!isValidPath(filePath)) {
            return res.status(400).json({
                status: 'error',
                message: '无效的路径'
            });
        }
        const fixedFilePath = fixWindowsPath(filePath);
        const stats = await fs.stat(fixedFilePath);
        if (!stats.isFile()) {
            return res.status(400).json({
                status: 'error',
                message: '指定路径不是文件'
            });
        }
        const fileName = path.basename(fixedFilePath);
        if (withProgress === 'true') {
            const taskId = taskManager.createTask('download', {
                filePath: fixedFilePath,
                fileName,
                fileSize: stats.size
            });
            return res.json({
                status: 'success',
                taskId,
                message: '下载任务已创建'
            });
        }
        const encodedFileName = encodeURIComponent(fileName);
        const asciiFileName = fileName.replace(/[^\x00-\x7F]/g, '_');
        res.setHeader('Content-Disposition', `attachment; filename="${asciiFileName}"; filename*=UTF-8''${encodedFileName}`);
        res.setHeader('Content-Type', 'application/octet-stream');
        res.setHeader('Content-Length', stats.size.toString());
        const fileStream = createReadStream(fixedFilePath);
        fileStream.pipe(res);
    }
    catch (error) {
        res.status(500).json({
            status: 'error',
            message: error.message
        });
    }
});
router.post('/upload/check-conflict', authenticateToken, async (req, res) => {
    try {
        const { targetPath, fileNames, filePaths } = req.body;
        if (!targetPath || !fileNames || !Array.isArray(fileNames)) {
            return res.status(400).json({
                success: false,
                message: '缺少必要参数: targetPath 和 fileNames'
            });
        }
        const fixedTargetPath = fixWindowsPath(targetPath);
        let fullTargetPath;
        if (path.isAbsolute(fixedTargetPath)) {
            fullTargetPath = fixedTargetPath;
        }
        else {
            fullTargetPath = path.resolve(process.cwd(), fixedTargetPath.replace(/^\//, ''));
        }
        let fileRelativePaths = [];
        if (filePaths && Array.isArray(filePaths)) {
            fileRelativePaths = filePaths;
        }
        const conflicts = [];
        for (let i = 0; i < fileNames.length; i++) {
            const fileName = fileNames[i];
            const relativePath = fileRelativePaths[i] || '';
            let targetFilePath;
            if (relativePath) {
                targetFilePath = path.join(fullTargetPath, relativePath);
            }
            else {
                targetFilePath = path.join(fullTargetPath, fileName);
            }
            try {
                const stats = await fs.stat(targetFilePath);
                if (stats.isFile()) {
                    conflicts.push({
                        fileName,
                        relativePath: relativePath || undefined,
                        exists: true,
                        existingSize: stats.size,
                        existingModified: stats.mtime
                    });
                }
                else {
                    conflicts.push({ fileName, relativePath: relativePath || undefined, exists: false });
                }
            }
            catch {
                conflicts.push({ fileName, relativePath: relativePath || undefined, exists: false });
            }
        }
        const hasConflicts = conflicts.some(c => c.exists);
        res.json({
            success: true,
            data: {
                hasConflicts,
                conflicts
            }
        });
    }
    catch (error) {
        console.error('Check conflict error:', error);
        res.status(500).json({
            success: false,
            message: error.message || '检查文件冲突失败'
        });
    }
});
router.post('/upload', authenticateToken, upload.array('files'), async (req, res) => {
    console.log('Upload request received:');
    console.log('Body:', req.body);
    console.log('Files:', req.files);
    console.log('Files length:', req.files?.length);
    try {
        const { targetPath, conflictStrategy = 'rename', filePaths } = req.body;
        const files = req.files;
        if (!targetPath) {
            console.log('Error: Invalid target path');
            return res.status(400).json({ success: false, message: 'Invalid target path' });
        }
        if (!files || files.length === 0) {
            console.log('Error: No files uploaded');
            return res.status(400).json({ success: false, message: 'No files uploaded' });
        }
        let fileRelativePaths = [];
        if (filePaths) {
            try {
                fileRelativePaths = JSON.parse(filePaths);
                console.log('Folder upload detected, file paths:', fileRelativePaths);
            }
            catch (e) {
                console.warn('Failed to parse filePaths:', e);
            }
        }
        const fixedTargetPath = fixWindowsPath(targetPath);
        console.log('Original target path:', targetPath);
        console.log('Fixed target path:', fixedTargetPath);
        console.log('Conflict strategy:', conflictStrategy);
        let fullTargetPath;
        if (path.isAbsolute(fixedTargetPath)) {
            fullTargetPath = fixedTargetPath;
        }
        else {
            fullTargetPath = path.resolve(process.cwd(), fixedTargetPath.replace(/^\//, ''));
        }
        console.log('Resolved target path:', fullTargetPath);
        await fs.mkdir(fullTargetPath, { recursive: true });
        const results = [];
        for (let i = 0; i < files.length; i++) {
            const file = files[i];
            try {
                let originalName = fixChineseFilename(file.originalname);
                console.log(`Processing file: ${file.originalname} -> ${originalName}`);
                if (!isSafeFilename(originalName)) {
                    const timestamp = Date.now();
                    const ext = path.extname(file.originalname) || '.tmp';
                    originalName = `upload-${timestamp}${ext}`;
                    console.log(`Using fallback filename: ${originalName}`);
                }
                const relativePath = fileRelativePaths[i] || '';
                let fileTargetDir = fullTargetPath;
                if (relativePath) {
                    const relativeDir = path.dirname(relativePath);
                    if (relativeDir && relativeDir !== '.') {
                        fileTargetDir = path.join(fullTargetPath, relativeDir);
                        await fs.mkdir(fileTargetDir, { recursive: true });
                        console.log(`Created folder structure: ${fileTargetDir}`);
                    }
                }
                let finalFileName = originalName;
                const targetFilePath = path.join(fileTargetDir, originalName);
                const fileExists = await fs.access(targetFilePath).then(() => true).catch(() => false);
                if (fileExists) {
                    switch (conflictStrategy) {
                        case 'replace':
                            console.log(`Replacing existing file: ${originalName}`);
                            await fs.unlink(targetFilePath);
                            break;
                        case 'skip':
                            console.log(`Skipping existing file: ${originalName}`);
                            try {
                                await fs.unlink(file.path);
                            }
                            catch { }
                            results.push({ name: originalName, relativePath, success: true, skipped: true });
                            continue;
                        case 'rename':
                        default:
                            let counter = 1;
                            while (await fs.access(path.join(fileTargetDir, finalFileName)).then(() => true).catch(() => false)) {
                                const ext = path.extname(originalName);
                                const nameWithoutExt = path.basename(originalName, ext);
                                finalFileName = `${nameWithoutExt}(${counter})${ext}`;
                                counter++;
                            }
                            console.log(`Renaming file: ${originalName} -> ${finalFileName}`);
                            break;
                    }
                }
                const finalTargetFilePath = path.join(fileTargetDir, finalFileName);
                console.log(`Moving file from ${file.path} to ${finalTargetFilePath}`);
                try {
                    await fs.rename(file.path, finalTargetFilePath);
                }
                catch (renameError) {
                    if (renameError.code === 'EXDEV') {
                        console.log(`Cross-device rename failed, using copy+unlink for ${file.originalname}`);
                        await fs.copyFile(file.path, finalTargetFilePath);
                        await fs.unlink(file.path);
                    }
                    else {
                        throw renameError;
                    }
                }
                results.push({ name: finalFileName, relativePath, success: true, replaced: fileExists && conflictStrategy === 'replace' });
            }
            catch (error) {
                console.error(`Failed to move file ${file.originalname}:`, error);
                results.push({ name: file.originalname, success: false, error: error.message });
                try {
                    const fileExists = await fs.stat(file.path).then(() => true).catch(() => false);
                    if (fileExists) {
                        await fs.unlink(file.path);
                    }
                }
                catch (unlinkError) {
                    console.error(`Failed to cleanup file ${file.path}:`, unlinkError);
                }
            }
        }
        res.json({
            success: true,
            message: `Successfully uploaded ${results.filter(r => r.success).length} of ${files.length} files`,
            data: results
        });
    }
    catch (error) {
        console.error('Upload error:', error);
        res.status(500).json({ success: false, message: 'Upload failed', error: error.message });
    }
});
router.post('/upload/check', authenticateToken, async (req, res) => {
    try {
        const { uploadId, fileName, fileSize, totalChunks } = req.body;
        if (!uploadId || !fileName || !fileSize || !totalChunks) {
            return res.status(400).json({
                success: false,
                message: '缺少必要参数'
            });
        }
        const chunkManager = ChunkUploadManager.getInstance();
        const upload = await chunkManager.getOrCreateUpload(uploadId, fileName, fileSize, totalChunks, '');
        const uploadedChunks = chunkManager.getUploadedChunks(uploadId);
        res.json({
            success: true,
            data: {
                uploadId,
                uploadedChunks,
                totalChunks: upload.totalChunks
            }
        });
    }
    catch (error) {
        console.error('Check upload error:', error);
        res.status(500).json({
            success: false,
            message: error.message || '检查上传状态失败'
        });
    }
});
router.post('/upload/chunk', authenticateToken, upload.single('chunk'), async (req, res) => {
    try {
        const { uploadId, fileName, fileSize, chunkIndex, totalChunks, chunkHash, targetPath } = req.body;
        const chunkFile = req.file;
        if (!uploadId || !fileName || !fileSize || chunkIndex === undefined || !totalChunks || !chunkHash || !targetPath || !chunkFile) {
            return res.status(400).json({
                success: false,
                message: '缺少必要参数'
            });
        }
        const chunkManager = ChunkUploadManager.getInstance();
        const uploadSession = await chunkManager.getOrCreateUpload(uploadId, fileName, parseInt(fileSize), parseInt(totalChunks), targetPath);
        const chunkData = await fs.readFile(chunkFile.path);
        await chunkManager.saveChunk(uploadId, parseInt(chunkIndex), chunkData, chunkHash);
        try {
            const fileExists = await fs.stat(chunkFile.path).then(() => true).catch(() => false);
            if (fileExists) {
                await fs.unlink(chunkFile.path);
            }
        }
        catch (err) {
            console.warn('删除临时分片文件失败:', err);
        }
        res.json({
            success: true,
            message: `分片 ${chunkIndex} 上传成功`,
            data: {
                chunkIndex: parseInt(chunkIndex),
                uploaded: true
            }
        });
    }
    catch (error) {
        console.error('Upload chunk error:', error);
        if (req.file?.path) {
            await fs.unlink(req.file.path).catch(() => { });
        }
        res.status(500).json({
            success: false,
            message: error.message || '分片上传失败'
        });
    }
});
router.post('/upload/merge', authenticateToken, async (req, res) => {
    try {
        const { uploadId, fileName, fileSize, totalChunks, targetPath, conflictStrategy = 'rename' } = req.body;
        if (!uploadId || !fileName || !fileSize || !totalChunks || !targetPath) {
            return res.status(400).json({
                success: false,
                message: '缺少必要参数'
            });
        }
        const chunkManager = ChunkUploadManager.getInstance();
        if (!chunkManager.isUploadComplete(uploadId)) {
            const uploadedChunks = chunkManager.getUploadedChunks(uploadId);
            return res.status(400).json({
                success: false,
                message: `文件未完全上传，已上传 ${uploadedChunks.length}/${totalChunks} 个分片`
            });
        }
        const fixedTargetPath = fixWindowsPath(targetPath);
        let fullTargetPath;
        if (path.isAbsolute(fixedTargetPath)) {
            fullTargetPath = fixedTargetPath;
        }
        else {
            fullTargetPath = path.resolve(process.cwd(), fixedTargetPath.replace(/^\//, ''));
        }
        const targetFilePath = path.join(fullTargetPath, fileName);
        let finalFilePath = targetFilePath;
        const fileExists = await fs.access(targetFilePath).then(() => true).catch(() => false);
        if (fileExists) {
            if (conflictStrategy === 'replace') {
                console.log(`Replacing existing file: ${fileName}`);
                await fs.unlink(targetFilePath);
            }
            else {
                let counter = 1;
                while (await fs.access(finalFilePath).then(() => true).catch(() => false)) {
                    const ext = path.extname(fileName);
                    const nameWithoutExt = path.basename(fileName, ext);
                    const newFileName = `${nameWithoutExt}(${counter})${ext}`;
                    finalFilePath = path.join(fullTargetPath, newFileName);
                    counter++;
                }
                console.log(`Renaming file: ${fileName} -> ${path.basename(finalFilePath)}`);
            }
        }
        await chunkManager.mergeChunks(uploadId, finalFilePath);
        res.json({
            success: true,
            message: '文件合并成功',
            data: {
                filePath: finalFilePath,
                fileName: path.basename(finalFilePath),
                fileSize: parseInt(fileSize),
                replaced: fileExists && conflictStrategy === 'replace'
            }
        });
    }
    catch (error) {
        console.error('Merge chunks error:', error);
        res.status(500).json({
            success: false,
            message: error.message || '合并文件失败'
        });
    }
});
router.delete('/upload/cancel/:uploadId', authenticateToken, async (req, res) => {
    try {
        const { uploadId } = req.params;
        if (!uploadId) {
            return res.status(400).json({
                success: false,
                message: '缺少uploadId'
            });
        }
        const chunkManager = ChunkUploadManager.getInstance();
        await chunkManager.cancelUpload(uploadId);
        res.json({
            success: true,
            message: '上传已取消'
        });
    }
    catch (error) {
        console.error('Cancel upload error:', error);
        res.status(500).json({
            success: false,
            message: error.message || '取消上传失败'
        });
    }
});
router.post('/compress', authenticateToken, async (req, res) => {
    try {
        const { sourcePaths, targetPath, archiveName, format = 'zip', compressionLevel = 6 } = req.body;
        if (!Array.isArray(sourcePaths) || sourcePaths.length === 0) {
            return res.status(400).json({
                status: 'error',
                message: '源路径不能为空'
            });
        }
        const fixedSourcePaths = sourcePaths.map(sourcePath => fixWindowsPath(sourcePath));
        const fixedTargetPath = fixWindowsPath(targetPath);
        for (const sourcePath of fixedSourcePaths) {
            if (!isValidPath(sourcePath)) {
                return res.status(400).json({
                    status: 'error',
                    message: `无效的源路径: ${sourcePath}`
                });
            }
        }
        if (!isValidPath(fixedTargetPath)) {
            return res.status(400).json({
                status: 'error',
                message: '无效的目标路径'
            });
        }
        const archivePath = path.join(fixedTargetPath, archiveName);
        const taskId = taskManager.createTask('compress', {
            sourcePaths: fixedSourcePaths,
            archivePath,
            format,
            compressionLevel
        });
        compressionWorker.compressFiles(taskId, fixedSourcePaths, archivePath, format, compressionLevel)
            .catch(error => {
            console.error('压缩任务失败:', error);
        });
        res.json({
            status: 'success',
            message: '解压/压缩命令已下发',
            taskId,
            archivePath
        });
    }
    catch (error) {
        res.status(500).json({
            status: 'error',
            message: error.message
        });
    }
});
router.post('/extract', authenticateToken, async (req, res) => {
    try {
        const { archivePath, targetPath } = req.body;
        const fixedArchivePath = fixWindowsPath(archivePath);
        const fixedTargetPath = fixWindowsPath(targetPath);
        if (!isValidPath(fixedArchivePath) || !isValidPath(fixedTargetPath)) {
            return res.status(400).json({
                status: 'error',
                message: '无效的路径'
            });
        }
        const taskId = taskManager.createTask('extract', {
            archivePath: fixedArchivePath,
            targetPath: fixedTargetPath
        });
        compressionWorker.extractArchive(taskId, fixedArchivePath, fixedTargetPath)
            .catch(error => {
            console.error('解压任务失败:', error);
        });
        res.json({
            status: 'success',
            message: '解压/压缩命令已下发',
            taskId
        });
    }
    catch (error) {
        res.status(500).json({
            status: 'error',
            message: error.message
        });
    }
});
async function copyDirectory(src, dest) {
    await fs.mkdir(dest, { recursive: true });
    const items = await fs.readdir(src);
    for (const item of items) {
        const srcPath = path.join(src, item);
        const destPath = path.join(dest, item);
        const stats = await fs.stat(srcPath);
        if (stats.isDirectory()) {
            await copyDirectory(srcPath, destPath);
        }
        else {
            await fs.copyFile(srcPath, destPath);
        }
    }
}
async function searchFiles(searchPath, query, type, caseSensitive, maxResults) {
    const results = [];
    const searchQuery = caseSensitive ? query : query.toLowerCase();
    async function searchRecursive(currentPath) {
        if (results.length >= maxResults)
            return;
        try {
            const items = await fs.readdir(currentPath);
            for (const item of items) {
                if (results.length >= maxResults)
                    break;
                const itemPath = path.join(currentPath, item);
                const stats = await fs.stat(itemPath);
                const itemName = caseSensitive ? item : item.toLowerCase();
                if (itemName.includes(searchQuery)) {
                    if (type === 'all' ||
                        (type === 'file' && stats.isFile()) ||
                        (type === 'directory' && stats.isDirectory())) {
                        results.push({
                            name: item,
                            path: itemPath,
                            type: stats.isDirectory() ? 'directory' : 'file',
                            size: stats.size,
                            modified: stats.mtime.toISOString(),
                            parent_dir: currentPath
                        });
                    }
                }
                if (stats.isDirectory()) {
                    await searchRecursive(itemPath);
                }
            }
        }
        catch (error) {
        }
    }
    await searchRecursive(searchPath);
    return results;
}
async function compressFiles(sourcePaths, archivePath, format, compressionLevel) {
    return new Promise(async (resolve, reject) => {
        try {
            const output = createWriteStream(archivePath);
            const archive = archiver(format, {
                zlib: { level: compressionLevel }
            });
            output.on('close', () => resolve());
            archive.on('error', (err) => reject(err));
            archive.pipe(output);
            for (const sourcePath of sourcePaths) {
                const stats = await fs.stat(sourcePath);
                const name = path.basename(sourcePath);
                if (stats.isDirectory()) {
                    archive.directory(sourcePath, name);
                }
                else {
                    archive.file(sourcePath, { name });
                }
            }
            archive.finalize();
        }
        catch (error) {
            reject(error);
        }
    });
}
async function extractArchive(archivePath, targetPath) {
    return new Promise(async (resolve, reject) => {
        try {
            const ext = path.extname(archivePath).toLowerCase();
            const fileName = path.basename(archivePath).toLowerCase();
            await fs.mkdir(targetPath, { recursive: true });
            if (ext === '.zip') {
                await zipToolsManager.extractZip(archivePath, targetPath);
                resolve();
            }
            else if (ext === '.7z') {
                await zipToolsManager.extract7z(archivePath, targetPath);
                resolve();
            }
            else if (ext === '.tar') {
                await tar.extract({
                    file: archivePath,
                    cwd: targetPath,
                    filter: createTarSecurityFilter({ cwd: targetPath })
                });
                resolve();
            }
            else if (fileName.endsWith('.tar.gz') || fileName.endsWith('.tgz')) {
                await tar.extract({
                    file: archivePath,
                    cwd: targetPath,
                    gzip: true,
                    filter: createTarSecurityFilter({ cwd: targetPath })
                });
                resolve();
            }
            else if (fileName.endsWith('.tar.xz') || fileName.endsWith('.txz')) {
                const tempTarPath = archivePath.replace(/\.(tar\.xz|txz)$/, '.tar');
                try {
                    await new Promise((resolveXz, rejectXz) => {
                        const readStream = createReadStream(archivePath);
                        const writeStream = createWriteStream(tempTarPath);
                        const decompressStream = zlib.createGunzip();
                        readStream
                            .pipe(decompressStream)
                            .pipe(writeStream)
                            .on('finish', resolveXz)
                            .on('error', rejectXz);
                    });
                    await tar.extract({
                        file: tempTarPath,
                        cwd: targetPath,
                        filter: createTarSecurityFilter({ cwd: targetPath })
                    });
                    await fs.unlink(tempTarPath);
                    resolve();
                }
                catch (error) {
                    try {
                        await fs.unlink(tempTarPath);
                    }
                    catch { }
                    reject(error);
                }
            }
            else {
                reject(new Error(`不支持的压缩格式: ${ext}。支持的格式: .zip, .7z, .tar, .tar.gz, .tar.xz`));
            }
        }
        catch (error) {
            reject(error);
        }
    });
}
router.post('/read', authenticateToken, async (req, res) => {
    try {
        const { filePath, encoding = 'utf-8' } = req.body;
        if (!filePath) {
            return res.status(400).json({
                success: false,
                message: '缺少文件路径参数'
            });
        }
        const dataDir = path.join(process.cwd(), 'data');
        const fullPath = path.resolve(dataDir, filePath);
        if (!fullPath.startsWith(dataDir)) {
            return res.status(403).json({
                success: false,
                message: '访问被拒绝：文件路径超出允许范围'
            });
        }
        const content = await fs.readFile(fullPath, encoding);
        res.json({
            success: true,
            data: { content, encoding, filePath }
        });
    }
    catch (error) {
        if (error.code === 'ENOENT') {
            return res.status(404).json({
                success: false,
                message: '文件不存在'
            });
        }
        res.status(500).json({
            success: false,
            message: error.message
        });
    }
});
router.post('/write', authenticateToken, async (req, res) => {
    try {
        const { filePath, content, encoding = 'utf-8' } = req.body;
        if (!filePath || content === undefined) {
            return res.status(400).json({
                success: false,
                message: '缺少必要参数'
            });
        }
        const dataDir = path.join(process.cwd(), 'data');
        const fullPath = path.resolve(dataDir, filePath);
        if (!fullPath.startsWith(dataDir)) {
            return res.status(403).json({
                success: false,
                message: '访问被拒绝：文件路径超出允许范围'
            });
        }
        await fs.mkdir(path.dirname(fullPath), { recursive: true });
        await fs.writeFile(fullPath, content, encoding);
        res.json({
            success: true,
            message: '文件写入成功',
            data: { filePath, size: Buffer.byteLength(content, encoding) }
        });
    }
    catch (error) {
        res.status(500).json({
            success: false,
            message: error.message
        });
    }
});
router.delete('/delete', authenticateToken, async (req, res) => {
    try {
        const { filePath } = req.body;
        if (!filePath) {
            return res.status(400).json({
                success: false,
                message: '缺少文件路径参数'
            });
        }
        const dataDir = path.join(process.cwd(), 'data');
        const fullPath = path.resolve(dataDir, filePath);
        if (!fullPath.startsWith(dataDir)) {
            return res.status(403).json({
                success: false,
                message: '访问被拒绝：文件路径超出允许范围'
            });
        }
        await fs.unlink(fullPath);
        res.json({
            success: true,
            message: '文件删除成功',
            data: { filePath }
        });
    }
    catch (error) {
        if (error.code === 'ENOENT') {
            return res.status(404).json({
                success: false,
                message: '文件不存在'
            });
        }
        res.status(500).json({
            success: false,
            message: error.message
        });
    }
});
router.post('/mkdir', authenticateToken, async (req, res) => {
    try {
        const { dirPath, recursive = true } = req.body;
        if (!dirPath) {
            return res.status(400).json({
                success: false,
                message: '缺少目录路径参数'
            });
        }
        const fullPath = path.resolve(dirPath);
        try {
            const stats = await fs.stat(fullPath);
            if (stats.isDirectory()) {
                return res.json({
                    success: true,
                    message: '目录已存在',
                    data: { dirPath: fullPath }
                });
            }
            else {
                return res.status(409).json({
                    success: false,
                    message: '同名文件已存在，无法创建目录'
                });
            }
        }
        catch (error) {
            if (error.code === 'ENOENT') {
                await fs.mkdir(fullPath, { recursive });
                return res.json({
                    success: true,
                    message: '目录创建成功',
                    data: { dirPath: fullPath }
                });
            }
            throw error;
        }
    }
    catch (error) {
        res.status(500).json({
            success: false,
            message: error.message
        });
    }
});
router.delete('/rmdir', authenticateToken, async (req, res) => {
    try {
        const { dirPath, recursive = false } = req.body;
        if (!dirPath) {
            return res.status(400).json({
                success: false,
                message: '缺少目录路径参数'
            });
        }
        const dataDir = path.join(process.cwd(), 'data');
        const fullPath = path.resolve(dataDir, dirPath);
        if (!fullPath.startsWith(dataDir)) {
            return res.status(403).json({
                success: false,
                message: '访问被拒绝：目录路径超出允许范围'
            });
        }
        if (recursive) {
            await fs.rm(fullPath, { recursive: true, force: true });
        }
        else {
            await fs.rmdir(fullPath);
        }
        res.json({
            success: true,
            message: '目录删除成功',
            data: { dirPath }
        });
    }
    catch (error) {
        if (error.code === 'ENOENT') {
            return res.status(404).json({
                success: false,
                message: '目录不存在'
            });
        }
        res.status(500).json({
            success: false,
            message: error.message
        });
    }
});
router.post('/list', authenticateToken, async (req, res) => {
    try {
        const { dirPath = '', includeHidden = false } = req.body;
        const dataDir = path.join(process.cwd(), 'data');
        const fullPath = dirPath ? path.resolve(dataDir, dirPath) : dataDir;
        if (!fullPath.startsWith(dataDir)) {
            return res.status(403).json({
                success: false,
                message: '访问被拒绝：目录路径超出允许范围'
            });
        }
        const stats = await fs.stat(fullPath);
        if (!stats.isDirectory()) {
            return res.status(400).json({
                success: false,
                message: '指定路径不是目录'
            });
        }
        const items = await fs.readdir(fullPath);
        const files = [];
        for (const item of items) {
            if (!includeHidden && item.startsWith('.')) {
                continue;
            }
            const itemPath = path.join(fullPath, item);
            try {
                const itemStats = await fs.stat(itemPath);
                files.push({
                    name: item,
                    path: path.relative(dataDir, itemPath),
                    type: itemStats.isDirectory() ? 'directory' : 'file',
                    size: itemStats.size,
                    modified: itemStats.mtime.toISOString(),
                    created: itemStats.birthtime.toISOString()
                });
            }
            catch (error) {
                continue;
            }
        }
        res.json({
            success: true,
            data: files
        });
    }
    catch (error) {
        if (error.code === 'ENOENT') {
            return res.status(404).json({
                success: false,
                message: '目录不存在'
            });
        }
        res.status(500).json({
            success: false,
            message: error.message
        });
    }
});
router.post('/info', authenticateToken, async (req, res) => {
    try {
        const { path: itemPath } = req.body;
        if (!itemPath) {
            return res.status(400).json({
                success: false,
                message: '缺少路径参数'
            });
        }
        const dataDir = path.join(process.cwd(), 'data');
        const fullPath = path.resolve(dataDir, itemPath);
        if (!fullPath.startsWith(dataDir)) {
            return res.status(403).json({
                success: false,
                message: '访问被拒绝：路径超出允许范围'
            });
        }
        const stats = await fs.stat(fullPath);
        const info = {
            name: path.basename(fullPath),
            path: path.relative(dataDir, fullPath),
            type: stats.isDirectory() ? 'directory' : 'file',
            size: stats.size,
            modified: stats.mtime.toISOString(),
            created: stats.birthtime.toISOString(),
            accessed: stats.atime.toISOString(),
            permissions: stats.mode.toString(8),
            isReadable: true,
            isWritable: true
        };
        res.json({
            success: true,
            data: info
        });
    }
    catch (error) {
        if (error.code === 'ENOENT') {
            return res.status(404).json({
                success: false,
                message: '文件或目录不存在'
            });
        }
        res.status(500).json({
            success: false,
            message: error.message
        });
    }
});
router.post('/exists', authenticateToken, async (req, res) => {
    try {
        const { path: itemPath } = req.body;
        if (!itemPath) {
            return res.status(400).json({
                success: false,
                message: '缺少路径参数'
            });
        }
        const dataDir = path.join(process.cwd(), 'data');
        const fullPath = path.resolve(dataDir, itemPath);
        if (!fullPath.startsWith(dataDir)) {
            return res.status(403).json({
                success: false,
                message: '访问被拒绝：路径超出允许范围'
            });
        }
        try {
            const stats = await fs.stat(fullPath);
            res.json({
                success: true,
                data: {
                    exists: true,
                    type: stats.isDirectory() ? 'directory' : 'file'
                }
            });
        }
        catch (error) {
            if (error.code === 'ENOENT') {
                res.json({
                    success: true,
                    data: {
                        exists: false
                    }
                });
            }
            else {
                throw error;
            }
        }
    }
    catch (error) {
        res.status(500).json({
            success: false,
            message: error.message
        });
    }
});
router.post('/search', authenticateToken, async (req, res) => {
    try {
        const { pattern, searchPath = '', recursive = true } = req.body;
        if (!pattern) {
            return res.status(400).json({
                success: false,
                message: '缺少搜索模式参数'
            });
        }
        const dataDir = path.join(process.cwd(), 'data');
        const fullSearchPath = searchPath ? path.resolve(dataDir, searchPath) : dataDir;
        if (!fullSearchPath.startsWith(dataDir)) {
            return res.status(403).json({
                success: false,
                message: '访问被拒绝：搜索路径超出允许范围'
            });
        }
        const results = [];
        async function searchRecursive(currentPath) {
            try {
                const items = await fs.readdir(currentPath);
                for (const item of items) {
                    const itemPath = path.join(currentPath, item);
                    const stats = await fs.stat(itemPath);
                    const regex = new RegExp(pattern.replace(/\*/g, '.*').replace(/\?/g, '.'), 'i');
                    if (regex.test(item)) {
                        results.push({
                            name: item,
                            path: path.relative(dataDir, itemPath),
                            type: stats.isDirectory() ? 'directory' : 'file',
                            size: stats.size,
                            modified: stats.mtime.toISOString()
                        });
                    }
                    if (recursive && stats.isDirectory()) {
                        await searchRecursive(itemPath);
                    }
                }
            }
            catch (error) {
            }
        }
        await searchRecursive(fullSearchPath);
        res.json({
            success: true,
            data: results
        });
    }
    catch (error) {
        res.status(500).json({
            success: false,
            message: error.message
        });
    }
});
router.get('/tasks', authenticateToken, async (req, res) => {
    try {
        const tasks = taskManager.getAllTasks();
        res.json({
            status: 'success',
            data: tasks
        });
    }
    catch (error) {
        res.status(500).json({
            status: 'error',
            message: error.message
        });
    }
});
router.get('/tasks/active', authenticateToken, async (req, res) => {
    try {
        const tasks = taskManager.getActiveTasks();
        res.json({
            status: 'success',
            data: tasks
        });
    }
    catch (error) {
        res.status(500).json({
            status: 'error',
            message: error.message
        });
    }
});
router.get('/tasks/:taskId', authenticateToken, async (req, res) => {
    try {
        const { taskId } = req.params;
        const task = taskManager.getTask(taskId);
        if (!task) {
            return res.status(404).json({
                status: 'error',
                message: '任务不存在'
            });
        }
        res.json({
            status: 'success',
            data: task
        });
    }
    catch (error) {
        res.status(500).json({
            status: 'error',
            message: error.message
        });
    }
});
router.delete('/tasks/:taskId', authenticateToken, async (req, res) => {
    try {
        const { taskId } = req.params;
        const task = taskManager.getTask(taskId);
        if (!task) {
            return res.status(404).json({
                status: 'error',
                message: '任务不存在'
            });
        }
        if (task.status === 'running') {
            return res.status(400).json({
                status: 'error',
                message: '无法删除正在运行的任务'
            });
        }
        taskManager.deleteTask(taskId);
        res.json({
            status: 'success',
            message: '任务已删除'
        });
    }
    catch (error) {
        res.status(500).json({
            status: 'error',
            message: error.message
        });
    }
});
router.get('/download-task/:taskId', authenticateToken, async (req, res) => {
    try {
        const { taskId } = req.params;
        const task = taskManager.getTask(taskId);
        if (!task) {
            return res.status(404).json({
                status: 'error',
                message: '任务不存在'
            });
        }
        if (task.type !== 'download') {
            return res.status(400).json({
                status: 'error',
                message: '任务类型不正确'
            });
        }
        const { filePath, fileName, fileSize } = task.data;
        taskManager.updateTask(taskId, {
            status: 'running',
            message: '开始下载'
        });
        const encodedFileName = encodeURIComponent(fileName);
        const asciiFileName = fileName.replace(/[^\x00-\x7F]/g, '_');
        res.setHeader('Content-Disposition', `attachment; filename="${asciiFileName}"; filename*=UTF-8''${encodedFileName}`);
        res.setHeader('Content-Type', 'application/octet-stream');
        res.setHeader('Content-Length', fileSize.toString());
        const fileStream = createReadStream(filePath);
        let downloadedBytes = 0;
        fileStream.on('data', (chunk) => {
            downloadedBytes += chunk.length;
            const progress = Math.round((downloadedBytes / fileSize) * 100);
            taskManager.updateTask(taskId, {
                progress,
                message: `下载中... ${progress}%`
            });
        });
        fileStream.on('end', () => {
            taskManager.updateTask(taskId, {
                status: 'completed',
                progress: 100,
                message: '下载完成'
            });
        });
        fileStream.on('error', (error) => {
            taskManager.updateTask(taskId, {
                status: 'failed',
                message: `下载失败: ${error.message}`
            });
        });
        res.on('close', () => {
            if (!res.headersSent) {
                taskManager.updateTask(taskId, {
                    status: 'failed',
                    message: '下载被中断'
                });
            }
        });
        fileStream.pipe(res);
    }
    catch (error) {
        res.status(500).json({
            status: 'error',
            message: error.message
        });
    }
});
router.get('/permissions', authenticateToken, async (req, res) => {
    try {
        if (process.platform === 'win32') {
            return res.status(400).json({
                status: 'error',
                message: 'Windows系统不支持此功能'
            });
        }
        const { path: filePath } = req.query;
        if (!isValidPath(filePath)) {
            return res.status(400).json({
                status: 'error',
                message: '无效的路径'
            });
        }
        const fixedFilePath = fixWindowsPath(filePath);
        try {
            await fs.access(fixedFilePath);
        }
        catch (error) {
            return res.status(404).json({
                status: 'error',
                message: '文件或目录不存在'
            });
        }
        const stats = await fs.stat(fixedFilePath);
        const { stdout } = await execAsync(`stat -c "%a %U %G" "${fixedFilePath}"`);
        const [octalPermissions, owner, group] = stdout.trim().split(' ');
        const parseOctalPermissions = (octal) => {
            const ownerPerms = parseInt(octal[0]);
            const groupPerms = parseInt(octal[1]);
            const othersPerms = parseInt(octal[2]);
            const parsePermBits = (bits) => ({
                read: (bits & 4) !== 0,
                write: (bits & 2) !== 0,
                execute: (bits & 1) !== 0
            });
            return {
                owner: parsePermBits(ownerPerms),
                group: parsePermBits(groupPerms),
                others: parsePermBits(othersPerms)
            };
        };
        res.json({
            status: 'success',
            data: {
                owner: owner,
                group: group,
                permissions: parseOctalPermissions(octalPermissions),
                octal: octalPermissions,
                isDirectory: stats.isDirectory(),
                size: stats.size,
                modified: stats.mtime.toISOString()
            }
        });
    }
    catch (error) {
        res.status(500).json({
            status: 'error',
            message: error.message
        });
    }
});
router.post('/permissions', authenticateToken, async (req, res) => {
    try {
        if (process.platform === 'win32') {
            return res.status(400).json({
                status: 'error',
                message: 'Windows系统不支持此功能'
            });
        }
        const { path: filePath, permissions, recursive } = req.body;
        if (!isValidPath(filePath)) {
            return res.status(400).json({
                status: 'error',
                message: '无效的路径'
            });
        }
        if (!permissions) {
            return res.status(400).json({
                status: 'error',
                message: '权限参数不能为空'
            });
        }
        const fixedFilePath = fixWindowsPath(filePath);
        try {
            await fs.access(fixedFilePath);
        }
        catch (error) {
            return res.status(404).json({
                status: 'error',
                message: '文件或目录不存在'
            });
        }
        let octalPermissions;
        if (typeof permissions === 'string' && /^[0-7]{3}$/.test(permissions)) {
            octalPermissions = permissions;
        }
        else if (typeof permissions === 'object' && permissions.owner && permissions.group && permissions.others) {
            const convertPermBits = (perms) => {
                return (perms.read ? 4 : 0) + (perms.write ? 2 : 0) + (perms.execute ? 1 : 0);
            };
            const ownerBits = convertPermBits(permissions.owner);
            const groupBits = convertPermBits(permissions.group);
            const othersBits = convertPermBits(permissions.others);
            octalPermissions = `${ownerBits}${groupBits}${othersBits}`;
        }
        else {
            return res.status(400).json({
                status: 'error',
                message: '权限格式无效'
            });
        }
        const chmodCommand = recursive
            ? `chmod -R ${octalPermissions} "${fixedFilePath}"`
            : `chmod ${octalPermissions} "${fixedFilePath}"`;
        await execAsync(chmodCommand);
        res.json({
            status: 'success',
            message: '权限修改成功'
        });
    }
    catch (error) {
        res.status(500).json({
            status: 'error',
            message: error.message
        });
    }
});
router.post('/ownership', authenticateToken, async (req, res) => {
    try {
        if (process.platform === 'win32') {
            return res.status(400).json({
                status: 'error',
                message: 'Windows系统不支持此功能'
            });
        }
        const { path: filePath, owner, group, recursive } = req.body;
        if (!isValidPath(filePath)) {
            return res.status(400).json({
                status: 'error',
                message: '无效的路径'
            });
        }
        if (!owner && !group) {
            return res.status(400).json({
                status: 'error',
                message: '必须指定所有者或组'
            });
        }
        const fixedFilePath = fixWindowsPath(filePath);
        try {
            await fs.access(fixedFilePath);
        }
        catch (error) {
            return res.status(404).json({
                status: 'error',
                message: '文件或目录不存在'
            });
        }
        let ownerGroup = '';
        if (owner && group) {
            ownerGroup = `${owner}:${group}`;
        }
        else if (owner) {
            ownerGroup = owner;
        }
        else if (group) {
            ownerGroup = `:${group}`;
        }
        const chownCommand = recursive
            ? `chown -R ${ownerGroup} "${fixedFilePath}"`
            : `chown ${ownerGroup} "${fixedFilePath}"`;
        await execAsync(chownCommand);
        res.json({
            status: 'success',
            message: '所有者修改成功'
        });
    }
    catch (error) {
        res.status(500).json({
            status: 'error',
            message: error.message
        });
    }
});
router.get('/drives', authenticateToken, async (req, res) => {
    try {
        const drives = [];
        if (process.platform === 'win32') {
            try {
                const { stdout } = await execAsync('wmic logicaldisk get size,freespace,caption,description,drivetype');
                const lines = stdout.split('\n').filter(line => line.trim() && !line.includes('Caption'));
                for (const line of lines) {
                    const parts = line.trim().split(/\s+/);
                    if (parts.length >= 5) {
                        const caption = parts[0];
                        const driveType = parseInt(parts[2]);
                        if (caption && caption.match(/^[A-Z]:$/)) {
                            let type = 'unknown';
                            switch (driveType) {
                                case 2:
                                    type = 'removable';
                                    break;
                                case 3:
                                    type = 'fixed';
                                    break;
                                case 4:
                                    type = 'network';
                                    break;
                                case 5:
                                    type = 'cdrom';
                                    break;
                                default:
                                    type = 'unknown';
                                    break;
                            }
                            drives.push({
                                label: `${caption}\\`,
                                value: `${caption}\\`,
                                type: type
                            });
                        }
                    }
                }
            }
            catch (error) {
                console.warn('wmic命令失败，使用备用方法获取盘符:', error);
                const commonDrives = ['C:', 'D:', 'E:', 'F:', 'G:', 'H:', 'I:', 'J:', 'K:', 'L:', 'M:', 'N:', 'O:', 'P:', 'Q:', 'R:', 'S:', 'T:', 'U:', 'V:', 'W:', 'X:', 'Y:', 'Z:'];
                for (const drive of commonDrives) {
                    try {
                        const drivePath = `${drive}\\`;
                        await fs.access(drivePath);
                        drives.push({
                            label: drivePath,
                            value: drivePath,
                            type: 'fixed'
                        });
                    }
                    catch (error) {
                    }
                }
            }
        }
        else {
            drives.push({
                label: '根目录 (/)',
                value: '/',
                type: 'fixed'
            });
        }
        if (drives.length === 0) {
            const cwd = process.cwd();
            const rootPath = process.platform === 'win32' ? path.parse(cwd).root : '/';
            drives.push({
                label: process.platform === 'win32' ? rootPath : '根目录 (/)',
                value: rootPath,
                type: 'fixed'
            });
        }
        res.json({
            status: 'success',
            data: drives
        });
    }
    catch (error) {
        console.error('获取盘符失败:', error);
        res.status(500).json({
            status: 'error',
            message: error.message || '获取盘符失败'
        });
    }
});
const getFavoritesPath = () => {
    const baseDir = process.cwd();
    const possiblePaths = [
        path.join(baseDir, 'data', 'favorites.json'),
        path.join(baseDir, 'server', 'data', 'favorites.json'),
    ];
    for (const favPath of possiblePaths) {
        const dir = path.dirname(favPath);
        if (fsSync.existsSync(dir)) {
            return favPath;
        }
    }
    return possiblePaths[0];
};
const readFavorites = async () => {
    const favPath = getFavoritesPath();
    try {
        const data = await fs.readFile(favPath, 'utf-8');
        return JSON.parse(data);
    }
    catch (error) {
        return [];
    }
};
const saveFavorites = async (favorites) => {
    const favPath = getFavoritesPath();
    const dir = path.dirname(favPath);
    await fs.mkdir(dir, { recursive: true });
    await fs.writeFile(favPath, JSON.stringify(favorites, null, 2), 'utf-8');
};
router.get('/favorites', authenticateToken, async (req, res) => {
    try {
        const favorites = await readFavorites();
        const validFavorites = [];
        const favoritesWithInfo = [];
        for (const favPath of favorites) {
            try {
                const stats = await fs.stat(favPath);
                validFavorites.push(favPath);
                favoritesWithInfo.push({
                    path: favPath,
                    name: path.basename(favPath),
                    type: stats.isDirectory() ? 'directory' : 'file',
                    exists: true
                });
            }
            catch (error) {
                favoritesWithInfo.push({
                    path: favPath,
                    name: path.basename(favPath),
                    type: 'file',
                    exists: false
                });
            }
        }
        res.json({
            status: 'success',
            data: favoritesWithInfo
        });
    }
    catch (error) {
        console.error('获取收藏列表失败:', error);
        res.status(500).json({
            status: 'error',
            message: error.message || '获取收藏列表失败'
        });
    }
});
router.post('/favorites', authenticateToken, async (req, res) => {
    try {
        const { path: filePath } = req.body;
        if (!filePath) {
            return res.status(400).json({
                status: 'error',
                message: '缺少文件路径参数'
            });
        }
        try {
            await fs.access(filePath);
        }
        catch (error) {
            return res.status(404).json({
                status: 'error',
                message: '文件或文件夹不存在'
            });
        }
        const favorites = await readFavorites();
        if (favorites.includes(filePath)) {
            return res.status(400).json({
                status: 'error',
                message: '该路径已在收藏列表中'
            });
        }
        favorites.push(filePath);
        await saveFavorites(favorites);
        res.json({
            status: 'success',
            message: '添加收藏成功',
            data: { path: filePath }
        });
    }
    catch (error) {
        console.error('添加收藏失败:', error);
        res.status(500).json({
            status: 'error',
            message: error.message || '添加收藏失败'
        });
    }
});
router.delete('/favorites', authenticateToken, async (req, res) => {
    try {
        const { path: filePath } = req.body;
        if (!filePath) {
            return res.status(400).json({
                status: 'error',
                message: '缺少文件路径参数'
            });
        }
        const favorites = await readFavorites();
        const newFavorites = favorites.filter(fav => fav !== filePath);
        if (newFavorites.length === favorites.length) {
            return res.status(404).json({
                status: 'error',
                message: '该路径不在收藏列表中'
            });
        }
        await saveFavorites(newFavorites);
        res.json({
            status: 'success',
            message: '移除收藏成功',
            data: { path: filePath }
        });
    }
    catch (error) {
        console.error('移除收藏失败:', error);
        res.status(500).json({
            status: 'error',
            message: error.message || '移除收藏失败'
        });
    }
});
router.get('/favorites/check', authenticateToken, async (req, res) => {
    try {
        const { path: filePath } = req.query;
        if (!filePath || typeof filePath !== 'string') {
            return res.status(400).json({
                status: 'error',
                message: '缺少文件路径参数'
            });
        }
        const favorites = await readFavorites();
        const isFavorited = favorites.includes(filePath);
        res.json({
            status: 'success',
            data: { isFavorited }
        });
    }
    catch (error) {
        console.error('检查收藏状态失败:', error);
        res.status(500).json({
            status: 'error',
            message: error.message || '检查收藏状态失败'
        });
    }
});
export default router;
