/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package org.redkale.demo.user;

import org.redkale.demo.notice.RandomCode;
import java.io.IOException;
import java.net.HttpCookie;
import java.util.Map;
import javax.annotation.Resource;
import org.redkale.convert.json.*;
import org.redkale.demo.base.*;
import org.redkale.net.http.*;
import org.redkalex.weixin.WeiXinMPService;
import org.redkale.service.RetResult;
import org.redkale.util.AnyValue;

/**
 * 用户模块的Servlet
 *
 * @author zhangjx
 */
@WebServlet({"/user/*"})
public class UserServlet extends BaseServlet {

    public static final String COOKIE_AUTOLOGIN = "UNF";

    @Resource
    private UserService service;

    //用于微信登录
    @Resource
    private WeiXinMPService wxService;

    @Resource
    private JsonConvert userConvert;

    @Override
    public void init(HttpContext context, AnyValue config) {
        JsonFactory factory = JsonFactory.root().createChild();
        //当前用户查看自己的用户信息时允许输出隐私信息
        factory.register(UserDetail.class, false, "mobile", "email", "wxunionid", "qqopenid", "apptoken");
        userConvert = factory.getConvert();
        super.init(context, config);
    }

    //用户注销
    @HttpMapping(url = "/user/logout", auth = false)
    public void logout(HttpRequest req, HttpResponse resp) throws IOException {
        String sessionid = req.getSessionid(false);
        if (sessionid != null) service.logout(sessionid);
        HttpCookie cookie = new HttpCookie(COOKIE_AUTOLOGIN, "");
        cookie.setPath("/");
        cookie.setMaxAge(1);
        resp.addCookie(cookie);
        resp.finish("{\"success\":true}");
    }

    @HttpMapping(url = "/user/updatewxid", auth = false)
    public void updateWxunionid(HttpRequest req, HttpResponse resp) throws IOException {
        String code = req.getParameter("code");
        String state = req.getParameter("state");  //state值格式: appid_autoregflag
        if (finest) logger.finest("/user/updatewxid :  " + code + "," + state);
        service.updateWxunionid(req.currentUser(), code);
        resp.setHeader("Location", req.getParameter("url", "/"));
        resp.finish(302, null);
    }

    //需要在 “开发 - 接口权限 - 网页服务 - 网页帐号 - 网页授权获取用户基本信息”的配置选项中,修改授权回调域名
    @HttpMapping(url = "/user/wxopenid", auth = false)
    public void wxopenid(HttpRequest req, HttpResponse resp) throws IOException {
        String code = req.getParameter("code");
        if (finest) logger.finest("/user/wxopenid :  " + req);
        Map<String, String> rr = wxService.getMPUserTokenByCode(code);
        resp.setHeader("Location", req.getParameter("url", "/"));
        resp.finish(302, null);
    }

