package cn.quantgroup.xyqb.controller.external;

import cn.quantgroup.xyqb.Constants;
import cn.quantgroup.xyqb.controller.IBaseController;
import cn.quantgroup.xyqb.entity.Merchant;
import cn.quantgroup.xyqb.entity.User;
import cn.quantgroup.xyqb.entity.WechatUserInfo;
import cn.quantgroup.xyqb.model.AuthBean;
import cn.quantgroup.xyqb.model.LoginProperties;
import cn.quantgroup.xyqb.model.webchat.AccessTokenResponse;
import cn.quantgroup.xyqb.service.merchant.IMerchantService;
import cn.quantgroup.xyqb.service.session.ISessionService;
import cn.quantgroup.xyqb.service.user.IUserService;
import cn.quantgroup.xyqb.service.wechat.IWechatService;
import cn.quantgroup.xyqb.util.EmojiUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;

/**
 * Created by 11 on 2017/1/17.
 */
@Slf4j
@RestController
@RequestMapping("/wechat")
public class WeChatController implements IBaseController {

    // todo: 配置文件
    private static final String WECHAT_DEVELOPOR_TOKEN = "5YihkluEo5QuWAWpFwzvA";

    @Autowired
    private IWechatService wechatService;

    @Autowired
    private IMerchantService merchantService;

    @Autowired
    private ISessionService sessionService;

    @Autowired
    private IUserService userService;

    /**
     * 用户中心UI的http协议头域名
     * http://passport.xyqb.com
     */
    @Value("${passport.http}")
    private String userUiDomain;

    /**
     * 用户中心UI的https协议头域名
     * https://passport.xyqb.com
     */
    @Value("${passport.https}")
    private String userUiDomainS;

    @Value("${api.http}")
    private String xyqbDomainStr;
    /**
     * 现金贷Ui
     */
    @Value("${m.https}")
    private String xjdDomain;
    /**
     * 开发者资质认证,有必要吗?
     *
     * @param request
     * @return
     * @yapi unknown
     * @Deprecated 20210318
     */
    @Deprecated
    @RequestMapping("/checkValid")
    public String valid(HttpServletRequest request) {
        String echoStr = request.getParameter("echostr");
        if (checkSignature(request)) {
            return echoStr;
        }
        return "";
    }

    /**
     * 验签：步骤
     * 1.获取signature, timestamp, nonce三个参数
     * 2.用timestamp, nonce, token三个参数做字符升序排列
     * 3.将排列好的字符串进行sha1加密,和signature参数做比较
     * 4.相等返回true,否则返回false
     *
     * @param request
     * @return
     */
    private boolean checkSignature(HttpServletRequest request) {
        String signature = request.getParameter("signature");
        String timestamp = request.getParameter("timestamp");
        String nonce = request.getParameter("nonce");
        String[] arrs = new String[]{WECHAT_DEVELOPOR_TOKEN, timestamp, nonce};
        Arrays.sort(arrs);
        String joinStr = joinArray(arrs);
        joinStr = sha1(joinStr);
        return joinStr.equals(signature);
    }

    /**
     * sha1加密算法,
     *
     * @param decript
     * @return 返回40位16进制字符串
     */
    private String sha1(String decript) {
        try {
            MessageDigest digest = java.security.MessageDigest
                    .getInstance("SHA-1");
            digest.update(decript.getBytes());
            byte messageDigest[] = digest.digest();
            StringBuilder hexString = new StringBuilder();
            // 字节数组转换为十六进制数
            for (byte aMessageDigest : messageDigest) {
                String shaHex = Integer.toHexString(aMessageDigest & 0xFF);
                if (shaHex.length() < 2) {
                    hexString.append(0);
                }
                hexString.append(shaHex);
            }
            return hexString.toString();

        } catch (NoSuchAlgorithmException e) {
            log.error("未找到sha1算法." + e.getMessage());
        }
        return "";
    }

    /**
     * 数组分割为字符串
     *
     * @param arr
     * @return
     */
    private String joinArray(String[] arr) {
        StringBuilder builder = new StringBuilder();
        for (String str : arr) {
            builder.append(str);
        }
        return builder.toString();
    }

