/**
 * Copyright (c) 2011-2014, James Zhan 詹波 (inlet@126.com).
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 */

package com.zzwtec.wechat.sdk.inlet;


import com.jfinal.kit.StrKit;
import com.zzwtec.wechat.sdk.aop.annotation.ZZWAround;
import com.zzwtec.wechat.sdk.aop.annotation.ZZWMsgController;
import com.zzwtec.wechat.sdk.aop.inject.impl.ApiHandler;
import com.zzwtec.wechat.sdk.api.ApiConfigKit;
import com.zzwtec.wechat.sdk.kit.MsgEncryptKit;
import com.zzwtec.wechat.sdk.msg.InMsgParser;
import com.zzwtec.wechat.sdk.msg.in.*;
import com.zzwtec.wechat.sdk.msg.in.card.*;
import com.zzwtec.wechat.sdk.msg.in.event.*;
import com.zzwtec.wechat.sdk.msg.in.speech_recognition.InSpeechRecognitionResults;
import com.zzwtec.wechat.sdk.msg.out.OutMsg;
import com.zzwtec.wechat.sdk.msg.out.OutTextMsg;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;

import static com.zzwtec.wechat.sdk.utils.RenderUtil.renderText;

/**
 * 接收微信服务器消息，自动解析成 InMsg 并分发到相应的处理方法
 */
@ZZWMsgController
public abstract class MsgController {

    private static final Logger log = LoggerFactory.getLogger(MsgController.class);
    @Autowired
    private HttpServletRequest request;

    private static final ThreadLocal<String> INM_SGXMLT_CONTEXT = new ThreadLocal<>();
    private static final ThreadLocal<InMsg> IN_MSGT_CONTEXT = new ThreadLocal<>();