    /**
     * 微信登陆 https://open.weixin.qq.com/connect/qrconnect?appid=wx微信ID&redirect_uri=xxxxx&response_type=code&scope=snsapi_login&state=wx微信ID_1#wechat_redirect
     * 接收两种形式:
     * WEB端微信登录: /user/wxlogin?code=XXXXXX&state=wx微信ID_1&apptoken=XXX
     * APP端微信登录: /user/wxlogin?openid=XXXX&state=1&access_token=XXX&apptoken=XXX
     * <p>
     * @param req
     * @param resp
     *
     * @throws IOException
     */
    @HttpMapping(url = "/user/wxlogin", auth = false)
    public void wxlogin(HttpRequest req, HttpResponse resp) throws IOException {
        String code = req.getParameter("code");
        String state = req.getParameter("state");  //state值格式: appid_autoregflag

        String access_token = req.getParameter("access_token");
        String openid = req.getParameter("openid");

        if (finest) logger.finest("/user/wxlogin :  code = " + code + ", access_token = " + access_token + ", openid = " + openid + ", state =" + state);
        int pos = state.indexOf('_');
        String appid = pos > 0 ? state.substring(0, pos) : state;
        if (appid.length() < 2) appid = "";
        boolean autoreg = (pos > 0 || "1".equals(state)) ? (state.charAt(pos + 1) == '1') : true;
        final boolean wxbrowser = req.getHeader("User-Agent", "").contains("MicroMessenger");
        LoginWXBean bean = new LoginWXBean();
        { //WEB方式
            bean.setAppid(appid);
            bean.setCode(code);
        }
        { //APP方式
            bean.setAccesstoken(access_token);
            bean.setOpenid(openid);
        }
        bean.setAutoreg(autoreg);
        bean.setApptoken(req.getParameter("apptoken", ""));
        bean.setLoginaddr(req.getRemoteAddr());
        bean.setLoginagent(req.getHeader("User-Agent"));
        if (autoreg) bean.setSessionid(req.changeSessionid());
        RetResult<UserInfo> rr = service.wxlogin(bean);
        if (autoreg && rr.isSuccess() && (wxbrowser || (access_token != null && !access_token.isEmpty()))) {
            UserInfo info = rr.getResult();
            int age = 1000 * 24 * 60 * 60;
            String key = (bean.emptyApptoken() ? "" : (bean.getApptoken() + "#")) + info.getUser36id() + "$1" + info.getWxunionid() + "?" + age + "-" + System.currentTimeMillis();
            HttpCookie cookie = new HttpCookie(COOKIE_AUTOLOGIN, UserService.encryptAES(key));
            cookie.setHttpOnly(true);
            cookie.setPath("/");
            cookie.setMaxAge(age);
            resp.addCookie(cookie);
        }
        if (access_token == null || access_token.isEmpty()) { //WEB登录
            resp.setHeader("Location", req.getParameter("url", "/"));
            resp.finish(302, null);
        } else { //APP 模式
            resp.finishJson(rr);
        }
    }

    @HttpMapping(url = "/user/qqlogin", auth = false)
    public void qqlogin(HttpRequest req, HttpResponse resp) throws IOException {
        String access_token = req.getParameter("access_token");
        String openid = req.getParameter("openid");
        if (finest) logger.finest("/user/qqlogin :  " + openid + "," + access_token);
        LoginQQBean bean = new LoginQQBean();
        bean.setAccesstoken(access_token);
        bean.setApptoken(req.getParameter("apptoken", ""));
        bean.setOpenid(openid);
        bean.setLoginaddr(req.getRemoteAddr());
        bean.setLoginagent(req.getHeader("User-Agent"));
        bean.setSessionid(req.changeSessionid());
        RetResult<UserInfo> rr = service.qqlogin(bean);
        if (rr.isSuccess()) {
            UserInfo info = rr.getResult();
            int age = 1000 * 24 * 60 * 60;
            String key = info.getUser36id() + "$2" + info.getQqopenid() + "?" + age + "-" + System.currentTimeMillis();
            HttpCookie cookie = new HttpCookie(COOKIE_AUTOLOGIN, UserService.encryptAES(key));
            cookie.setHttpOnly(true);
            cookie.setPath("/");
            cookie.setMaxAge(age);
            resp.addCookie(cookie);
        }
        if (access_token == null || access_token.isEmpty()) {
            resp.setHeader("Location", req.getParameter("url", "/"));
            resp.finish(302, null);
        } else { //APP 模式
            resp.finishJson(rr);
        }
    }

