/*
 * Copyright (c) 2016-2017, Guangshan ([email protected]) and the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.mxixm.fastboot.weixin.module.message.support;

import com.mxixm.fastboot.weixin.annotation.WxAsyncMessage;
import com.mxixm.fastboot.weixin.annotation.WxMapping;
import com.mxixm.fastboot.weixin.module.message.WxGroupMessage;
import com.mxixm.fastboot.weixin.module.message.WxMessage;
import com.mxixm.fastboot.weixin.module.message.WxTemplateMessage;
import com.mxixm.fastboot.weixin.module.message.WxUserMessage;
import com.mxixm.fastboot.weixin.module.message.parameter.WxMessageParameter;
import com.mxixm.fastboot.weixin.util.WxMessageUtils;
import com.mxixm.fastboot.weixin.util.WxWebUtils;
import org.springframework.core.MethodParameter;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.ParameterizedType;

/**
 * FastBootWeixin WxSyncMessageReturnValueHandler
 * 其实@WxAsyncMessage并不是通过这个类发送的,这里应该叫做CustomerMessage,这里的消息不需要异步发送,直接调用发送即可
 * todo 按照上面的逻辑重构
 * done 已重构
 * 该类不处理声明为被动返回的消息,即返回XML消息,其他类型都通过本类处理
 * 而对于消息的发送,都采用异步的方式我觉得没有问题,如果同步发送,报错之后响应给微信,微信会返回给用户服务器故障,这是不友好的,所以这里固定异步发送。
 * 但是有点区别,如果是普通的@RequestMapping呢,可能就不需要异步发送了
 * todo 分离出来两种,WxMappingReturnValueHandler和WxMessageReturnValueHandler
 *
 * @author Guangshan
 * @date 2017/8/20 22:53
 * @update 2018-5-24 17:24:21
 * @since 0.6.1
 */
@Deprecated
public abstract class AbstractWxMessageReturnValueHandler implements HandlerMethodReturnValueHandler {

    /**
     * 有WxAsyncMessage注解且
     * 返回值是WxMessage的子类
     * 或者是CharSequence的子类,且有注解WxButton或者WxMessageMapping
     * 这里其实应该判断反向的,即不需要Async发送的。只有几个用户类型的消息不需要异步发送
     *
     * @param returnType
     * @return result
     */
    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        // 是否需要xml格式的相应,如果需要xml响应,则不通过这个处理器处理
        // 判断逻辑是方法声明为WxMaping且支持XML响应且没有显式声明为WxAsyncMessage
        boolean returnXml = WxMessageUtils.supportsXmlResponse(returnType.getParameterType()) &&
                returnType.hasMethodAnnotation(WxMapping.class) && !returnType.hasMethodAnnotation(WxAsyncMessage.class);
        // 逻辑分开,快速返回,防止代码不易理解
        if (returnXml) {
            return false;
        }
        // 如果不需要响应为xml,则进入内部判断
        return supportsReturnTypeInternal(returnType);
    }

    /**
     * 额外进行判断
     * @param returnType
     * @return result
     */
    protected abstract boolean supportsReturnTypeInternal(MethodParameter returnType);

    @Override
    public void handleReturnValue(Object returnValue, MethodParameter returnType,
                                  ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
        // @WxAsyncMessage声明的Handler恒为null,这里提供支持
        if (returnValue != null) {
            handlReturnValueInternal(returnValue);
        }
        mavContainer.setRequestHandled(true);
        HttpServletResponse servletResponse = webRequest.getNativeResponse(HttpServletResponse.class);
        ServletServerHttpResponse outputMessage = new ServletServerHttpResponse(servletResponse);
        outputMessage.getBody();


    }

    /**
     * 处理returnValue
     * @param returnValue
     */
    protected abstract void handlReturnValueInternal(Object returnValue);

    /**
     * 只查找一层
     * @param returnType 返回类型
     * @return result
     */
    protected Class getGenericType(MethodParameter returnType) {
        boolean isIterable = Iterable.class.isAssignableFrom(returnType.getParameterType());
        if (isIterable) {
            if (returnType.getGenericParameterType() instanceof ParameterizedType) {
                return (Class) ((ParameterizedType) returnType.getGenericParameterType()).getActualTypeArguments()[0];
            } else {
                return returnType.getParameterType();
            }
        }
        if (returnType.getParameterType().isArray()) {
            return returnType.getParameterType().getComponentType();
        }
        return returnType.getParameterType();
    }

    /**
     * 有WxAsyncMessage注解且
     * 返回值是WxMessage的子类
     * 或者是CharSequence的子类,且有注解WxButton或者WXMessageMapping
     * 这里其实应该判断反向的,即不需要Async发送的。只有几个用户类型的消息不需要异步发送
     *
     * @param returnType
     * @return result
     */
    @Deprecated
    public boolean supportsReturnTypeOld(MethodParameter returnType) {
        // 如果是iterable或者array,都只能作为asyncMessage消息处理
        boolean isIterableType = Iterable.class.isAssignableFrom(returnType.getParameterType());
        boolean isArrayType = returnType.getParameterType().isArray();
        // 如果是群发消息,只能作为asyncMessage消息处理
        boolean isGroupMessage = WxGroupMessage.class.isAssignableFrom(returnType.getParameterType());
        // 如果是模板消息,只能作为asyncMessage消息处理
        boolean isTemplateMessage = WxTemplateMessage.class.isAssignableFrom(returnType.getParameterType());
        // 如果是模板消息,只能作为asyncMessage消息处理
        boolean isMiniProgramMessage = WxUserMessage.MiniProgram.class.isAssignableFrom(returnType.getParameterType());
        // 理论上WxAsyncMessage已经被上层处理过了,这里保险起见再处理一次
        boolean needAsyncSend = isIterableType || isArrayType || isGroupMessage || isTemplateMessage || isMiniProgramMessage;
        Class realType = getGenericType(returnType);
        boolean isWxMessage = WxMessage.class.isAssignableFrom(realType);
        boolean isWxStringMessage = CharSequence.class.isAssignableFrom(realType) &&
                returnType.hasMethodAnnotation(WxMapping.class);
        return needAsyncSend && (isWxMessage || isWxStringMessage);
    }

}