package cn.component.logger;

import cn.component.context.SpringContextKit;
import cn.constant.CommonConst;
import cn.pojo.entity.TSysLogOp;
import cn.pojo.entity.TSysUser;
import cn.pojo.vo.LoginUser;
import cn.schedule.core.AsyncManager;
import cn.service.token.JwtTokenService;
import cn.common.annotation.Log;
import cn.common.enums.HttpMethod;
import cn.logger.AsyncLoggingFactory;
import cn.util.core.IsKit;
import cn.util.core.LogKit;
import cn.util.core.TypeKit;
import cn.util.http.IpKit;
import cn.util.core.ServletKit;
import cn.util.string.StringKit;
import cn.util.time.TimeKit;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.HandlerMapping;
import com.alibaba.fastjson2.JSON;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Map;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RestController;

/**
 * 日志，切面处理类 参考：OpLogDetailAspect
 */
@Aspect
@Component
public class LogAspect {
    // 配置织入点
    @Pointcut("@annotation(cn.common.annotation.Log)")
    public void logPointCut() {
    }

    /**
     * 处理完请求后执行
     *
     * @param joinPoint 切点
     */
    @AfterReturning(pointcut = "logPointCut()", returning = "jsonResult")
    public void doAfterReturning(JoinPoint joinPoint, Object jsonResult) {
        handleLog(joinPoint, null, jsonResult);
    }

    /**
     * 拦截异常操作     *
     * @param joinPoint 切点
     * @param e         异常
     */
    @AfterThrowing(value = "logPointCut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Exception e) {
        handleLog(joinPoint, e, null);
    }

    /*
     * 环绕增强，相当于MethodInterceptor
     * proceed为执行方法后返回的值
     @Around("logPointCut()") public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable
     {
     Object res = null;
     try {
     res = joinPoint.proceed();
     return res;
     } finally {
     try {//方法执行完成后增加日志
     handleLog(joinPoint,res);
     }catch (Exception e){
     LogKit.info(e.getMessage());
     }
     }
     }*/
    @Resource
    private JwtTokenService tokenService;
    /***mysql插入带单引号双引号的字符串 会报错*/
    protected void handleLog(final JoinPoint joinPoint, final Exception e, Object jsonResult) {
       /*
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Log annotation = signature.getMethod().getAnnotation(Log.class);
        Method methodInvocation =  signature.getMethod();
        如果注解方法不在 web项目的 controller 类上 则注解不起作用
        if (!checkClassIsController(methodInvocation)) {
            return;
        }*/

        try {
            // 获得注解
            Log controllerLog = getAnnotationLog(joinPoint);
            if (IsKit.isNull(controllerLog)) {
                return;
            }
            HttpServletRequest request = ServletKit.getRequest();
            String ip = IpKit.getIpAddr(request);//127.0.0.1
            /*http://www.localhost:8081/user/query/
              StringKit.removeNoSQLChar(request.getRequestURI()) */
            String url = TypeKit.objToStr(request.getRequestURL());
            String address = IpKit.getRealAddressByIP(ip);//内网IP
            //String reqParam = preHandle(joinPoint,request);//{"tel":["1"]}

            TSysLogOp entity = new TSysLogOp();
            //获取当前的用户 PC端用户
            LoginUser loginUser = SpringContextKit.getBean(JwtTokenService.class).getLoginUser(ServletKit.getRequest());
            if (IsKit.isNotNull(loginUser)) {
                entity.setDeptName(loginUser.getDeptName());
                entity.setOperName(loginUser.getUsername());
            }
            else{//App用户
                String token = request.getHeader(CommonConst.X_ACCESS_TOKEN);
                TSysUser userEntity = tokenService.getAppUser(token) ;
                if (IsKit.isNotNull(userEntity)) {
                    entity.setDeptName(TypeKit.objToStr(userEntity.getDeptId()));
                    entity.setOperName(userEntity.getUserName());
                }
            }

            entity.setStatus(CommonConst.INT_NUM_1);//1成功
            entity.setOperIp(ip);
            entity.setOperLocation(address);
            // 返回参数
            entity.setJsonResult(StringKit.removeNoSQLChar(StringKit.substring(JSON.toJSONString(jsonResult), 0, 300)));
            entity.setOperUrl(url);
            entity.setOperTime(TimeKit.getNowLocalDateTime());
            if (IsKit.isNotNullOrEmpty(e)) {
                entity.setStatus(CommonConst.INT_NUM_0);//0失败
                entity.setErrorMsg(StringKit.substring(e.getMessage(), 0, 2000));
            }
            // 设置方法名称
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = joinPoint.getSignature().getName();
            entity.setMethod(className + "." + methodName + "()");
            // 设置请求方式
            entity.setRequestMethod(request.getMethod());
            // 处理设置注解上的参数
            getControllerMethodDescription(joinPoint, controllerLog, entity);
            // 保存数据库
            AsyncManager.me().execute(AsyncLoggingFactory.insertOpInfo(entity));
        } catch (Exception exp) {
            LogKit.error("异常信息:{}", exp.getMessage());
        }
    }

