// 引入Passport模块，它是Node.js中用于处理用户认证的常用中间件，可方便地集成多种认证策略
const passport = require('passport');
// 引入Passport的本地策略（LocalStrategy），通常用于基于用户名和密码的认证方式，比如常见的表单登录验证
const LocalStrategy = require('passport-local').Strategy;
// 引入Passport的HTTP Bearer策略（Strategy），常用于基于令牌（Token）的认证，比如JSON Web Tokens（JWT）认证场景
const Strategy = require('passport-http-bearer').Strategy;

// 引入jsonwebtoken模块，用于处理JSON Web Tokens（JWT）的生成、验证等操作
var jwt = require("jsonwebtoken");
// 引入Lodash库，它提供了很多实用的工具函数，用于处理数据、对象等，此处虽未明确体现具体使用的功能，但可能用于辅助数据操作等
var _ = require('lodash');
// 引入配置模块（config），并获取名为"jwt_config"的配置项，通常用于获取与JWT相关的配置信息，如密钥、过期时间等
var jwt_config = require("config").get("jwt_config");

// 通过登录函数初始化
/**
 * 初始化passport框架
 * 此函数的主要作用是配置Passport的不同认证策略，并将Passport初始化到给定的应用程序（app）中，
 * 同时提供回调机制，方便在初始化完成后执行额外的操作。
 * 
 * @param {[type]} app       全局应用程序，通常是Express等Web框架创建的应用实例，用于挂载中间件等操作。
 * @param {[type]} loginFunc 登录函数，用于执行具体的用户名和密码验证逻辑，比如查询数据库验证用户输入的用户名和密码是否匹配等操作。
 * @param {Function} callback 回调函数，在Passport初始化及相关策略配置完成后被调用，可用于执行后续自定义的初始化步骤等操作（可选参数）。
 */
module.exports.setup = function (app, loginFunc, callback) {
    // 用户名密码 登录策略
    // 使用Passport的LocalStrategy配置基于用户名和密码的认证策略
    passport.use(new LocalStrategy(
        // 定义验证逻辑函数，接收用户名、密码以及回调函数（done）作为参数
        function (username, password, done) {
            // 如果没有传入登录验证函数（loginFunc），则直接通过回调函数（done）返回错误信息，表示登录验证函数未设置
            if (!loginFunc) return done("登录验证函数未设置");

            // 调用传入的登录验证函数（loginFunc），传入用户名和密码进行验证，验证完成后会在回调函数中返回结果
            loginFunc(username, password, function (err, user) {
                // 如果验证过程中出现错误，通过回调函数（done）返回错误信息给Passport，告知认证失败原因
                if (err) return done(err);
                // 如果验证成功，通过回调函数（done）返回用户信息（通常是包含用户相关数据的对象）给Passport，表示认证成功
                return done(null, user);
            });
        }
    ));

    // token验证策略
    // 使用Passport的HTTP Bearer策略配置基于令牌（Token）的认证策略
    passport.use(new Strategy(
        // 定义验证逻辑函数，接收令牌（token）以及回调函数（done）作为参数
        function (token, done) {
            // 使用jsonwebtoken模块的verify方法验证传入的令牌（token）是否有效，
            // 传入令牌、配置中的密钥（jwt_config.get("secretKey")）以及验证回调函数，验证回调函数接收验证结果（err）和解析后的令牌数据（decode）作为参数
            jwt.verify(token, jwt_config.get("secretKey"), function (err, decode) {
                // 如果验证过程中出现错误，通过回调函数（done）返回错误信息给Passport，表示令牌验证失败
                if (err) { return done("验证错误"); }
                // 如果验证成功，通过回调函数（done）返回解析后的令牌数据（decode）给Passport，表示令牌验证通过
                return done(null, decode);
            });
        }
    ));

    // 初始化passport模块
    // 将Passport中间件初始化到应用程序（app）中，使其能够在后续的请求处理流程中生效，用于处理各种认证相关的操作
    app.use(passport.initialize());

    // 如果传入了回调函数（callback），则执行该回调函数，用于在初始化完成后执行额外的自定义操作
    if (callback) callback();
};

/**
 * 登录验证逻辑
 * 此函数主要用于处理基于用户名和密码的登录请求验证逻辑，通过调用Passport的认证中间件进行验证，
 * 根据验证结果生成相应的响应信息返回给客户端，包括在验证成功时生成并返回JWT令牌等操作。
 * 
 * @param {[type]} req  请求对象，包含了客户端发送的请求相关信息，如请求头、请求体等内容，常用于获取用户名、密码等登录信息。
 * @param {[type]} res  响应对象，用于向客户端发送响应信息，如返回登录成功或失败的消息、生成的令牌等内容。
 * @param {Function} next 传递事件函数，用于将请求传递给下一个中间件继续处理（在Express等框架的中间件链机制中使用），此处未明确体现后续传递逻辑，但遵循中间件规范保留该参数。
 */
