package com.yeejoin.amos.boot.biz.common.aop;

import com.alibaba.fastjson.JSONObject;
import com.yeejoin.amos.boot.biz.common.bo.CompanyBo;
import com.yeejoin.amos.boot.biz.common.bo.DepartmentBo;
import com.yeejoin.amos.boot.biz.common.bo.ReginParams;
import com.yeejoin.amos.boot.biz.common.bo.RoleBo;
import com.yeejoin.amos.boot.biz.common.interceptors.PermissionInterceptor;
import com.yeejoin.amos.boot.biz.common.interceptors.PermissionInterceptorContext;
import com.yeejoin.amos.boot.biz.common.utils.RedisKey;
import com.yeejoin.amos.boot.biz.common.utils.RedisUtils;
import com.yeejoin.amos.component.feign.model.FeignClientResult;
import com.yeejoin.amos.feign.privilege.Privilege;
import com.yeejoin.amos.feign.privilege.model.AgencyUserModel;
import com.yeejoin.amos.feign.privilege.model.CompanyModel;
import com.yeejoin.amos.feign.privilege.model.DepartmentModel;
import com.yeejoin.amos.feign.privilege.model.RoleModel;
import org.apache.commons.lang3.ObjectUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.typroject.tyboot.core.auth.exception.AuthException;
import org.typroject.tyboot.core.foundation.context.RequestContext;
import org.typroject.tyboot.core.foundation.utils.Bean;
import org.typroject.tyboot.core.foundation.utils.ValidationUtil;
import org.typroject.tyboot.core.restful.doc.TycloudOperation;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * controller层切面 用于用户数据缓存 供 sql自动填充使用
 * (使用粒度过大的Aop会创建大量代理对象，影响性能，占用内存，考虑使用 WebMvcConfigurer#addInterceptors 添加拦截器 )
 *
 * @author Admin
 */
@Aspect
@Component
@Order(value = 0)
public class ControllerAop {
    /**
     * saveUserRedis设置过期时间
     */
    @Value("${redis.cache.failure.time}")
    private Long redisRegionTimeSecond;

    @Autowired
    private RedisUtils redisUtils;

    @Pointcut("(execution(public * com.yeejoin.amos.boot.module..*.biz.controller..*(..))"
            + "|| execution(public * com.yeejoin.amos.*.business.controller..*(..))"
            + " && !@annotation(org.springframework.scheduling.annotation.Scheduled))"
            + "|| execution(public * com.yeejoin.equipmanage.controller..*.*(..))"
            + "||execution(public * com.yeejoin.amos.api.openapi.controller..*(..))")
    public void userCache() {

    }

    @Before("userCache()")
    public void doBefore(JoinPoint joinPoint) {
        PermissionInterceptorContext.clean();
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = null;
        if(ObjectUtils.isNotEmpty(attributes)){
            request = attributes.getRequest();
        } else {
            return;
        }
        // 不需要添加请求头的接口
        String[] url = new String[]{"/api/user/save/curCompany", "/jcs/command/lookHtmlText",
                "/jcs/common/duty-person/findByDutyAreaId", "/tzs/wechatBack", "/tzs/elevator/getElevatorInfo",
                "/openapi/bizToken/applyToken","/fire-patrol-report/download","/fire-patrol-report/allPage"};
        // 获取请求路径
        for (String uri : url) {
            if (request.getRequestURI().indexOf(uri) != -1) {
                return;
            }
        }
        //TODO tyboot 框架拦截器已缓存数据
        String token = RequestContext.getToken();

        // 不需要校验token的接口直接返回
        if (joinPoint.getSignature() instanceof MethodSignature) {
            if (!((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(TycloudOperation.class).needAuth() && !request.getParameterMap().containsKey("token") && ValidationUtil.isEmpty(token)) {
                return;
            }
        }

        // 平台studio配置的下载接口token从url里取
        if (ValidationUtil.isEmpty(token)) {
            fillRequestContext(request);
            token = RequestContext.getToken();
        }
        if (token != null) {
            String pattern = RedisKey.buildPatternKey(token);
            //验证token有效性，防止token失效
            AgencyUserModel userModel;
            try {
                FeignClientResult<AgencyUserModel> agencyUserModel = Privilege.agencyUserClient.getme();
                userModel = agencyUserModel.getResult();
                if (userModel == null) {
                    throw new Exception("无法获取用户信息");
                }
                RequestContext.setExeUserId(userModel.getUserId());
            } catch (Exception e) {
                //删除失效token缓存
                redisUtils.getAndDeletePatternKeys(pattern);
                throw new RuntimeException(e.getMessage());
            }
            saveUserRedis(userModel, token);
        } else {
            throw new AuthException("请求未包含认证信息.");
        }
    }

    private void saveUserRedis(AgencyUserModel user, String token) {
        String authToken = RedisKey.buildReginKey(user.getUserId(), token);
        if (redisUtils.hasKey(authToken)) {
            return;
        }
        CompanyBo company = new CompanyBo();
        DepartmentBo department = new DepartmentBo();
        RoleBo role = new RoleBo();
        //平台更新 人员部门可能为NULL 导致redis缓存不到人员信息
        Map<Long, List<DepartmentModel>> mapDepartments = new HashMap<>();
        DepartmentModel departmentM = new DepartmentModel();
        CompanyModel companyM = user.getCompanys() != null ? user.getCompanys().get(0) : null;
        Bean.copyExistPropertis(companyM, company);
        if (!ValidationUtil.isEmpty(user.getCompanyDepartments())){
            mapDepartments   = user.getCompanyDepartments();
            departmentM = companyM != null ? mapDepartments.get(companyM.getSequenceNbr()).get(0) : null;
        }
        Bean.copyExistPropertis(departmentM, department);
        Map<Long, List<RoleModel>> roles = user.getOrgRoles();
        Long sequenceNbr;
        if (departmentM == null) {
            sequenceNbr = null;
        } else {
            sequenceNbr = departmentM.getSequenceNbr();
        }
        RoleModel roleM = null;
        if (sequenceNbr == null) {
            roleM = companyM != null ? roles.get(companyM.getSequenceNbr()).get(0) : null;
        } else {
            roleM = roles.get(sequenceNbr).get(0);
        }
        Bean.copyExistPropertis(roleM, role);
        ReginParams reginParams = new ReginParams();
        reginParams.setCompany(company);
        reginParams.setRole(role);
        reginParams.setDepartment(department);
        reginParams.setUserModel(user);
        redisUtils.set(authToken, JSONObject.toJSONString(reginParams), redisRegionTimeSecond);
    }

    @AfterReturning(returning = "ret", pointcut = "userCache()")
    public void doAfterReturning(Object ret) throws Throwable {
        //统一redis管理
//        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
//        HttpServletRequest request = attributes.getRequest();
//        String token = request.getHeader("token");
//        if (token != null) {
//            GlobalCache.paramMap.remove(token);
//        }
    }

    private void fillRequestContext(HttpServletRequest request) {
        String token = request.getParameterMap().get("token")[0];
        String product = request.getParameterMap().get("product")[0];
        String appKey = request.getParameterMap().get("appKey")[0];
        RequestContext.setToken(token);
        RequestContext.setProduct(product);
        RequestContext.setAppKey(appKey);
    }
}