    /**
     * 用户登陆
     *
     * @param req
     * @param resp
     *
     * @throws IOException
     */
    @HttpMapping(url = "/user/login", auth = false)
    public void login(HttpRequest req, HttpResponse resp) throws IOException {
        LoginBean bean = req.getJsonParameter(LoginBean.class, "bean");
        if (bean == null) bean = new LoginBean();
        if (!bean.emptyPassword()) bean.setPassword(UserService.secondPasswordMD5(bean.getPassword()));
        bean.setLoginagent(req.getHeader("User-Agent"));
        bean.setLoginip(req.getRemoteAddr());
        String oldsessionid = req.getSessionid(false);
        if (oldsessionid != null && !oldsessionid.isEmpty()) service.logout(oldsessionid);
        bean.setSessionid(req.changeSessionid());
        RetResult<UserInfo> result = service.login(bean);
        if (result.isSuccess() && !bean.emptyPassword()) { //必须是密码登录类
            if (bean.getCacheday() > 0 && bean.emptyCookieinfo()) {  //保存N天 
                UserInfo info = result.getResult();
                int age = bean.getCacheday() * 24 * 60 * 60;
                String key = (bean.emptyApptoken() ? "" : (bean.getApptoken() + "#")) + info.getUser36id() + "$0" + bean.getPassword() + "?" + age + "-" + System.currentTimeMillis();
                HttpCookie cookie = new HttpCookie(COOKIE_AUTOLOGIN, UserService.encryptAES(key));
                cookie.setHttpOnly(true);
                cookie.setPath("/");
                cookie.setMaxAge(age);
                resp.addCookie(cookie);
            }
        }
        resp.finishJson(result);
    }

    @HttpMapping(url = "/user/signup", auth = false)   //待定
    public void signup(HttpRequest req, HttpResponse resp) throws IOException {
        long s = System.currentTimeMillis();
        Map<String, String> map = convert.convertFrom(JsonConvert.TYPE_MAP_STRING_STRING, req.getParameter("bean"));
        RetResult<RandomCode> ret = null;
        String beanaccount;
        UserDetail bean = new UserDetail();
        if (map.containsKey("mobile")) {
            bean.setMobile(map.get("mobile"));
            beanaccount = bean.getMobile();
            ret = service.checkRandomCode(bean.getMobile(), map.get("vercode"), RandomCode.TYPE_SMSREG);
            if (!ret.isSuccess()) {
                resp.finishJson(ret);
                return;
            }
        } else if (map.containsKey("email")) {
            bean.setEmail(map.get("email"));
            beanaccount = bean.getEmail();
        } else {
            bean.setAccount(map.getOrDefault("account", ""));
            beanaccount = bean.getAccount();
        }
        bean.setUsername(map.getOrDefault("username", ""));
        bean.setApptoken(map.getOrDefault("apptoken", ""));
        bean.setPassword(map.getOrDefault("password", ""));
        bean.setRegaddr(req.getRemoteAddr());
        bean.setRegagent(req.getHeader("User-Agent", ""));
        final String reqpwd = bean.getPassword();
        RetResult<UserInfo> rr = service.register(bean);
        if (rr.isSuccess()) {
            if (ret != null) {
                ret.getResult().setUserid(rr.getResult().getUserid());
                service.removeRandomCode(ret.getResult());
            }
            LoginBean loginbean = new LoginBean();
            loginbean.setAccount(beanaccount);
            loginbean.setApptoken(bean.getApptoken());
            loginbean.setPassword(UserService.secondPasswordMD5(reqpwd));
            loginbean.setSessionid(req.changeSessionid());
            loginbean.setLoginagent(req.getHeader("User-Agent"));
            if (map.containsKey("cacheday")) loginbean.setCacheday(Integer.parseInt(map.getOrDefault("cacheday", "0")));
            loginbean.setLoginip(req.getRemoteAddr());
            rr = service.login(loginbean);
        }
        long e = System.currentTimeMillis() - s;
        if (e > 500) logger.warning("/user/signup cost " + e / 1000.0 + " seconds " + bean);
        resp.finishJson(rr);
    }