    /**
     * 前端微信跳转页面，通过extdata
     *
     * @param response
     * @return
     * @yapi http://yapi.quantgroups.com/project/17/interface/api/14447
     */
    @RequestMapping("/receiveCode/extdata/{key}/{extdata}")
    public void receiveCodeWithExtData(String code, @PathVariable(value = "key") String systemKey,
                                       @PathVariable(value = "extdata") String extData, HttpServletResponse response) {
        Long registerFrom = null;
        String redirect = null;
        String schema = null;
        String customRedirect = null;
        try {
            extData = new String(Base64.decodeBase64(extData), "UTF-8");
        } catch (Exception ex) {
            extData = "";
        }
        if (StringUtils.isBlank(extData)) {
            receiveCodeWithDefault(code, systemKey, schema, registerFrom, redirect, customRedirect, response);
            return;
        }
        log.info("从微信extdata版本接口进入：{}, extData:{}", schema, extData);
        HashMap<String, Object> extDataObj;
        try {
            extDataObj = JSON.parseObject(extData, new TypeReference<HashMap<String, Object>>() {
            });
        } catch (Exception ex) {
            log.error("解析extData发生错误", ex);
            receiveCodeWithDefault(code, systemKey, schema, registerFrom, redirect, customRedirect, response);
            return;
        }
        schema = extDataObj.getOrDefault("protocol", Constants.PROTOCOL_HEAD_HTTP).toString();
        log.info("从微信登录extData中获得协议信息,protocol:{}", schema);
        registerFrom = Long.valueOf(extDataObj.getOrDefault("registerFrom", "1").toString());
        redirect = (String) extDataObj.getOrDefault(Constants.REDIRECT, Constants.REDIRECT);
        customRedirect = (String) extDataObj.getOrDefault(Constants.CUSTOMREDIRECT, null);
        log.info("从微信登录,registerFrom:{}, redirect:{}", registerFrom, redirect);
        receiveCodeWithDefault(code, systemKey, schema, registerFrom, redirect, customRedirect, response);
    }