    /**
     * weixin 公众号服务器调用唯一入口，即在开发者中心输入的 URL 必须要指向此 action
     */
    @ZZWAround(ApiHandler.class)
    @RequestMapping
    public @ResponseBody
    ModelAndView index(@RequestBody(required = false) String xmlMsg) throws RuntimeException {

        if (ApiConfigKit.getApiConfig().isEncryptMessage()) {
            xmlMsg = MsgEncryptKit.decrypt(xmlMsg, request.getParameter("timestamp"), request.getParameter("nonce"), request.getParameter("msg_signature"));
        }
        INM_SGXMLT_CONTEXT.set(xmlMsg);

        // 开发模式输出微信服务发送过来的  xml 消息
        if (ApiConfigKit.isDevMode()) {
            log.info("-------------------------------receive start-------------------------------------------------\n");
            log.info("\n接收消息:\n{}",xmlMsg);
            log.info("--------------------------------receive end--------------------------------------------------\n");
        }

        try {
            if (StrKit.isBlank(xmlMsg)) {
                throw new RuntimeException("未接受到微信参数");
            }

            // 解析消息并根据消息类型分发到相应的处理方法
            InMsg msg = getInMsg();

            if (msg instanceof InTextMsg) {
                processInTextMsg((InTextMsg) msg);
            } else if (msg instanceof InImageMsg) {
                processInImageMsg((InImageMsg) msg);
            } else if (msg instanceof InSpeechRecognitionResults){
                //update by unas at 2016-1-29, 由于继承InVoiceMsg，需要在InVoiceMsg前判断类型
                processInSpeechRecognitionResults((InSpeechRecognitionResults) msg);
            } else if (msg instanceof InVoiceMsg) {
                processInVoiceMsg((InVoiceMsg) msg);
            } else if (msg instanceof InVideoMsg) {
                processInVideoMsg((InVideoMsg) msg);
            } else if (msg instanceof InShortVideoMsg){
                //支持小视频
                processInShortVideoMsg((InShortVideoMsg) msg);
            } else if (msg instanceof InLocationMsg) {
                processInLocationMsg((InLocationMsg) msg);
            } else if (msg instanceof InLinkMsg) {
                processInLinkMsg((InLinkMsg) msg);
            } else if (msg instanceof InCustomEvent) {
                processInCustomEvent((InCustomEvent) msg);
            } else if (msg instanceof InFollowEvent) {
                processInFollowEvent((InFollowEvent) msg);
            } else if (msg instanceof InQrCodeEvent) {
                processInQrCodeEvent((InQrCodeEvent) msg);
            } else if (msg instanceof InLocationEvent) {
                processInLocationEvent((InLocationEvent) msg);
            } else if (msg instanceof InMassEvent) {
                processInMassEvent((InMassEvent) msg);
            } else if (msg instanceof InMenuEvent) {
                processInMenuEvent((InMenuEvent) msg);
            } else if (msg instanceof InTemplateMsgEvent) {
                processInTemplateMsgEvent((InTemplateMsgEvent) msg);
            } else if (msg instanceof InShakearoundUserShakeEvent) {
                processInShakearoundUserShakeEvent((InShakearoundUserShakeEvent) msg);
            } else if (msg instanceof InVerifySuccessEvent) {
                processInVerifySuccessEvent((InVerifySuccessEvent) msg);
            } else if (msg instanceof InVerifyFailEvent) {
                processInVerifyFailEvent((InVerifyFailEvent) msg);
            } else if (msg instanceof InPoiCheckNotifyEvent) {
                processInPoiCheckNotifyEvent((InPoiCheckNotifyEvent) msg);
            } else if (msg instanceof InWifiEvent) {
                processInWifiEvent((InWifiEvent) msg);
            } else if (msg instanceof InUserCardEvent) {
                processInUserCardEvent((InUserCardEvent) msg);
            } else if (msg instanceof InUpdateMemberCardEvent) {
                processInUpdateMemberCardEvent((InUpdateMemberCardEvent) msg);
            } else if (msg instanceof InUserPayFromCardEvent) {
                processInUserPayFromCardEvent((InUserPayFromCardEvent) msg);
            } else if (msg instanceof InMerChantOrderEvent) {
                processInMerChantOrderEvent((InMerChantOrderEvent) msg);
            } else if (msg instanceof InCardPassCheckEvent) {
                processInCardPassCheckEvent((InCardPassCheckEvent) msg);
            } else if (msg instanceof InCardPayOrderEvent) {
                processInCardPayOrderEvent((InCardPayOrderEvent) msg);
            } else if (msg instanceof InCardSkuRemindEvent) {
                processInCardSkuRemindEvent((InCardSkuRemindEvent) msg);
            } else if (msg instanceof InUserConsumeCardEvent) {
                processInUserConsumeCardEvent((InUserConsumeCardEvent) msg);
            } else if (msg instanceof InUserGetCardEvent) {
                processInUserGetCardEvent((InUserGetCardEvent) msg);
            } else if (msg instanceof InUserGiftingCardEvent) {
                processInUserGiftingCardEvent((InUserGiftingCardEvent) msg);
            } else if (msg instanceof InNotDefinedEvent) {
                log.error("未能识别的事件类型。 消息 xml 内容为：\n" + xmlMsg);
                processIsNotDefinedEvent((InNotDefinedEvent) msg);
            } else if (msg instanceof InNotDefinedMsg) {
                log.error("未能识别的消息类型。 消息 xml 内容为：\n" + xmlMsg);
                processIsNotDefinedMsg((InNotDefinedMsg) msg);
            }
        }catch (Exception e){
            log.error("微信流程处理失败!",e);
            throw e;

        }finally {
            INM_SGXMLT_CONTEXT.remove();
            IN_MSGT_CONTEXT.remove();
        }


        return null;
    }

    /**
     * 在接收到微信服务器的 InMsg 消息后后响应 OutMsg 消息
     *
     * @param outMsg 输出对象
     */
    public void render(OutMsg outMsg) {
        String outMsgXml = outMsg.toXml();
        // 开发模式向控制台输出即将发送的 OutMsg 消息的 xml 内容
        if (ApiConfigKit.isDevMode()) {
            log.info("-------------------------------send start-------------------------------------------------\n");
            log.info("\n发送消息:\n{}",outMsgXml);
            log.info("--------------------------------send end--------------------------------------------------\n");
        }

        // 是否需要加密消息
        if (ApiConfigKit.getApiConfig().isEncryptMessage()) {
            outMsgXml = MsgEncryptKit.encrypt(outMsgXml, request.getParameter("timestamp"), request.getParameter("nonce"));
        }

        renderText(outMsgXml, "text/xml");
    }