    /**
     * 获取注解中对方法的描述信息 用于Controller层注解
     */
    public void getControllerMethodDescription(JoinPoint joinPoint, Log log, TSysLogOp entity) {
        //设置操作类型ordinal 从0开始计数
        entity.setBusinessType(log.businessType().ordinal() + 1);
        // 设置标题
        entity.setTitle(log.title());
        // 设置操作人类别
        entity.setOperatorType(log.operatorType().ordinal() + 1);
        // 是否需要保存request，参数和值
        if (log.isSaveRequestData()) {
            // 获取参数的信息，传入到数据库中。
            setRequestValue(joinPoint, entity);
        }
    }

    /**
     * 判断是否需要过滤的对象。
     *
     * @param o 对象信息。
     * @return 如果是需要过滤的对象，则返回true；否则返回false。
     */
    public boolean isFilterObject(final Object o) {
        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse;
    }

    /**
     * 是否存在注解，如果存在就获取
     */
    private Log getAnnotationLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        if (IsKit.isNotNull(method)) {
            return method.getAnnotation(Log.class);
        }
        return null;
    }

    /**
     * 获取请求的参数，放到log中
     */
    private void setRequestValue(JoinPoint joinPoint, TSysLogOp entity) {
        String requestMethod = TypeKit.objToStr(entity.getMethod());
        String params;
        if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {
            params = argsArrayToString(joinPoint.getArgs());
        } else {
            Map<?, ?> paramsMap = (Map<?, ?>) ServletKit.getRequest().getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
            params = paramsMap.toString();
        }
        entity.setOperParam(StringKit.removeNoSQLChar(StringKit.substring(params, 0, 300)));
    }

    /**
     * 参数拼装
     */
    private String argsArrayToString(Object[] paramsArray) {
        StringBuilder params = new StringBuilder();
        for (Object o : paramsArray) {
            if (!isFilterObject(o)) {
                Object jsonObj = JSON.toJSON(o);
                params.append(jsonObj.toString());
                params.append(" ");
            }
        }
        return params.toString().trim();
    }

    /**
     * 检查当前的方法所在类是不是web controller
     * A.isAnnotationPresent(B.class);B类型的注解是否在A类上。在的话返回true
     */
    private Boolean checkClassIsController(Method methodInvocation) {
        boolean isOk = methodInvocation.getDeclaringClass().isAnnotationPresent(Controller.class);
        boolean isOk2 = methodInvocation.getDeclaringClass().isAnnotationPresent(RestController.class);
        return isOk || isOk2;
    }

    /**入参数据*/
    private String preHandle(JoinPoint joinPoint, HttpServletRequest request) {
        String reqParam = "";
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method targetMethod = methodSignature.getMethod();
        Annotation[] annotations = targetMethod.getAnnotations();
        for (Annotation annotation : annotations) {
            //此处可以改成自定义的注解
            if (annotation.annotationType().equals(RequestMapping.class)) {
                reqParam = JSON.toJSONString(request.getParameterMap());
                break;
            }
        }
        return reqParam;
    }

}