module.exports.login = function (req, res, next) {
    // 使用Passport的authenticate方法，传入'local'表示采用之前配置的基于用户名和密码的本地认证策略进行认证，
    // 同时传入一个回调函数用于处理认证结果，该回调函数接收认证过程中的错误（err）、认证通过后的用户信息（user）以及额外的提示信息（info）作为参数
    passport.authenticate('local', function (err, user, info) {
        // 如果认证过程中出现错误，使用响应对象（res）的sendResult方法（这里假设是自定义的用于统一返回响应格式的方法）向客户端发送包含错误状态码（400表示请求错误）和错误信息的响应
        if (err) return res.sendResult(null, 400, err);
        // 如果没有获取到用户信息（即认证失败，未找到匹配的用户），同样使用响应对象的sendResult方法向客户端发送包含错误状态码和相应错误提示信息的响应
        if (!user) return res.sendResult(null, 400, "参数错误");

        // 获取角色信息
        // 使用jsonwebtoken模块的sign方法生成JWT令牌，传入包含用户ID（uid）和角色ID（rid）的对象、配置中的密钥以及令牌过期时间等配置信息，生成一个有效的JWT令牌
        var token = jwt.sign({"uid": user.id, "rid": user.rid}, jwt_config.get("secretKey"), {"expiresIn": jwt_config.get("expiresIn")});
        // 将生成的令牌添加到用户对象中，并添加"Bearer "前缀，符合Bearer Token的规范格式，方便后续在请求头中传递和验证
        user.token = "Bearer " + token;
        // 使用响应对象的sendResult方法向客户端发送包含用户信息、成功状态码（200）以及登录成功消息的响应，表示登录操作成功完成，并返回相关用户数据和令牌
        return res.sendResult(user, 200, '登录成功');
    })(req, res, next);
};

/**
 * token验证函数
 * 此函数用于处理基于令牌（Token）的验证逻辑，通过调用Passport的相应认证中间件验证传入的令牌是否有效，
 * 根据验证结果设置请求对象（req）中的用户信息，并决定是否将请求传递给下一个中间件继续处理。
 * 
 * @param {[type]} req  请求对象，包含了客户端发送的请求相关信息以及在验证通过后可用于存储用户相关数据等内容，方便后续中间件使用验证后的用户信息进行业务逻辑处理。
 * @param {[type]} res  响应对象，用于向客户端发送响应信息，如在令牌验证失败时返回相应的错误消息等内容。
 * @param {Function} next 传递事件函数，用于将请求传递给下一个中间件继续处理，在令牌验证通过后会调用该函数将请求传递下去，让后续中间件继续执行相应的业务逻辑。
 */
module.exports.tokenAuth = function (req, res, next) {
    // 使用Passport的authenticate方法，传入'bearer'表示采用之前配置的基于HTTP Bearer的认证策略进行令牌验证，
    // 同时传入{ session: false }表示不依赖会话（Session）进行验证（常用于无状态的API认证场景，如基于JWT的认证），
    // 并传入一个回调函数用于处理验证结果，该回调函数接收验证过程中的错误（err）以及验证通过后的令牌数据（tokenData）作为参数
    passport.authenticate('bearer', { session: false }, function (err, tokenData) {
        // 如果验证过程中出现错误，使用响应对象（res）的sendResult方法向客户端发送包含错误状态码（400表示请求错误）和相应错误提示信息（无效token）的响应
        if (err) return res.sendResult(null, 400, '无效token');
        // 如果没有获取到有效的令牌数据（即令牌验证失败），同样使用响应对象的sendResult方法向客户端发送包含错误状态码和相应错误提示信息的响应
        if (!tokenData) return res.sendResult(null, 400, '无效token');
        // 如果令牌验证通过，在请求对象（req）中创建一个空的用户信息对象（userInfo），用于存储从令牌中解析出的相关用户信息，方便后续中间件使用
        req.userInfo = {};
        // 将从令牌数据（tokenData）中解析出的用户ID（uid）存储到请求对象的用户信息对象中
        req.userInfo.uid = tokenData["uid"];
        // 将从令牌数据（tokenData）中解析出的角色ID（rid）存储到请求对象的用户信息对象中
        req.userInfo.rid = tokenData["rid"];
        // 调用传递事件函数（next），将请求传递给下一个中间件继续处理，表示令牌验证通过，后续中间件可以基于请求对象中的用户信息进行相应的业务逻辑操作
        next();
    })(req, res, next);
}