var https = require('https');
var httpsAgent = require('./httpsagent');
var parse   = require('url').parse;
var config = require('../conf/config.js').config();
var iconv = require('iconv-lite');
var crypto = require('crypto');
var r=require('./r.js');
var co       = require('co');
var memCacheUtil=require('./MemCacheUtil.js');
var state=config.weixin.state;//得到token值
var agent = new httpsAgent.HttpsProxyAgent({
    proxyHost: config.proxy&&config.proxy.ip?config.proxy.ip:'192.168.10.199',
    proxyPort: config.proxy&&config.proxy.port?config.proxy.port: 1080
});
var xml2js=require("xml2js");
var parser= new xml2js.Parser(xml2js.defaults["0.1"]);


function get(url,callback){
    var info=parse(url);
    var options={
        host: info.hostname,
        port: 443,
        method: 'GET',
        path: info.path,
        agent: agent
    };

    https.request(options, function (res) {
        res.on('data', function (chunk) {
            callback(null,chunk);//返回结果
        }).on('error', function (e) {
            console.log('problem with request: ' + e.message);
            callback(e,null);
        });
    }).end();
}

function post(url,data,callback){
    var info=parse(url);
    data=JSON.stringify(data);
    if (typeof data == 'string') {
        var buffer = new Buffer(data, 'utf8');
        data = buffer;
    }
    var opt={
        host: info.hostname,
        port: 443,
        method: 'POST',
        path: info.path,
        agent: agent,
        headers: {
            "Content-Type": 'application/x-www-form-urlencoded',
            "Content-Length": data.length
        }
    };

    var req = https.request(opt, function (serverFeedback) {
        if (serverFeedback.statusCode == 200) {
            var body = "";
            serverFeedback.on('data', function (data) { body += data; })
                .on('end', function () {
                    // res.send(200, body);
                    callback(null,body);//返回结果
                }).on('error', function (e) {
                    console.log('problem with request: ' + e.message);
                    callback(e,null);
                });
        }
        else {
            res.send(500, "error");
            callback(err,null);
        }
    });
    req.write(data + "\n");

    req.end();
}

//得到access_token
function getOauth2(param,callback){
    var url="https://api.weixin.qq.com/sns/oauth2/access_token?appid="+config.weixin.appid+"&secret="+config.weixin.secret+"&code="+param.code+"&grant_type=authorization_code";
    get(url,callback);
}
//检查access是否有效
function getIsValid(param,callback){
    var invalidAT = "https://api.weixin.qq.com/sns/auth?access_token=" + param.access_token + "&openid=" + param.openid;
    get(invalidAT,callback);
}
//重新刷新token
function  reFreshToken(param,callback){
    var url = "https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=" + config.weixin.appid + "&grant_type=refresh_token&refresh_token=REFRESH_TOKEN";
    get(url,callback);
}


//网页授权后提交页面到微信接口，此时需要拿到用户的微信一些信息，用于插入操作
/**
 *
 * @param req
 * @param param
 * @param callback
 * @des：
 * 具体流程如下：1.查对应的access_token 2.access_token失效的情况下重新进行刷新，拿最新的access_token去进行传参
 *               3.将最新的access_token,open_id传入拿用户数据的url中取得userInfo数据，然后抛出
 */
function getWeiXinUser(req,param,callback){
    getOauth2(param,function(err,res){
        if(err){
            console.error(err);
            callback(err,null);
        }
        //只返回结果
        var json = JSON.parse(iconv.decode(res, 'UTF-8'));
        if(json.errcode){
            callback(json.errcode,null);
        }else {
            if (json.openid && r.loadFromCache(json.openid)) {
                var user = r.loadFromCache(json.openid).data;//data才是user里面的内容
                callback(null, user);//如果数据中有
            } else {
                if (json.access_token) {
                    getIsValid(json,function (err, resAcc) {
                        var accResult = JSON.parse(iconv.decode(resAcc));
                        if (accResult.errcode != 0) {
                            reFreshToken(accResult,function (errR, resR) {
                                var newResult = JSON.parse(iconv.decode(resR));
                                if (newResult.errcode == undefined) {
                                    getUserInfo(newResult, function (err, user) {
                                        callback(err, user);
                                    });
                                } else {
                                    callback(err, null);
                                }
                            })
                        } else {
                            if (json.errcode == undefined) {//报错时直接退出，不进入查询userInfo的页面
                                getUserInfo(json, function (err, user) {
                                    callback(err, user);
                                });
                            } else {
                                callback(err, null);
                            }
                        }

                    });
                } else {
                    callback(err, null);//授权code处已失败
                }
            }
        }
    });
}