    /**
     * 前端微信跳转页面
     *
     * @param code
     * @param systemKey
     * @param schema
     * @param registerFrom
     * @param redirect
     * @param response
     */
    private void receiveCodeWithDefault(String code, String systemKey, String schema, Long registerFrom, String redirect, String customRedirect, HttpServletResponse response) {
        // 微信跳转请求入参监控
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        log.info("[WeChatController][receiveCodeWithDefault]微信授权及跳转：code:{},systemKey:{},schema:{},registerFrom:{},redirect:{},request:{}", code, systemKey, schema, registerFrom, redirect, JSON.toJSONString(getRequestHeaderMap(request)));
        /*
         * 预处理（容错）
         */
        registerFrom = Optional.ofNullable(registerFrom).orElse(Constants.Channel.WECHAT);
        redirect = Optional.ofNullable(redirect).orElse("");
        schema = StringUtils.isBlank(schema) ? getProtocol() : schema;
        String domain = userUiDomain;
        if (Objects.equals(schema, Constants.PROTOCOL_HEAD_HTTPS)) {
            domain = userUiDomainS;
        }
        // 从code获取token
        Merchant merchant = merchantService.findMerchantByName(systemKey);
        AccessTokenResponse token = wechatService.getToken(code);
        log.info("[WeChatController][receiveCodeWithDefault]微信授权及跳转：merchant:{},token:{}", merchant, token);
        if (Objects.isNull(token) || StringUtils.isBlank(token.getOpenId())) {
            // 让用户登录，不关联微信, 构造不关联微信的url
            redirectNormalUrl(response, merchant, registerFrom, domain);
            return;
        }
        // 获取已授权信息 - db
        WechatUserInfo userInfoInDb = wechatService.findWechatUserInfoFromDb(token.getOpenId());
        log.info("[WeChatController][receiveCodeWithDefault]微信授权及跳转：WechatUserInfo - from DB:{}", userInfoInDb);
        // welcome 首次登录
        if (Objects.isNull(userInfoInDb)) {
            WechatUserInfo userInfo = wechatService.getWechatUserInfoFromWechatServer(token.getAccessToken(), token.getOpenId());
            log.info("[WeChatController][receiveCodeWithDefault]微信授权及跳转：WechatUserInfo - from wechat api:{}", userInfo);
            if (Objects.isNull(userInfo) || StringUtils.isBlank(userInfo.getOpenId())) {
                // 让用户登录，不关联微信, 构造不关联微信的url
                // 如果有自定义跳转链接
                redirectNormalUrl(response, merchant, registerFrom, domain);
                return;
            }
            try {
                userInfo = wechatService.saveWechatUserInfo(userInfo);
            } catch (Exception e) {
                log.warn("微信用户首次登陆，保存userInfo异常，执行操作：Nick置为*并重新保存.", e);
                userInfo.setNickName(EmojiUtil.BYTE_4_REPLACE_TEMPLATE);
                userInfo = wechatService.saveWechatUserInfo(userInfo);
            }
            if (customRedirect != null) {
                customRedirectWechatLoginUrlWithoutLogin(response, merchant, userInfo, registerFrom, customRedirect);
            } else {
                redirectWechatLoginUrlWithoutLogin(response, merchant, userInfo, registerFrom, domain);
            }
            return;
        }
        if (Objects.isNull(userInfoInDb.getUserId())) {
            // 用户已经微信登录了，但是没有关联信用钱包，跳转到注册页面
            if (customRedirect != null) {
                customRedirectWechatLoginUrlWithoutLogin(response, merchant, userInfoInDb, registerFrom, customRedirect);
            } else {
                redirectWechatLoginUrlWithoutLogin(response, merchant, userInfoInDb, registerFrom, domain);
            }
            return;
        }
        User user = userService.findById(userInfoInDb.getUserId());
        // 用户不存在或已注销
        if (Objects.isNull(user) || !user.getEnable()) {
            // 用户已经微信登录了，但是关联信用钱包用户信息无效，跳转到注册页面
            if (customRedirect != null) {
                customRedirectWechatLoginUrlWithoutLogin(response, merchant, userInfoInDb, registerFrom, customRedirect);
            } else {
                redirectWechatLoginUrlWithoutLogin(response, merchant, userInfoInDb, registerFrom, domain);
            }
            return;
        }
        // 已经关联了用户
        // create session, 登进去，该怎么玩怎么玩。
        String redirectUrl = createUserSession(user, merchant, redirect, domain, registerFrom);
        response.setHeader("Location", redirectUrl);
        response.setStatus(HttpStatus.SC_MOVED_PERMANENTLY);
    }

    /**
     * @yapi unknown
     * @Deprecated 20210318
     */
    @Deprecated
    @RequestMapping("/redirectForTest")
    public void redirectForTest(String redirect, HttpServletRequest request, HttpServletResponse response) throws MalformedURLException {
        URL url = new URL(redirect);
        String host = url.getHost();
        if (!host.contains(Constants.DOCKER_DOMAIN)) {
            log.error("redirect 非法请求, host:{}", host);
            return;
        }
        String queryString = request.getQueryString();
        String redirectUrl = redirect + "?" + queryString;
        StringBuilder html = new StringBuilder();
        html.append("<!DOCTYPE html>\n" +
                "<html>\n" +
                "<head>\n" +
                "    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\n" +
                "</head>\n" +
                "<body>");
        html.append(String.format("<a href=\"%s\">让我们走</a>", redirectUrl));
        html.append("</body>\n" +
                "</html>");
        response.setContentType("text/html;charset=utf-8");
        try {
            PrintWriter out = response.getWriter();
            out.write(html.toString());
            out.close();
        } catch (IOException e) {
            log.error("测试：重定向失败", e);
        }
    }

    /**
     * 通过redirect_url获取code
     *
     * @param response
     * @return
     * @yapi unknown
     */
    @RequestMapping("/receiveCode/{key}")
    public void receiveCodeNoRedirect(String code, @PathVariable(value = "key") String systemKey, HttpServletRequest request, HttpServletResponse response) {
        Long registerFrom = null;
        String redirect = null;
        String schema = Constants.PROTOCOL_HEAD_HTTP;
        if (Objects.equals(Constants.PROTOCOL_HEAD_HTTPS, request.getScheme())) {
            schema = Constants.PROTOCOL_HEAD_HTTPS;
        }
        receiveCodeWithDefault(code, systemKey, schema, registerFrom, redirect, redirect, response);
    }

