const logger = require('../utils/logger');
const db = require('../db');

/**
 * 根据请求方法和 URL 获取操作说明
 */
const getActionDescription = (method, url) => {
    // 移除查询参数
    const path = url.split('?')[0];

    // --- Auth ---
    if (path === '/api/auth/login' && method === 'POST') return '用户登录';
    if (path === '/api/auth/register' && method === 'POST') return '用户注册';
    if (path === '/api/auth/send-code' && method === 'POST') return '发送验证码';
    if (path === '/api/auth/reset-password' && method === 'POST') return '重置密码';

    // --- Achievements ---
    if (path === '/api/achievements' && method === 'POST') return '上传成果';
    if (path === '/api/achievements' && method === 'GET') {
        if (url.includes('export_action=true')) return '导出用户成果数据';
        return '查看成果列表';
    }
    if (path === '/api/achievements/export/csv' && method === 'GET') return '导出成果(CSV)';
    if (path === '/api/achievements/export/zip' && method === 'GET') return '导出成果(ZIP)';
    if (path === '/api/achievements/validate' && method === 'GET') return '数据校验';
    
    // 动态路径匹配
    if (path.match(/^\/api\/achievements\/audit\/\d+$/) && method === 'POST') return '审核成果';
    if (path.match(/^\/api\/achievements\/\d+\/attachments\/zip$/) && method === 'GET') return '下载成果附件包';
    if (path.match(/^\/api\/achievements\/attachments\/\d+\/download$/) && method === 'GET') return '下载单个附件';
    if (path.match(/^\/api\/achievements\/\d+$/)) {
        if (method === 'GET') return '查看成果详情';
        if (method === 'PUT') return '修改成果';
        if (method === 'DELETE') return '删除成果';
    }

    // --- Users ---
    if (path === '/api/users' && method === 'POST') return '创建用户';
    if (path === '/api/users' && method === 'GET') return '查看用户列表';
    if (path === '/api/users/search' && method === 'GET') return '搜索用户';
    if (path === '/api/users/pending-list' && method === 'GET') return '查看待定用户';
    if (path.match(/^\/api\/users\/\d+$/)) {
        if (method === 'PUT') return '修改用户';
        if (method === 'DELETE') return '删除用户';
    }

    // --- Dictionaries ---
    if (path === '/api/dictionaries/departments' && method === 'GET') return '查看部门列表(公开)';
    if (path.match(/^\/api\/dictionaries\/[^/]+$/)) {
        if (method === 'GET') return '查看字典列表';
        if (method === 'POST') return '添加字典项';
    }
    if (path.match(/^\/api\/dictionaries\/[^/]+\/\d+$/)) {
        if (method === 'PUT') return '修改字典项';
        if (method === 'DELETE') return '删除字典项';
    }

    // --- Notifications ---
    if (path === '/api/notifications' && method === 'GET') return '查看通知列表';
    if (path === '/api/notifications' && method === 'POST') return '发布通知';
    if (path.match(/^\/api\/notifications\/attachments\/\d+\/download$/) && method === 'GET') return '下载通知附件';
    if (path.match(/^\/api\/notifications\/\d+$/)) {
        if (method === 'GET') return '查看通知详情';
        if (method === 'DELETE') return '删除通知';
    }

    // --- Statistics ---
    if (path === '/api/statistics' && method === 'GET') return '查看统计数据';

    // --- Logs ---
    if (path === '/api/logs' && method === 'GET') return '查看系统日志';

    // 默认
    return null;
};

/**
 * 全局审计日志中间件
 * 记录所有 API 请求的操作人、IP、方法、URL 和 结果状态
 */
const auditLogger = (req, res, next) => {
    // 记录请求开始时间
    const start = Date.now();

    // 监听请求完成事件
    res.on('finish', async () => {
        const duration = Date.now() - start;
        const clientIp = req.headers['x-forwarded-for'] || req.socket.remoteAddress;
        const method = req.method;
        const url = req.originalUrl || req.url;
        const status = res.statusCode;
        
        // 忽略非 API 请求 (虽然通常挂载在 /api 下，但双重保险)
        if (!url.startsWith('/api/')) return;

        // 忽略 OPTIONS 预检请求，避免日志刷屏
        if (method === 'OPTIONS') return;

        // URL 脱敏处理：隐藏 token, password, code 等敏感参数
        let safeUrl = url;
        try {
            safeUrl = safeUrl.replace(/([?&])(token|password|code)=([^&]+)/gi, '$1$2=***');
        } catch (e) {
            // 忽略正则错误，保持原样
        }

        let userInfo = 'Guest';
        let userId = null;
        let username = null;
        let realName = null;

        // 1. 尝试获取已登录用户信息
        // 注意：req.user 由 verifyToken 中间件挂载。
        // 由于我们在 res 'finish' 事件中读取，此时路由处理已完成，req.user 应该已存在（如果通过了认证）。
        if (req.user) {
            userInfo = `${req.user.real_name || req.user.username} (ID:${req.user.id})`;
            userId = req.user.id;
            username = req.user.username;
            realName = req.user.real_name;
        } 
        // 2. 对于未登录的关键接口，尝试从 body 中提取身份信息
        else if (req.body) {
            if (url.includes('/login')) {
                userInfo = `[尝试登录: ${req.body.username || '未知'}]`;
                username = req.body.username;
            } else if (url.includes('/register')) {
                userInfo = `[尝试注册: ${req.body.username || '未知'}]`;
                username = req.body.username;
            } else if (url.includes('/send-code')) {
                userInfo = `[请求验证码: ${req.body.phoneNumber || '未知'}]`;
                username = req.body.phoneNumber;
            } else if (url.includes('/reset-password')) {
                userInfo = `[重置密码: ${req.body.phoneNumber || '未知'}]`;
                username = req.body.phoneNumber;
            }
        }

        // 获取操作说明
        const description = getActionDescription(method, url);

        // 构建日志消息
        const logMessage = `[AUDIT] IP:${clientIp} | User:${userInfo} | ${method} ${safeUrl} | Action:${description || 'Unknown'} | Status:${status} | Time:${duration}ms`;

        // 根据状态码记录不同级别的日志
        if (status >= 500) {
            logger.error(logMessage);
        } else if (status >= 400) {
            logger.warn(logMessage);
        } else {
            logger.info(logMessage);
        }

        // 写入数据库
        try {
            await db.query(
                'INSERT INTO audit_logs (user_id, username, real_name, ip_address, method, url, description, status, duration) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)',
                [userId, username, realName, clientIp, method, safeUrl, description, status, duration]
            );
        } catch (err) {
            // 数据库写入失败不应影响主流程，仅记录错误
            console.error('审计日志写入数据库失败:', err);
        }
    });

    next();
};

module.exports = auditLogger;