    /**
     * 修改密码
     *
     * @param req
     * @param resp
     *
     * @throws IOException
     */
    @HttpMapping(url = "/user/updatepwd")
    public void updatepwd(HttpRequest req, HttpResponse resp) throws IOException {
        UserPwdBean bean = req.getJsonParameter(UserPwdBean.class, "bean");
        UserInfo curr = req.currentUser();
        if (curr != null) bean.setSessionid(req.getSessionid(false));
        RetResult<UserInfo> result = service.updatePwd(bean);
        if (result.isSuccess() && curr == null) { //找回的密码
            curr = result.getResult();
            LoginBean loginbean = new LoginBean();
            loginbean.setAccount(curr.getEmail().isEmpty() ? curr.getMobile() : curr.getEmail());
            loginbean.setPassword(UserService.secondPasswordMD5(bean.getNewpwd()));
            loginbean.setSessionid(req.changeSessionid());
            loginbean.setLoginagent(req.getHeader("User-Agent"));
            loginbean.setLoginip(req.getRemoteAddr());
            result = service.login(loginbean);
        }
        String autologin = req.getCookie(COOKIE_AUTOLOGIN);
        if (result.isSuccess() && autologin != null) {
            autologin = UserService.decryptAES(autologin);
            if (autologin.contains("$0")) { //表示COOKIE_AUTOLOGIN 为密码类型存储
                String newpwd = UserService.secondPasswordMD5(bean.getNewpwd());
                int wen = autologin.indexOf('?');
                int mei = autologin.indexOf('$');
                String key = autologin.substring(0, mei + 2) + newpwd + autologin.substring(wen);
                HttpCookie cookie = new HttpCookie(COOKIE_AUTOLOGIN, UserService.encryptAES(key));
                cookie.setHttpOnly(true);
                cookie.setPath("/");
                String time = autologin.substring(wen + 1);
                int fen = time.indexOf('-');
                int age = Integer.parseInt(time.substring(0, fen)); //秒数
                long point = Long.parseLong(time.substring(fen + 1)); //毫秒数
                cookie.setMaxAge(age - (System.currentTimeMillis() - point) / 1000);
                resp.addCookie(cookie);
            }
        }
        resp.finishJson(result);
    }

    //更新用户手机号码
    @HttpMapping(url = "/user/updatemobile")
    public void updatemobile(HttpRequest req, HttpResponse resp) throws IOException {
        UserInfo user = req.currentUser();
        resp.finishJson(service.updateMobile(user.getUserid(), req.getParameter("mobile"), req.getParameter("vercode"), req.getParameter("precode")));
    }

    //更新用户昵称
    @HttpMapping(url = "/user/updateusername")
    public void updateUsername(HttpRequest req, HttpResponse resp) throws IOException {
        UserInfo user = req.currentUser();
        resp.finishJson(service.updateUsername(user.getUserid(), req.getParameter("username")));
    }

    //更新设备ID
    @HttpMapping(url = "/user/updateapptoken")
    public void updateApptoken(HttpRequest req, HttpResponse resp) throws IOException {
        String s = req.getRequstURILastPath();
        if ("updateapptoken".equalsIgnoreCase(s)) s = "";
        UserInfo user = req.currentUser();
        resp.finishJson(service.updateApptoken(user.getUserid(), req.getParameter("appos", req.getRequstURIPath("appos:", "")), req.getParameter("apptoken", s)));
    }

    //更新性别
    @HttpMapping(url = "/user/updategender/")
    public void updateGender(HttpRequest req, HttpResponse resp) throws IOException {
        UserInfo user = req.currentUser();
        resp.finishJson(service.updateGender(user.getUserid(), Short.parseShort(req.getRequstURILastPath())));
    }

    //发送修改密码验证码
    @HttpMapping(url = "/user/smspwdcode")
    public void smscode(HttpRequest req, HttpResponse resp) throws IOException {
        smsvercode(RandomCode.TYPE_SMSPWD, req, resp);
    }

    //发送手机修改验证码
    @HttpMapping(url = "/user/smsmobcode")
    public void smsmob(HttpRequest req, HttpResponse resp) throws IOException {
        smsvercode(RandomCode.TYPE_SMSMOB, req, resp);
    }

    //发送原手机验证码
    @HttpMapping(url = "/user/smsodmcode")
    public void smsodm(HttpRequest req, HttpResponse resp) throws IOException {
        smsvercode(RandomCode.TYPE_SMSODM, req, resp);
    }

    //发送手机注册验证码
    @HttpMapping(url = "/user/smsregcode", auth = false)
    public void smsreg(HttpRequest req, HttpResponse resp) throws IOException {
        smsvercode(RandomCode.TYPE_SMSREG, req, resp);
    }