    private String createUserSession(User user, Merchant merchant, String redirect, String domain, Long registerFrom) {
        log.info("[WeChatController][createUserSession]微信授权及跳转：user:{},merchant:{},redirect:{},domain:{},registerFrom:{}", user, merchant, redirect, domain, registerFrom);
        LoginProperties loginProperties = new LoginProperties("", 4, Constants.Channel.WECHAT, registerFrom, String.valueOf(Constants.Channel.WECHAT), merchant.getId(), merchant.getName(), null);
        if (StringUtils.isEmpty(redirect) || Constants.REDIRECT.equals(redirect)) {
            log.info("微信登录:redirect为null,走正常流程.");
            if (Constants.MERCHANT_BAITIAO.equals(merchant.getName())) {
                return loginInWechatWithSessionCreated(user, merchant, "cashTarget5", Constants.Channel.BAITIAO, domain, Constants.Channel.WECHAT);
            } else if (Constants.MERCHANT_WECHAT_PAY.equals(merchant.getName())) {
                AuthBean authBean = sessionService.createSession(user, loginProperties);
                return domain + "/landing?token=" + authBean.getToken() + "&registerFrom=" + registerFrom + "&channelId=" + Constants.Channel.WECHAT + "&key=" + merchant.getName() + "&target=cashTarget5";
            } else {
                return loginInWechatWithSessionCreated(user, merchant, "cashTarget4", 1L, domain, registerFrom);
            }
        } else if (Constants.LOCAL.equals(redirect)) {
            log.info("微信登录:redirect不为null,创建session跳到指定前端页面.");
            AuthBean authBean = sessionService.createSession(user, loginProperties);
            log.info("微信登录:跳转地址{}", domain + "/weixin/callback?phoneNo=" + user.getPhoneNo() + "&token=" + authBean.getToken());
            Long channelId = Constants.MERCHANT_BAITIAO.equals(merchant.getName()) ? Constants.Channel.BAITIAO : 1L;
            String target = Constants.MERCHANT_BAITIAO.equals(merchant.getName()) ? "cashTarget5" : "cashTarget4";
            return domain + "/landing?token=" + authBean.getToken() + "&registerFrom=" + registerFrom +
                    "&channelId=" + channelId + "&key=" + merchant.getName() + "&target=" + target + "&isWechat=true";
        } else if(Constants.REDIRECT_ORDER.equals(redirect)){
            log.info("微信登录:redirect为orders,创建session跳到指定前端页面.(兼容多订单页面)");
            AuthBean authBean = sessionService.createSession(user, loginProperties);
            return String.format("%s/loan-list?token=%s&registerFrom=%s&key=%s", xjdDomain, authBean.getToken(), registerFrom, merchant.getName());
        }
        return null;
    }

    private String loginInWechatWithSessionCreated(User user, Merchant merchant, String target, Long channelId, String domain, Long registerFrom) {
        LoginProperties loginProperties = new LoginProperties("", 4, channelId, registerFrom, String.valueOf(Constants.Channel.WECHAT), merchant.getId(), merchant.getName(), null);
        AuthBean authBean = sessionService.createSession(user, loginProperties);
        log.info("[WeChatController][loginInWechatWithSessionCreated]微信授权及跳转：user:{},merchant:{},target:{},channelId:{},domain:{},registerFrom:{}", user, merchant, target, channelId, domain, registerFrom);
        return domain + "/landing?token=" + authBean.getToken() + "&registerFrom=" + registerFrom + "&channelId=" + channelId + "&key=" + merchant.getName() + "&target=" + target;
    }

    private void redirectWechatLoginUrlWithoutLogin(HttpServletResponse response, Merchant merchant, WechatUserInfo userInfo, Long registerFrom, String domain) {
        String redirectUrl = assembleWechatRedirectUrl(merchant, userInfo, registerFrom, domain);
        log.info("[WeChatController][redirectWechatLoginUrlWithoutLogin]微信授权及跳转：redirectUrl:[{}]", redirectUrl);
        response.setHeader("Location", redirectUrl);
        response.setStatus(HttpStatus.SC_MOVED_PERMANENTLY);
    }

