package com.apobates.forum.trident.controller.helper;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Optional;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.apobates.forum.member.entity.Member;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.TypeMismatchException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.NoHandlerFoundException;
import com.apobates.forum.member.api.service.MemberService;
import com.apobates.forum.member.storage.OnlineMemberStorage;
import com.apobates.forum.member.storage.core.MemberSessionBean;
import com.apobates.forum.member.storage.core.MemberSessionBeanBuilder;
import com.apobates.forum.strategy.exception.StrategyException;
import com.apobates.forum.strategy.exception.VerificaFailException;
import com.apobates.forum.trident.exception.BorbidMemberRegisterException;
import com.apobates.forum.trident.exception.ForumValidateException;
import com.apobates.forum.trident.exception.MemberFreezeException;
import com.apobates.forum.trident.exception.PermissionLostException;
import com.apobates.forum.trident.exception.RequestLostTokenException;
import com.apobates.forum.trident.exception.ResourceNotFoundException;
import com.apobates.forum.utils.Commons;
import com.apobates.forum.utils.TipMessage;
import com.apobates.forum.utils.TipMessageLevelEnum;
/**
 * 控制器共享
 * @author xiaofanku@live.cn
 * @since 20171108
 */
@ControllerAdvice
public class CommonControllerAdvice {
    @Autowired
    private MemberService memberService;
    @Autowired
    private OnlineMemberStorage onlineMemberStorage;
	@Value("${site.domain}")
	private String siteDomain;
    private final static Logger logger=LoggerFactory.getLogger(CommonControllerAdvice.class);
    //有什么显示什么
    //不作还原
    @ModelAttribute
    public MemberSessionBean getSessionBean(HttpServletRequest request, HttpServletResponse response){
        logger.info("[CCA]EXE Sequence: 2");
        // Cookie
        MemberSessionBean mbean = onlineMemberStorage.getInstance(request, "CommonControllerAdvice").orElse(MemberSessionBeanBuilder.empty().build(Commons.getRequestIp(request), "CommonControllerAdvice"));
        if (mbean.getMid()>0) {
            //总要查一下看一看有没变化
            Optional<Member> data = memberService.get(mbean.getMid());
            if (data.isPresent()) {
                Member obj = data.get();
                if(obj.getMrole()!=mbean.getRole() || obj.getMgroup()!=mbean.getGroup() || obj.getStatus()!=mbean.getStatus()){
                    mbean = mbean.refact(obj.getMgroup(), obj.getMrole(), obj.getStatus()); //为什么要更新一下角色,组,状态?这三者随时可能发生变化@20200502
                    onlineMemberStorage.refresh(request, response, obj.getStatus(), obj.getMgroup(), obj.getMrole());
                }
            }
        }
        return mbean;
    }
    //https://stackoverflow.com/questions/24498662/howto-handle-404-exceptions-globally-using-spring-mvc-configured-using-java-base
    //https://stackoverflow.com/questions/2066946/trigger-404-in-spring-mvc-controller
    @ExceptionHandler(value={ResourceNotFoundException.class, NoHandlerFoundException.class})
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ModelAndView getModuleVisitException(
            HttpServletRequest request, 
            HttpServletResponse response, 
            Exception e){
        if(logger.isDebugEnabled()){
            logger.debug(e.getMessage());
        }
        e.printStackTrace();
        return normal(e, e.getMessage(), request, response, "404");
    }
    
    @ExceptionHandler(value={StrategyException.class, VerificaFailException.class, MemberFreezeException.class, PermissionLostException.class, BorbidMemberRegisterException.class, RequestLostTokenException.class})
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public ModelAndView getStrategyExecuteException(
            HttpServletRequest request, 
            HttpServletResponse response, 
            Exception e){
        if(logger.isDebugEnabled()){
            logger.debug(e.getMessage());
        }
        e.printStackTrace();
        return normal(e, e.getMessage(), request, response, "401");
    }
    
    @ExceptionHandler(value={
            ForumValidateException.class, 
            MissingServletRequestPartException.class, 
            MethodArgumentNotValidException.class, 
            HttpMessageNotWritableException.class, 
            HttpMessageNotReadableException.class, 
            TypeMismatchException.class, 
            ServletRequestBindingException.class, 
            MissingServletRequestParameterException.class, 
            IllegalAccessException.class})
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ModelAndView getExecteException(
            HttpServletRequest request, 
            HttpServletResponse response, 
            Exception e){
        if(logger.isDebugEnabled()){
            logger.debug(e.getMessage());
        }
        e.printStackTrace();
        return normal(e, e.getMessage(), request, response, "400");
    }
    
    @ExceptionHandler(value={
            java.lang.NoClassDefFoundError.class,
            java.lang.IllegalStateException.class,
            org.eclipse.persistence.exceptions.DatabaseException.class, 
            javax.persistence.RollbackException.class})
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ModelAndView getJpaDBExecteException(
            HttpServletRequest request, 
            HttpServletResponse response, 
            Exception e){
        if(logger.isDebugEnabled()){
            logger.debug(e.getMessage());
        }
        //logger.info("服务器组件发生错误", e);
        e.printStackTrace();
        return normal(e, "服务器组件发生错误", request, response, "500");
    }
    private ModelAndView normal(
            Exception e, 
            String message, 
            HttpServletRequest request, 
            HttpServletResponse response, 
            String viewName){
        if(isAjaxRequest(request)){
            TipMessage tipMsg = new TipMessage(message, TipMessageLevelEnum.ERR.getLevel(), request.getRequestURL().toString());
            try {
                //可能引起FF的XML Parsing Error: not well-formed
                response.setContentType("application/json");
                response.setCharacterEncoding("UTF-8");
                PrintWriter writer = response.getWriter();
                writer.write(tipMsg.toJsonString());
                writer.flush();
            } catch (IOException ex) {
                if(logger.isDebugEnabled()){
                    logger.debug("["+viewName+"]输出JSON格式消息", ex);
                }
            }
            return null;
        }
        ModelAndView model=new ModelAndView();
        model.addObject("errors", message);
        model.addObject("url", request.getRequestURL());
        model.addObject("referer", request.getHeader("referer"));
        model.addObject("paramer", request.getParameterMap());
        model.addObject("exception", e);
        model.addObject("debug", logger.isDebugEnabled());
        model.addObject("BASE", siteDomain); //request.getContextPath()
        model.setViewName(viewName);
        return model;
    }
    private boolean isAjaxRequest(HttpServletRequest request){
          return ((request.getHeader("accept").contains("application/json") || (request.getHeader("X-Requested-With") != null 
                  && 
               request.getHeader("X-Requested-With").contains("XMLHttpRequest"))));
    }
}
