package com.nantian.sc.cloud.admin.api.log;

import cn.hutool.core.util.URLUtil;
import cn.hutool.json.JSONUtil;
import com.nantian.sc.cloud.admin.api.entity.OperationLog;
import com.nantian.sc.cloud.admin.api.feign.RemoteOperationLogService;
import lombok.AllArgsConstructor;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Objects;

@Aspect
//@Component
@AllArgsConstructor
public class OperationLogAspect {

    @Autowired
    private RemoteOperationLogService remoteOperationLogService;

    /**
     * 此处的切点是包名
     */
    @Pointcut("@annotation(com.nantian.sc.cloud.admin.api.log.OperationLogAnnotation)")
    public void operationLog(){}

    /**
     * 环绕增强，相当于MethodInterceptor
     */
    @Around("operationLog()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Object res = null;
        long time = System.currentTimeMillis();
        try {
            res =  joinPoint.proceed();
            time = System.currentTimeMillis() - time;
            return res;
        } finally {
            try {
                //方法执行完成后增加日志
                addOperationLog(joinPoint,res,time);
            }catch (Exception e){
                System.err.println("LogAspect 操作失败：" + e.getMessage());
                e.printStackTrace();
            }
        }
    }

    private void addOperationLog(JoinPoint joinPoint, Object res, long time){
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        Method method = signature.getMethod();
        OperationLogAnnotation annotation = method.getAnnotation(OperationLogAnnotation.class);
        OperationLog operationLog = new OperationLog();
        operationLog.setMethod(signature.getDeclaringTypeName() + "." + signature.getName());
        operationLog.setModule(annotation.module());
        operationLog.setAction(annotation.action());
        operationLog.setRemark(annotation.remark());
        operationLog.setUserDept(annotation.userDept());

        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
        operationLog.setUserAgent(request.getHeader("user-agent"));
        operationLog.setRequestUri(URLUtil.getPath(request.getRequestURI()));
        //operationLog.setRemoteAddr(ServletUtil.getClientIP(request));
        operationLog.setRemoteAddr(getIpAddr(request));

        /*ScUser u= SecurityUtils.getUser();
        operationLog.setUserId(u.getId().toString());
        operationLog.setUserName(u.getUsername());
        operationLog.setUserRealName(u.getUserRealName());
        operationLog.setDeptId(u.getDeptId());*/

        operationLog.setRunTime(time+"");
        operationLog.setCreatTime(new Date());
        operationLog.setReturnValue(JSONUtil.toJsonStr(res));
        //operationLog.setReturnValue(res.toString());
        System.out.println("记录日志:" + operationLog.toString());
        remoteOperationLogService.save(operationLog);
    }

    public String getIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

    @Before("operationLog()")
    public void doBeforeAdvice(JoinPoint joinPoint){
        System.out.println("进入方法前执行.....");

    }

    /**
     * 处理完请求，返回内容
     * @param ret
     */
    @AfterReturning(returning = "ret", pointcut = "operationLog()")
    public void doAfterReturning(Object ret) {
        System.out.println("方法的返回值 : " + ret);
    }

    /**
     * 后置异常通知
     */
    @AfterThrowing("operationLog()")
    public void throwss(JoinPoint jp){
        System.out.println("方法异常时执行.....");
    }

    /**
     * 后置最终通知,final增强，不管是抛出异常或者正常退出都会执行
     */
    @After("operationLog()")
    public void after(JoinPoint jp){
        System.out.println("方法最后执行.....");
    }
}