    /**
     * 消息输出
     * @param content 输出的消息
     */
    public void renderOutTextMsg(String content) {
        OutTextMsg outMsg = new OutTextMsg(getInMsg());
        outMsg.setContent(content);
        render(outMsg);
    }

    public InMsg getInMsg() {
        InMsg inMsg = IN_MSGT_CONTEXT.get();
        if (inMsg == null){
            inMsg = InMsgParser.parse(INM_SGXMLT_CONTEXT.get());
            IN_MSGT_CONTEXT.set(inMsg);
        }
        return inMsg;
    }

    /**
     * 处理接收到的文本消息
     * @param inTextMsg 处理接收到的文本消息
     */
    protected abstract void processInTextMsg(InTextMsg inTextMsg);

    /**
     * 处理接收到的图片消息
     * @param inImageMsg 处理接收到的图片消息
     */
    protected abstract void processInImageMsg(InImageMsg inImageMsg);

    /**
     * 处理接收到的语音消息
     * @param inVoiceMsg 处理接收到的语音消息
     */
    protected abstract void processInVoiceMsg(InVoiceMsg inVoiceMsg);

    /**
     * 处理接收到的视频消息
     * @param inVideoMsg 处理接收到的视频消息
     */
    protected abstract void processInVideoMsg(InVideoMsg inVideoMsg);

    /**
     * 处理接收到的小视频消息
     * @param inShortVideoMsg 处理接收到的小视频消息
     */
    protected abstract void processInShortVideoMsg(InShortVideoMsg inShortVideoMsg);

    /**
     * 处理接收到的地址位置消息
     * @param inLocationMsg 处理接收到的地址位置消息
     */
    protected abstract void processInLocationMsg(InLocationMsg inLocationMsg);

    /**
     * 处理接收到的链接消息
     * @param inLinkMsg 处理接收到的链接消息
     */
    protected abstract void processInLinkMsg(InLinkMsg inLinkMsg);

    /**
     * 处理接收到的多客服管理事件
     * @param inCustomEvent 处理接收到的多客服管理事件
     */
    protected abstract void processInCustomEvent(InCustomEvent inCustomEvent);

    /**
     * 处理接收到的关注/取消关注事件
     * @param inFollowEvent 处理接收到的关注/取消关注事件
     */
    protected abstract void processInFollowEvent(InFollowEvent inFollowEvent);

    /**
     * 处理接收到的扫描带参数二维码事件
     * @param inQrCodeEvent 处理接收到的扫描带参数二维码事件
     */
    protected abstract void processInQrCodeEvent(InQrCodeEvent inQrCodeEvent);

    /**
     * 处理接收到的上报地理位置事件
     * @param inLocationEvent 处理接收到的上报地理位置事件
     */
    protected abstract void processInLocationEvent(InLocationEvent inLocationEvent);

    /**
     * 处理接收到的群发任务结束时通知事件
     * @param inMassEvent 处理接收到的群发任务结束时通知事件
     */
    protected abstract void processInMassEvent(InMassEvent inMassEvent);

    /**
     * 处理接收到的自定义菜单事件
     * @param inMenuEvent 处理接收到的自定义菜单事件
     */
    protected abstract void processInMenuEvent(InMenuEvent inMenuEvent);

    /**
     * 处理接收到的语音识别结果
     * @param inSpeechRecognitionResults 处理接收到的语音识别结果
     */
    protected abstract void processInSpeechRecognitionResults(InSpeechRecognitionResults inSpeechRecognitionResults);

    /**
     * 处理接收到的模板消息是否送达成功通知事件
     * @param inTemplateMsgEvent 处理接收到的模板消息是否送达成功通知事件
     */
    protected abstract void processInTemplateMsgEvent(InTemplateMsgEvent inTemplateMsgEvent);