//得到用户信息
function getUserInfo(param,callback){
    var urlInfo = "https://api.weixin.qq.com/sns/userinfo?lang=zh_CN&access_token=" + param.access_token + "&openid=" + param.openid;
    get(urlInfo, function (err, response) {
        var user = JSON.parse(iconv.decode(response, 'UTF-8'));
        r.writeToCache(user.openid,user,config.weixin.sessionTime);//openid==>user cache
        callback(null, user);
    });
}

/**生成数字签名开始 前提：npm install jssha**/
/**
 *
 * @param appid
 * @param appsecret
 * @des：1.操作步骤如下
 */
function getInfoForJsSDK(req,callback) {//在微信中打开页面形式是否可以调用对应的jsjdk
    var acToUrl='https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid='+config.weixin.appid+'&secret='+config.weixin.secret;

    get(acToUrl,function(err,res){
        var acToObj=JSON.parse(iconv.decode(res,"UTF-8"));
        var accessToken=acToObj.access_token;
        var ticketUrl="https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="+accessToken+"&type=jsapi";//
        get(ticketUrl,function(err,resTick){
            var ticketObj=JSON.parse(iconv.decode(resTick,"UTF-8"));
            var ticket=ticketObj.ticket;
            /**sign(ticket,req.hostname);//生成数据签名
             * somethings like this:
             * jsapi_ticket: 'jsapi_ticket',
             *  nonceStr: '82zklqj7ycoywrk',
             *  timestamp: '1415171822',
             *  url: 'http://example.com', //此时的url要记住了，要用全域名，包含整个域名，包括参数
             *  signature: '1316ed92e0827786cfda3ae355f33760c4f70c1f'
             */

            callback(err,sign(ticket,"http://"+req.headers.host+req.url));//得到签名
        });
    });
}
var createNonceStr = function () {
    return Math.random().toString(36).substr(2, 15);
};

var createTimestamp = function () {
    return parseInt(new Date().getTime() / 1000) + '';
};

var raw = function (args) {
    var keys = Object.keys(args);
    keys = keys.sort()
    var newArgs = {};
    keys.forEach(function (key) {
        newArgs[key.toLowerCase()] = args[key];
    });

    var string = '';
    for (var k in newArgs) {
        string += '&' + k + '=' + newArgs[k];
    }
    string = string.substr(1);
    return string;
};

/**
 * @synopsis 签名算法
 *
 * @param jsapi_ticket 用于签名的 jsapi_ticket
 * @param url 用于签名的 url ，注意必须动态获取，不能 hardcode
 *
 * @returns
 */
var sign = function (jsapi_ticket, url) {
    var ret = {
        jsapi_ticket: jsapi_ticket,
        nonceStr: createNonceStr(),
        timestamp: createTimestamp(),
        url: url
    };
    var string = raw(ret);
    jsSHA = require('jssha');
    shaObj = new jsSHA(string, 'TEXT');
    ret.signature = shaObj.getHash('SHA-1', 'HEX');
    ret.appid=config.weixin.appid;
    ret.secret=config.weixin.secret;
    return ret;
};

/**
 * 得到相应的模板内容
 * @param options
 * @constructor
 */
