/**
 * @Author  HonorLee (dev@honorlee.me)
 * @Version 1.0 (2018-05-05)
 * @License MIT
 */
'use strict'
const sha1 = require('sha1');
const tmpTokenFile = Core.Path.Temp + '/wechat_AccessToken.txt';
const tmpTicketFile = Core.Path.Temp + '/wechat_JsApiTicket.txt';
const WechatDomain = `https://${Config.Wechat.apiDomain}`;
const errMsg = require('./errMsg.js');
const WechatApiURL = {
    getAccessToken:`${WechatDomain}/cgi-bin/token?grant_type=client_credential&appid=${Config.Wechat.appId}&secret=${Config.Wechat.appSecret}`,
    getJsApiTicket:`${WechatDomain}/cgi-bin/ticket/getticket?type=jsapi&access_token=`,
    getJsOauthCode:`https://open.weixin.qq.com/connect/oauth2/authorize?appid=${Config.Wechat.appId}`,
    getOauthToken:`${WechatDomain}/sns/oauth2/access_token?appid=${Config.Wechat.appId}&secret=${Config.Wechat.appSecret}&grant_type=authorization_code`,
    getOauthUserinfo:`${WechatDomain}/sns/userinfo?lang=zh_CN`
}

let Wechat = {
    checkSignature:function(signature,timestamp,nonce){
        let arr = [Config.Wechat.token, timestamp, nonce];
        arr.sort();
        let content = arr.join('');
        return sha1(content) === signature;
    },
    getAccessToken:function(callback){
        let _this = this;
        getTokenFromCache(function(data){
            if(data){
                callback.call(_this,null,data);
            }else{
                Core.Request({url:WechatApiURL.getAccessToken,encoding:'UTF-8',json:true},function(err,response,body){
                    if(err || !body){
                        Logger.error('Wechat getAccessToken error!');
                        Logger.error(err);
                        callback.call(_this,new Error('Wechat getAccessToken error!'),null);
                        return;
                    }
                    if(body.errcode){
                        callback(_this,body.errcode,errMsg[body.errcode]);
                    }
                    let token = body.access_token;
                    let expires = body.expires_in;
                    setTokenToCahe(token,expires);
                    callback.call(_this,null,token);
                })
            }
        });
    },
    getJsApiTicket:function(callback){
        let _this = this;
        Wechat.getAccessToken(function(err,token){
            if(!err){
                getTicketFromCache(function(data){
                    if(data){
                        callback.call(_this,null,data);
                    }else{
                        let url = WechatApiURL.getJsApiTicket + token;
                        Core.Request({url:url,encoding:'UTF-8',json:true},function(err,response,body){
                            if(err || !body){
                                Logger.error('Wechat getJsApiTicket error!');
                                Logger.error(err);
                                callback.call(_this,new Error('Wechat getJsApiTicket error!'),null);
                                return;
                            }
                            if(body.errcode){
                                callback(_this,body.errcode,errMsg[body.errcode]);
                            }
                            let ticket = body.ticket;
                            let expires = body.expires_in;
                            setTicketToCahe(ticket,expires);
                            callback.call(_this,null,ticket);
                        });
                    }
                });
            }else{
                Logger.error('Wechat getJsApiTicket error!');
                Logger.error(err);
            }
        });
    },
    getSignature:function(url,callback){
        if(!url) return callback.call(this,new Error('URL is empty!'),null);
        let _this = this;
        let noncestr = String.random(16);
        let timestamp = Math.floor(Moment().valueOf()/1000);
        Wechat.getJsApiTicket(function(err,ticket){
            if(err){
                Logger.error('Wechat getSignature error!');
                Logger.error(err);
                callback.call(_this,err,null);
                return;
            }
            let combineStr = `jsapi_ticket=${ticket}&noncestr=${noncestr}&timestamp=${timestamp}&url=${url}`;
            let signature = sha1(combineStr);
            callback.call(_this,null,{nonceStr:noncestr,timestamp:timestamp,signature:signature});
        });
    },
    getJsOauthCodeURL:function(redirectURL,Scope,State){
        if(!redirectURL || !redirectURL.match(/(https?):\/\/[-A-Za-z0-9+&@#\/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]/gi)){
            return {err:'redirectURL Error'};
        }
        if(Scope!='snsapi_base' && Scope!='snsapi_userinfo'){
            return {err:'Scope must be snsapi_base or snsapi_userinfo'};
        }
        if(!State) State = 'null';
        redirectURL = encodeURI(redirectURL);
        let urlString = WechatApiURL.getJsOauthCode + `&redirect_uri=${redirectURL}&response_type=code&scope=${Scope}&state=${State}#wechat_redirect`;
        return {err:'',url:urlString};
    },
    getOauthToken:function(code,callback){
        if(!code) return callback.call(this,new Error('Code is empty!'),null);
        let _this = this;
        let url = WechatApiURL.getOauthToken + `&code=${code}`;
        Core.Request({url:url,encoding:'UTF-8',json:true},function(err,response,body){
            if(err || !body){
                Logger.error('Wechat getOauthToken error!');
                Logger.error(err);
                callback.call(_this,new Error('Wechat getOauthToken error!'),null);
                return;
            }
            if(body.errcode){
                return callback.call(_this,body.errcode,errMsg[body.errcode]);
            }
            callback.call(_this,null,body);
        });
    },
    getOauthUserinfo:function(access_token,openid,callback){
        let _this = this;
        if(!access_token) return callback.call(this,new Error('access_token is empty'));
        if(!openid) return callback.call(this,new Error('openid is empty'));
        let url = WechatApiURL.getOauthUserinfo + `&access_token=${access_token}&openid=${openid}`;
        Core.Request({url:url,encoding:'UTF-8',json:true},function(err,response,body){
            if(err || !body){
                Logger.error('Wechat getOauthUserinfo error!');
                Logger.error(err);
                callback.call(_this,new Error('Wechat getOauthUserinfo error!'),null);
                return;
            }
            if(body.errcode){
                return callback.call(_this,body.errcode,errMsg[body.errcode]);
            }
            callback.call(_this,null,body);
        });
    },
    getOauthUserinfoByCode:function(code,callback){
        if(!code) return callback.call(this,new Error('Code is empty!'),null);
        let _this = this;
        Wechat.getOauthToken(code,function(err,result){
            if(err) return callback.call(_this,err,null);
            Wechat.getOauthUserinfo(result.access_token,result.openid,function(err,result){
                if(err) return callback.call(_this,err,null);
                callback.call(_this,null,result);
            });
        });
    }
}

module.exports = Wechat;

function getTokenFromCache(callback){
    if(Config.Wechat.StoreType=='Memcache' && Config.Database.Memcache.on){
        Memcache.get('wechat_AccessToken',function(err,data){
            if(!err && data){
                callback(data);
            }else{
                callback();
            }
        });
        return;
    }

    try{
        FILE.statSync(tmpTokenFile);
    }catch(e){
        callback(null);
        return;
    }
    let obj = JSON.parse(FILE.readFileSync(tmpTokenFile,'UTF-8'));
    let now = Math.floor(Moment().valueOf()/1000)+60;
    if(now < obj.expires){
        callback(obj.token);
    }else{
        callback(null);
    }
    
}

function setTokenToCahe(token,expires){
    expires = expires - 60;
    if(Config.Wechat.StoreType=='Memcache' && Config.Database.Memcache.on){
        Memcache.set('wechat_AccessToken',token,expires,function(err){
            if(err) console.log(err);
        })
        return;
    }
    let expiresTime = Math.floor(Moment().valueOf()/1000) + expires;
    FILE.writeFileSync(tmpTokenFile,JSON.stringify({token:token,expires:expiresTime}),'UTF-8');
}

function getTicketFromCache(callback){
    if(Config.Wechat.StoreType=='Memcache' && Config.Database.Memcache.on){
        Memcache.get('wechat_JsApiTicket',function(err,data){
            if(!err && data){
                callback(data);
            }else{
                callback();
            }
        });
        return;
    }

    try{
        FILE.statSync(tmpTicketFile);
    }catch(e){
        callback(null);
        return;
    }
    let obj = JSON.parse(FILE.readFileSync(tmpTicketFile,'UTF-8'));
    let now = Math.floor(Moment().valueOf()/1000)+60;
    if(now < obj.expires){
        callback(obj.ticket);
    }else{
        callback(null);
    }
    
}

function setTicketToCahe(ticket,expires){
    expires = expires - 5;
    if(Config.Wechat.StoreType=='Memcache' && Config.Database.Memcache.on){
        Memcache.set('wechat_JsApiTicket',ticket,expires,function(err){
            if(err) console.log(err);
        })
        return;
    }
    let expiresTime = Math.floor(Moment().valueOf()/1000) + expires;
    FILE.writeFileSync(tmpTicketFile,JSON.stringify({ticket:ticket,expires:expiresTime}),'UTF-8');
}