    /**
     * 处理微信摇一摇事件
     * @param inShakearoundUserShakeEvent 处理微信摇一摇事件
     */
    protected abstract void processInShakearoundUserShakeEvent(InShakearoundUserShakeEvent inShakearoundUserShakeEvent);

    /**
     * 资质认证成功 || 名称认证成功 || 年审通知 || 认证过期失效通知
     * @param inVerifySuccessEvent 资质认证成功 || 名称认证成功 || 年审通知 || 认证过期失效通知
     */
    protected abstract void processInVerifySuccessEvent(InVerifySuccessEvent inVerifySuccessEvent);

    /**
     * 资质认证失败 || 名称认证失败
     * @param inVerifyFailEvent 资质认证失败 || 名称认证失败
     */
    protected abstract void processInVerifyFailEvent(InVerifyFailEvent inVerifyFailEvent);

    /**
     * 门店在审核事件消息
     * @param inPoiCheckNotifyEvent 门店在审核事件消息
     */
    protected abstract void processInPoiCheckNotifyEvent(InPoiCheckNotifyEvent inPoiCheckNotifyEvent);

    /**
     * WIFI连网后下发消息 by unas at 2016-1-29
     * @param inWifiEvent WIFI连网后下发消息
     */
    protected abstract void processInWifiEvent(InWifiEvent inWifiEvent);

    /**
     * 1. 微信会员卡二维码扫描领取接口
     * 2. 微信会员卡激活接口
     * 3. 卡券删除事件推送
     * 4. 从卡券进入公众号会话事件推送
     * @param inUserCardEvent InUserCardEvent
     */
    protected abstract void processInUserCardEvent(InUserCardEvent inUserCardEvent);

    /**
     * 微信会员卡积分变更
     * @param inUpdateMemberCardEvent 微信会员卡积分变更
     */
    protected abstract void processInUpdateMemberCardEvent(InUpdateMemberCardEvent inUpdateMemberCardEvent);

    /**
     * 微信会员卡快速买单
     * @param inUserPayFromCardEvent 微信会员卡快速买单
     */
    protected abstract void processInUserPayFromCardEvent(InUserPayFromCardEvent inUserPayFromCardEvent);

    /**
     * 微信小店订单支付成功接口消息
     * @param inMerChantOrderEvent 微信小店订单支付成功接口消息
     */
    protected abstract void processInMerChantOrderEvent(InMerChantOrderEvent inMerChantOrderEvent);

    //
    /**
     * 没有找到对应的事件消息
     * @param inNotDefinedEvent 没有对应的事件消息
     */
    protected abstract void processIsNotDefinedEvent(InNotDefinedEvent inNotDefinedEvent);

    /**
     * 没有找到对应的消息
     * @param inNotDefinedMsg 没有对应消息
     */
    protected abstract void processIsNotDefinedMsg(InNotDefinedMsg inNotDefinedMsg);

    /**
     * 卡券转赠事件推送
     * @param msg 卡券转赠事件推送
     */
    protected abstract void processInUserGiftingCardEvent(InUserGiftingCardEvent msg);

    /**
     * 卡券领取事件推送
     * @param msg 卡券领取事件推送
     */
    protected abstract void processInUserGetCardEvent(InUserGetCardEvent msg);

    /**
     * 卡券核销事件推送
     * @param msg 卡券核销事件推送
     */
    protected abstract void processInUserConsumeCardEvent(InUserConsumeCardEvent msg);

    /**
     * 卡券库存报警事件
     * @param msg 卡券库存报警事件
     */
    protected abstract void processInCardSkuRemindEvent(InCardSkuRemindEvent msg);

    /**
     * 券点流水详情事件
     * @param msg 券点流水详情事件
     */
    protected abstract void processInCardPayOrderEvent(InCardPayOrderEvent msg);

    /**
     * 审核事件推送
     * @param msg 审核事件推送
     */
    protected abstract void processInCardPassCheckEvent(InCardPassCheckEvent msg);

    public HttpServletRequest getRequest() {
        return request;
    }
}













