'use strict';

/**
 * 项目账号数据模型
 *
 * @author CaiAoLin
 * @date 2017/11/16
 * @version
 */

// 加密类
const crypto = require('crypto');
const SSO = require('../lib/sso');
const SMS = require('../lib/sms');
module.exports = app => {

    class ProjectAccount extends app.BaseService {

        /**
         * 构造函数
         *
         * @param {Object} ctx - egg全局变量
         * @return {void}
         */
        constructor(ctx) {
            super(ctx);
            this.tableName = 'project_account';
        }

        /**
         * 数据验证规则
         *
         * @param {String} scene - 场景
         * @return {Object} - 返回数据
         */
        rule(scene) {
            let rule = {};
            switch (scene) {
                case 'login':
                    rule = {
                        account: { type: 'string', required: true, min: 2 },
                        project_password: { type: 'string', required: true, min: 4 },
                        project: { type: 'string', required: true, min: 13 },
                    };
                    break;
                case 'ssoLogin':
                    rule = {
                        username: { type: 'string', required: true, min: 2 },
                        password: { type: 'string', required: true, min: 4 },
                    };
                    break;
                case 'profileBase':
                    rule = {
                        name: { type: 'string', allowEmpty: true, max: 10 },
                        company: { type: 'string', allowEmpty: true, max: 30 },
                        role: { type: 'string', allowEmpty: true, max: 10 },
                        mobile: { type: 'mobile', allowEmpty: true },
                        telephone: { type: 'string', allowEmpty: true, max: 12 },
                    };
                    break;
                case 'modifyPassword':
                    rule = {
                        password: { type: 'password', required: true, min: 6 },
                        new_password: { type: 'password', required: true, min: 6 },
                        confirm_password: { type: 'password', required: true, min: 6, compare: 'new_password' },
                    };
                    break;
                case 'bindMobile':
                    rule = {
                        code: { type: 'string', required: true, min: 6 },
                        auth_mobile: { type: 'mobile', allowEmpty: false },
                    };
                    break;
                default:
                    break;
            }

            return rule;
        }

        /**
         * 账号登录
         *
         * @param {Object} data - 表单post数据
         * @param {Number} loginType - 登录类型 1 | 2
         * @return {Boolean} - 返回登录结果
         */
        async accountLogin(data, loginType) {
            let result = false;
            try {
                // 验证数据
                const scene = loginType === 1 ? 'ssoLogin' : 'login';
                const rule = this.rule(scene);
                this.ctx.validate(rule, data);

                let accountData = {};
                let projectInfo = {};
                let projectList = [];
                let permission = '';
                if (loginType === 2) {
                    // 查找项目数据
                    const projectData = await this.ctx.service.project.getProjectByCode(data.project.toString());
                    if (projectData === null) {
                        throw '不存在项目数据';
                    }
                    projectInfo = {
                        id: projectData.id,
                        name: projectData.name,
                        userAccount: projectData.user_account,
                    };

                    // 查找对应数据
                    accountData = await this.db.get(this.tableName, {
                        account: data.account,
                        project_id: projectData.id,
                        enable: 1,
                    });

                    if (accountData === null) {
                        throw '不存在对应用户数据';
                    }

                    projectList = await this.getProjectInfoByAccount(data.account);

                    // 判断密码
                    if (accountData.is_admin === 1) {
                        // 管理员则用sso通道判断
                        const sso = new SSO(this.ctx);
                        result = await sso.loginValid(data.account, data.project_password.toString());
                        permission = 'all';
                    } else {
                        // 加密密码
                        const encryptPassword = crypto.createHmac('sha1', data.account).update(data.project_password)
                            .digest().toString('base64');
                        result = encryptPassword === accountData.password;
                        permission = accountData.permission;
                    }
                } else {
                    // sso登录（演示版）
                    const sso = new SSO(this.ctx);
                    result = await sso.loginValid(data.username, data.password.toString());
                    accountData.account = data.username;
                    accountData.id = sso.accountID;
                }


                // 如果成功则更新登录时间
                if (result) {
                    const currentTime = new Date().getTime() / 1000;
                    if (loginType === 2) {
                        const updateData = {
                            last_login: currentTime,
                        };
                        await this.update(updateData, { id: accountData.id });
                    }
                    // 加密token
                    const sessionToken = crypto.createHmac('sha1', currentTime + '').update(accountData.account)
                        .digest().toString('base64');
                    // 存入session
                    this.ctx.session.sessionUser = {
                        account: accountData.account,
                        name: accountData.name,
                        accountId: accountData.id,
                        loginTime: currentTime,
                        sessionToken,
                        loginType,
                        permission,
                    };
                    this.ctx.session.sessionProject = projectInfo;
                    this.ctx.session.sessionProjectList = projectList;
                }
            } catch (error) {
                console.log(error);
                result = false;
            }

            return result;
        }

        /**
         * 根据项目id获取用户列表
         *
         * @param {Number} projectId - 项目id
         * @return {Array} - 返回用户数据
         */
        async getAccountByProjectId(projectId) {
            const condition = {
                columns: ['id', 'account', 'name', 'company', 'role', 'mobile', 'telephone', 'enable', 'permission'],
                where: { project_id: projectId, is_admin: 0 },
            };
            const accountList = await this.getAllDataByCondition(condition);

            return accountList;
        }

        /**
         * 停用/启用
         *
         * @param {Number} accountId - 账号id
         * @return {Boolean} - 返回操作结果
         */
        async enableAccount(accountId) {
            let result = false;
            const accountData = await this.getDataByCondition({ id: accountId });
            if (accountData === null) {
                return result;
            }

            const changeStatus = accountData.enable === 1 ? 0 : 1;
            result = await this.update({ enable: changeStatus }, { id: accountId });

            return result;
        }

        /**
         * 根据账号id查找对应的项目数据
         *
         * @param {Number} account - 账号
         * @return {Array} - 返回数据
         */
        async getProjectInfoByAccount(account) {
            let column = ['p.name', 'p.id', 'p.user_account'];
            column = column.join(',');
            const sql = 'SELECT ' + column + ' FROM ' +
                '?? AS pa ' +
                'LEFT JOIN ?? AS p ' +
                'ON pa.`project_id` = p.`id` ' +
                'WHERE pa.`account` = ? ' +
                'GROUP BY pa.`project_id`;';
            const sqlParam = [this.tableName, this.ctx.service.project.tableName, account];
            const projectInfo = await this.db.query(sql, sqlParam);

            return projectInfo;
        }

        /**
         * 根据项目Id，用户名查找用户数据
         * @param projectId
         * @param name
         * @returns {Promise<*>}
         */
        async getAccountInfoByName(projectId, name) {
            this.initSqlBuilder();
            this.sqlBuilder.columns = ['id', 'name', 'company', 'role'];
            this.sqlBuilder.setAndWhere('project_id', {
                operate: '=',
                value: projectId,
            });
            this.sqlBuilder.setAndWhere('name', {
                operate: 'like',
                value: this.db.escape('%' + name + '%'),
            });

            const [sql, sqlParam] = this.sqlBuilder.build(this.tableName, 'select');
            const info = await this.db.queryOne(sql, sqlParam);

            return info;
        }

        /**
         * 修改用户数据
         *
         * @param {Object} data - post过来的数据
         * @param {Number} accountId - 账号id
         * @return {Boolean} - 返回修改结果
         */
        async save(data, accountId) {
            if (data._csrf !== undefined) {
                delete data._csrf;
            }
            accountId = parseInt(accountId);
            accountId = isNaN(accountId) ? 0 : accountId;
            let result = false;

            if (accountId <= 0) {
                return result;
            }
            data.id = accountId;

            // 更新数据
            const operate = await this.db.update(this.tableName, data);
            result = operate.affectedRows > 0;

            return result;
        }

        /**
         * 修改密码
         *
         * @param {Number} accountId - 账号id
         * @param {String} password - 旧密码
         * @param {String} newPassword - 新密码
         * @return {Boolean} - 返回修改结果
         */
        async modifyPassword(accountId, password, newPassword) {
            // 查找账号
            const accountData = await this.getDataByCondition({ id: accountId });
            if (accountData.password === undefined) {
                throw '不存在对应用户';
            }
            // 判断是否为sso账号，如果是则不能在此系统修改（后续通过接口修改？）
            if (accountData.password === 'SSO password') {
                throw 'SSO用户请到SSO系统修改密码';
            }

            // 加密密码
            const encryptPassword = crypto.createHmac('sha1', accountData.account).update(password)
                .digest().toString('base64');
            if (encryptPassword !== accountData.password) {
                throw '密码错误';
            }

            // 通过密码验证后修改数据
            const encryptNewPassword = crypto.createHmac('sha1', accountData.account).update(newPassword)
                .digest().toString('base64');
            const updateData = { password: encryptNewPassword };
            const result = await this.save(updateData, accountId);

            return result;
        }

        /**
         * 设置短信验证码
         *
         * @param {Number} accountId - 账号id
         * @param {String} mobile - 电话号码
         * @return {Boolean} - 设置结果
         */
        async setSMSCode(accountId, mobile) {
            const cacheKey = 'smsCode:' + accountId;
            const randString = this.ctx.helper.generateRandomString(6, 2);
            // 缓存15分钟(拼接电话，防止篡改)
            this.cache.set(cacheKey, randString + mobile, 'EX', 900);
            let result = false;

            // 发送短信
            try {
                const sms = new SMS(this.ctx);
                const content = '【纵横计量支付】验证码：' + randString + '，15分钟有效。';
                result = await sms.send(mobile, content);
            } catch (error) {
                result = false;
            }

            return result;
        }

        /**
         * 绑定认证手机
         *
         * @param {Number} accountId - 账号id
         * @param {Object} data - post过来的数据
         * @return {Boolean} - 绑定结果
         */
        async bindMobile(accountId, data) {
            const cacheKey = 'smsCode:' + accountId;
            const cacheCode = await this.cache.get(cacheKey);
            if (cacheCode === null || data.code === undefined || cacheCode !== (data.code + data.auth_mobile)) {
                return false;
            }

            // 查找是否有重复的认证手机
            const accountData = await this.getDataByCondition({ auth_mobile: data.auth_mobile });
            if (accountData !== null) {
                throw '已存在对应的手机';
            }

            const updateData = { auth_mobile: data.auth_mobile };

            return this.save(updateData, accountId);
        }

    }

    return ProjectAccount;
};