    private void redirectNormalUrl(HttpServletResponse response, Merchant merchant, Long registerFrom, String domain) {
        String redirectUrl = assembleNormalRedirectUrl(merchant, registerFrom, domain);
        log.info("[WeChatController][redirectNormalUrl]微信授权及跳转： redirectUrl:[{}]", redirectUrl);
        response.setHeader("Location", redirectUrl);
        response.setStatus(HttpStatus.SC_MOVED_PERMANENTLY);
    }

    private void customRedirectWechatLoginUrlWithoutLogin(HttpServletResponse response, Merchant merchant, WechatUserInfo userInfo, Long registerFrom, String customRedirect) {
        String redirectUrl =  customRedirect + "?registerFrom= " + registerFrom + "&channelId=1&isWechat=true&openId=" + userInfo.getOpenId();
        log.info("[WeChatController][redirectWechatLoginUrlWithoutLogin]微信授权及自定义跳转：redirectUrl:[{}]", redirectUrl);
        response.setHeader("Location", redirectUrl);
        response.setStatus(HttpStatus.SC_MOVED_PERMANENTLY);
    }

    private String assembleNormalRedirectUrl(Merchant merchant, Long registerFrom, String domain) {
        if (Constants.MERCHANT_BAITIAO.equals(merchant.getName())) {
            if (registerFrom == Constants.Channel.HENGCHANG || registerFrom == Constants.Channel.WECHAT) {
                return domain + "/landing?key=baitiao&target=cashTarget5&channelId=222&registerFrom=" + registerFrom;
            } else {
                return domain + "/landing?key=baitiao&target=cashTarget5&channelId=222&registerFrom=198";
            }
        } else if (Constants.MERCHANT_WECHAT_PAY.equals(merchant.getName())) {
            return domain + "/landing?key=wechat-pay&target=cashTarget5&channelId=1&page=landing/4&registerFrom=" + registerFrom;
        } else {
            return domain + "/landing?key=xyqb&target=cashTarget4&channelId=1&registerFrom=" + registerFrom;
        }
    }

    private String assembleWechatRedirectUrl(Merchant merchant, WechatUserInfo userInfo, Long registerFrom, String domain) {
        if (Constants.MERCHANT_BAITIAO.equals(merchant.getName())) {
            if (registerFrom == Constants.Channel.HENGCHANG || registerFrom == Constants.Channel.WECHAT) {
                return domain + "/landing?key=baitiao&target=cashTarget5&registerFrom=" + registerFrom + "&channelId=222&isWechat=true&openId=" + userInfo.getOpenId();
            } else {
                return domain + "/landing?key=baitiao&target=cashTarget5&registerFrom=198&channelId=222&isWechat=true&openId=" + userInfo.getOpenId();
            }
        } else if (Constants.MERCHANT_WECHAT_PAY.equals(merchant.getName())) {
            return domain + "/landing?key=wechat-pay&target=cashTarget5&page=landing/4&registerFrom= " + registerFrom + "&channelId=1&isWechat=true&openId=" + userInfo.getOpenId();
        } else {
            return domain + "/landing?key=xyqb&target=cashTarget4&registerFrom= " + registerFrom + "&channelId=1&isWechat=true&openId=" + userInfo.getOpenId();
        }
    }

    @RequestMapping("/active_redirect")
    public void forwardWechatRequest(HttpServletRequest request, HttpServletResponse response) {
        String redirectUrl = xyqbDomainStr + "/api/coupon/activity/wechat/rent_house/bargain?";
        Enumeration<String> parameterNames = request.getParameterNames();
        ImmutableList.Builder<String> builder = ImmutableList.builder();
        while (parameterNames.hasMoreElements()) {
            String name = parameterNames.nextElement();
            builder.add(name.concat("=").concat(request.getParameter(name)));
        }
        redirectUrl = redirectUrl.concat(Joiner.on("&").join(builder.build()));
        response.setHeader("Location", redirectUrl);
        response.setStatus(HttpStatus.SC_MOVED_PERMANENTLY);
    }


}