function ToXml(options){
    options.CreateTime=new Date().getTime();
    var xml= '<xml>'+
        '<ToUserName><![CDATA['+options.ToUserName+']]></ToUserName>'+
        '<FromUserName><![CDATA['+options.FromUserName+']]></FromUserName>'+
        '<CreateTime>'+options.CreateTime+'</CreateTime>'+
        '<MsgType><![CDATA['+options.MsgType+']]></MsgType>';
    if (options.MsgType === "news"){
        xml=xml+  '<ArticleCount>'+options.Content.length+'</ArticleCount>'+
            '<Articles>';
        options.Content.forEach(function(item){
            xml = xml + '<item>' +
                '<Title><![CDATA['+ item.title + ']]></Title>' +
                '<Description><![CDATA['+item.description+']]></Description>' +
                '<PicUrl><![CDATA[' + item.picUrl + ']]></PicUrl>'+
                '<Url><![CDATA[' + item.url + ']]></Url>' +
                '</item>';
        });
        xml='<Title>'+options.Title+ '</Title>';
        xml='<Description>'+options.Description+ '</Description>';
        xml='<PicUrl>'+options.PicUrl+ '</PicUrl>';
        xml='<Url>'+options.Url+ '</Url>';

    } else if (options.MsgType === "music") {
        xml=xml+ '<Music>'+
            '<Title><![CDATA['+options.Title+']]></Title>'+
            '<Description><![CDATA['+options.Description+']]></Description>'+
            '<MusicUrl><![CDATA['+options.MusicUrl+']]></MusicUrl>'+
            '<HQMusicUrl><![CDATA['+options.HqMusicUrl+']]></HQMusicUrl>'+
            '</Music>';
    } else if (options.MsgType === "voice") {
        xml=xml+'<Voice>'+
            '<MediaId><![CDATA[<'+options.mediaId+']]></MediaId>'+
            '</Voice>';
    } else if (options.MsgType === "image") {
        xml=xml+'<Image>'+
            '<MediaId><![CDATA['+options.MediaId+']]></MediaId>'+
            '</Image>';
    } else if (options.MsgType === "video") {
        xml=xml+'<Video>'+
            '<MediaId><![CDATA['+options.MediaId+']]></MediaId>'+
            '<Title><![CDATA['+options.Title+']></Title>'+
            '<Description><![CDATA[<'+options.Description+']]></Description>'+
            '</Video>';
    } else if (options.MsgType === "transfer_customer_service") {
        if (options.KfAccount) {
            xml = xml + '<TransInfo>' +
                '<KfAccount><![CDATA[' + options.KfAccount + ']]></KfAccount>' +
                '</TransInfo>';
        }
    }else if (options.MsgType === "location") {
        xml=xml+  '<Location_X>'+options.XPos+'</Location_X>'+
            '<Location_Y>'+options.YPos+'</Location_Y>'+
            '<Scale>'+options.Scale+'</Scale>'+
            '<Label><![CDATA['+options.Label+']]></Label>';
    }else if (options.MsgType === "link") {
        xml = xml + '<Title><![CDATA[' + options.Title + ']]></Title>' +
            '<Description><![CDATA[' + options.Description + ']]></Description>' +
            '<Url><![CDATA[' + options.Url + ']]></Url>';
    }else if (options.MsgType === "event") {
        xml = xml + '<Event><![CDATA[<' + options.Event + ']]></Event>';
        if(options.Event==="VIEW"){//菜单链接
            xml=xml+'<EventKey><![CDATA['+options.Url+']]></EventKey>';
        }
        if (options.Event === "LOCATION") {
            xml = xml + '<Latitude>'+options.Latitude+'</Latitude>' +
                '<Longitude>'+options.Longitude+'</Longitude>' +
                '<Precision>'+options.Precision+'</Precision>';
        }
        if (options.Event === "location_select") {//弹出地理选择器时
            xml = xml + '<Event><![CDATA[6]]></Event>' +
                '<EventKey><![CDATA[' + options.EventKey + ']]></EventKey>' +
                '<SendLocationInfo>' +
                '<Location_X><![CDATA[' + options.XPos + ']]></Location_X>' +
                '<Location_Y><![CDATA[<' + options.YPos + ']]></Location_Y>' +
                '<Scale><![CDATA[16]]></Scale>' +
                '<Label><![CDATA[' + options.Label + ']]></Label>' +
                '<Poiname><![CDATA[]]></Poiname>' +
                '<EventKey><![CDATA[' + options.EventKey + ']]></EventKey>' +
                '</SendLocationInfo>';
        }
    } else {
        xml = xml + '<Content><![CDATA[' + options.Content + ']]></Content>';
    }
    xml=xml+'</xml>';

    return xml;
}