    //发送手机登录验证码
    @HttpMapping(url = "/user/smslgncode", auth = false)
    public void smslgn(HttpRequest req, HttpResponse resp) throws IOException {
        smsvercode(RandomCode.TYPE_SMSLGN, req, resp);
    }

    private void smsvercode(final short type, HttpRequest req, HttpResponse resp) throws IOException {
        String mobile = req.getRequstURIPath("mobile:", req.getParameter("mobile"));
        if (type == RandomCode.TYPE_SMSODM) { //给原手机号码发送验证短信
            UserInfo user = req.currentUser();
            if (user != null) mobile = user.getMobile();
        }
        RetResult rr = service.smscode(type, mobile);
        if (finest) logger.finest(req.getRequestURI() + ", mobile = " + mobile + "---->" + rr);
        resp.finishJson(rr);
    }

    //检测账号是否有效, 返回t0表示可用.给新用户注册使用
    @HttpMapping(url = "/user/checkaccount/", auth = false)
    public void checkAccount(HttpRequest req, HttpResponse resp) throws IOException {
        resp.finishJson(RetCodes.retResult(service.checkAccount(req.getRequstURILastPath())));
    }

    //检测手机号码是否有效, 返回0表示可用.给新用户注册使用
    @HttpMapping(url = "/user/checkmobile/", auth = false)
    public void checkMobile(HttpRequest req, HttpResponse resp) throws IOException {
        resp.finishJson(RetCodes.retResult(service.checkMobile(req.getRequstURILastPath())));
    }

    //检测邮箱地址是否有效, 返回0表示可用.给新用户注册使用
    @HttpMapping(url = "/user/checkemail/", auth = false)
    public void checkEmail(HttpRequest req, HttpResponse resp) throws IOException {
        resp.finishJson(RetCodes.retResult(service.checkEmail(req.getRequstURILastPath())));
    }

    //验证短信验证码
    @HttpMapping(url = "/user/checkcode", auth = false)
    public void checkcode(HttpRequest req, HttpResponse resp) throws IOException {
        String mobile = req.getRequstURIPath("mobile:", req.getParameter("mobile"));
        String vercode = req.getRequstURIPath("vercode:", req.getParameter("vercode"));
        RetResult<RandomCode> ret = service.checkRandomCode(mobile, vercode, (short) 0);
        resp.finishJson(RetCodes.retResult(ret.getRetcode()));
    }

    //获取当前用户基本信息
    @HttpMapping(url = "/user/myinfo")
    public void myinfo(HttpRequest req, HttpResponse resp) throws IOException {
        UserInfo user = req.currentUser();
        resp.finishJson(user);
    }

    //获取当前用户基本信息(js格式)
    @HttpMapping(url = "/user/js/myinfo", auth = false)
    public void myinfojs(HttpRequest req, HttpResponse resp) throws IOException {
        UserInfo user = req.currentUser();
        resp.setContentType("application/javascript; charset=utf-8");
        if (user == null) {
            resp.finish("var userself = null;");
        } else {
            resp.finish("var userself = " + convert.convertTo(user) + ";");
        }
    }

    //获取个人基本信息
    @HttpMapping(url = "/user/info/")
    public void info(HttpRequest req, HttpResponse resp) throws IOException {
        resp.finishJson(service.findUserInfo(Integer.parseInt(req.getRequstURILastPath(), 36)));
    }

    //获取当前用户详细信息
    @HttpMapping(url = "/user/mydetail")
    public void mydetail(HttpRequest req, HttpResponse resp) throws IOException {
        UserInfo user = req.currentUser();
        resp.finish(userConvert.convertTo(service.findUserDetail(user.getUserid())));
    }

    //获取当前用户详细信息(js格式)
    @HttpMapping(url = "/user/js/mydetail", auth = false)
    public void mydetailjs(HttpRequest req, HttpResponse resp) throws IOException {
        UserInfo user = req.currentUser();
        resp.setContentType("application/javascript; charset=utf-8");
        if (user == null) {
            resp.finish("var userdetailself = null;");
        } else {
            resp.finish("var userdetailself = " + userConvert.convertTo(service.findUserDetail(user.getUserid())) + ";");
        }
    }

}