/**
 *
 * @param query 该值为微信接入时向用户发送的密钥,包含四个参数
 */
function checkSignature(query){
    var signature = query.signature;
    var timestamp = query.timestamp;
    var nonce = query.nonce;

    var shasum = crypto.createHash('sha1');
    var arr = [config.weixin.state, timestamp, nonce].sort();
    shasum.update(arr.join(''));
    return shasum.digest('hex') === signature;
}

//首次订阅时需要创建菜单
function  setSubscribeCreateMenu(menus,callback){
    getAccessToken(function(err,accessToken){
        var menuUrl = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=" + accessToken;
        post(menuUrl, menus, callback);
    });

}
/**生成数据签名结束**/


/** 获取 accessToken 开始*/
function getAccessToken(callback){
   co (function *(){
       var token=yield memCacheUtil.get("accessToken");//取缓存数据
       //取缓存数据
        if(token){
             var accessToken =token;
             callback(null,accessToken);
        }else {
            var acToUrl = 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' + config.weixin.appid + '&secret=' + config.weixin.secret;
            get(acToUrl,function(err,res) {
                var acToObj = JSON.parse(iconv.decode(res, "UTF-8"));
                if(acToObj.errcode){
                    callback(new Error(acToObj.errmsg),null);
                    return;
                }
                var accessToken = acToObj.access_token;
                memCacheUtil.set("accessToken", accessToken, 100);
                callback(null,accessToken);
            });
        }
    })();
}
/** 获取 accessToken结束*/

/**生成带参数的二维码开始*/
function  getTicket(options,callback){
    getAccessToken(function(err,accessToken) {
        if(err) callback(new Error("get AccessToken error"),null);
        var url ="https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token="+accessToken;
        // var url="https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token="+'rFVrtBnH7S_0bKRlaYmIY86HwimeV3ONEtkUUL9bta3DSuzf4gSsrVV2D5IzMzP_PqxqZzlqTDT6W_hJHJrUuXp12-h4J-do7LCHP2uafRoFCEgAJAJGI';
        // console.log(url);
        post(url,options,callback);
    });
}

//返回ticket值，供pc端调用,options是post参数
/**
 *
 * @param options
 * @param callback
 * eg:options:
 * {"expire_seconds": 604800, "action_name": "QR_SCENE", "action_info": {"scene": {"scene_id": 123}}}
 * {"action_name": "QR_LIMIT_SCENE", "action_info": {"scene": {"scene_id": 123}}}
 */
function getQrCode(options,callback){
    getTicket(options,function(err,results){
        results=JSON.parse(results);
        if(results.errcode==undefined){
            var url="https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket="+encodeURIComponent(results.ticket);
            callback(null,url);
        }else{
            callback(new Error("ticket error"),null);
        }
    });
}

/**生成带参数的二维码结束*/

//exports.get=get;
module.exports={
        getWeiXinUser:function(req,param) {
            return function (callback) {
                getWeiXinUser(req, param, callback);
            }
        },
        getUserInfo: function (openid) {//不通过授权页面的方式
            return function (callback) {
                var entry = r.loadFromCache(openid);
                if (entry && entry.data) {
                    callback(null, entry.data);//得到用户信息
                } else {
                    getAccessToken(function (err, accessToken) {
                        if (err) {
                            callback(err, null);
                            return ;
                        }
                        var param = {"openid": openid, "access_token": accessToken};
                        getUserInfo(param, callback);
                    });
                }
            }
        },
        getInfoForJsSDK:function(req) {
            return function (callback) {
                getInfoForJsSDK(req, callback);//得到签名
            }
        },
        checkSignature:checkSignature,
        setSubscribeCreateMenu: function (menus) {
            return function (callback) {
                setSubscribeCreateMenu(menus, callback);
            }
        },
        ToXml:ToXml,
        xml2Json:function(strXML) {
            return function (callback) {
                parser.parseString(strXML, callback);
            }
        },
       getAccessToken:function(){
           return function(callback){
                   getAccessToken(callback);
                    }
       },
       getQrCode:function(options) {
                return function (callback) {
                    getQrCode(options, callback);
                }
           }
}
