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

import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
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.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.component.feign.utils.FeignUtil;
import com.yeejoin.amos.feign.privilege.Privilege;
import com.yeejoin.amos.feign.privilege.model.*;
import com.yeejoin.amos.feign.systemctl.Systemctl;
import com.yeejoin.amos.feign.systemctl.model.RegionModel;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.util.StopWatch;
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.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * controller层切面 用于用户数据缓存 供 sql自动填充使用 (使用粒度过大的Aop会创建大量代理对象，影响性能，占用内存，考虑使用
 * WebMvcConfigurer#addInterceptors 添加拦截器 )
 *
 * @author Admin
 */
@Aspect
@Component
@Order(value = 0)
public class ControllerAop {

	private static final Logger logger = LoggerFactory.getLogger(ControllerAop.class);

	/**
	 * saveUserRedis设置过期时间
	 */
	@Value("${redis.cache.failure.time:10800}")
	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;
		}
		// 不需要添加请求头的接口，如使用url中有PathVariable，则使用正则表达式匹配
		List<String> urls = Lists.newArrayList();
		urls.add("/api/user/save/curCompany");
		urls.add("/jcs/command/lookHtmlText");
		urls.add("^/jcs/common/duty-person/findByDutyAreaId/[A-Za-z0-9]+$");
		urls.add("/tcm/wechatBack");
		urls.add("/elevator/elevator/getElevatorInfo");
		urls.add("/openapi/bizToken/applyToken");
		urls.add("/tcm/flc-unit-info/region/tree");
		urls.add("/tcm/reg-unit-info/management-unit/tree");
		urls.add("/tcm/reg-unit-info/unit-type/list");
		urls.add("/tcm/flc-unit-info/hasExistPhone/[0-9]+$");
		urls.add("/tcm/flc-unit-info/sendTelCode/[0-9]+$");
		urls.add("/tcm/reg-unit-info/[A-Za-z0-9]+/check$");
		urls.add("/tcm/flc-unit-info/verifyTelCode/[A-Za-z0-9]+/[A-Za-z0-9]+");
		urls.add("/tcm/flc-unit-info/hasExistUser/.+");
		urls.add("/tcm/reg-unit-info/save");
		urls.add("/tcm/reg-unit-info/child-list");
		urls.add("/tcm/reg-unit-info/management-unit/tree");
		urls.add("/tcm/flc-unit-info/national/region/tree");
		urls.add("/ymt/equipment-category/getFormRecordById");
		urls.add("/ymt/tzs-auth-api/getCodeAndName");
		urls.add("/tcm/reg-unit-info/dataDictionaryListByType");
		urls.add("/tcm/reg-unit-info/dataDictionaryIdFillMenu");
		urls.add("/elevator/alert-called/getWorkOderNumber");
		urls.add("/elevator/alert-called/save");
		urls.add("/tcm/base-institution/register");
		urls.add("/tcm/base-individuality/person/register");
		urls.add("/tcm/userInfo/arrangement-statistic");
		urls.add("/tcm/userInfo/getPersonType");
		urls.add("/patrol/api/check/saveRecordAll");
		urls.add("/patrol/api/check/saveRecordAll2");
		urls.add("/patrol/api/planTask/queryPlanTaskNew");
		// 获取请求路径
		for (String uri : urls) {
			Pattern p = Pattern.compile(uri);
			if (p.matcher(request.getRequestURI()).matches()) {
				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();
			logger.info("studio url token ===========>" + token);
		}
		if (token != null) {
			String pattern = RedisKey.buildPatternKey(token);
			// 验证token有效性，防止token失效
			AgencyUserModel userModel;
			try {
				String authToken = RedisKey.buildReginKey(RequestContext.getExeUserId(), token);
				if (redisUtils.hasKey(authToken)) {
					logger.info("登录命中缓存 ，直接返回！！===========>");
					return;
				}
				FeignClientResult<AgencyUserModel> agencyUserModel = Privilege.agencyUserClient.getme();
				userModel = agencyUserModel.getResult();
				if (userModel == null) {
					throw new Exception("无法获取用户信息");
				}
				RequestContext.setExeUserId(userModel.getUserId());
			} catch (Exception e) {
				// 删除失效token缓存
				logger.info("catch pattern before==========>" + pattern);
				redisUtils.getAndDeletePatternKeys(pattern);
				logger.info("catch pattern after==========>" + pattern);
				throw new RuntimeException(e.getMessage());
			}
			saveUserRedis(userModel, token);
//			saveUserRedis();
		} 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;
		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;
		}
		if (!ValidationUtil.isEmpty(company.getCompanyType()) && redisUtils.hasKey(RedisKey.buildReginRoleKey(RequestContext.getExeUserId()))){
			company.setCompanyType(String.valueOf(redisUtils.get(RedisKey.buildReginRoleKey(RequestContext.getExeUserId()))));
		}
		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);
		}
		if (companyM != null && companyM.getRegionSeq() != null) {
			FeignClientResult<RegionModel> result = Systemctl.regionClient
					.getRegion(Long.valueOf(companyM.getRegionSeq()));
			company.setRegionCode(String.valueOf(result.getResult().getRegionCode()));
		}
		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);
	}

	private void saveUserRedis() {
		String authToken = RedisKey.buildReginKey(RequestContext.getExeUserId(), RequestContext.getToken());
		if (redisUtils.hasKey(authToken)) {
			return;
		}

		CompanyBo company = new CompanyBo();
		DepartmentBo department = new DepartmentBo();
		RoleBo role = new RoleBo();

		StopWatch stopWatch0 = new StopWatch();
		stopWatch0.start();
		StopWatch stopWatch1 = new StopWatch();
		stopWatch1.start("用户企业部门人员信息查询任务====>");
		// 查询当前人所在第一个公司信息
		List<CompanyModel> companyModels = FeignUtil.remoteCall(() -> Privilege.companyClient.queryListByChild(RequestContext.getExeUserId()));
		stopWatch1.stop();
//		logger.info("用户企业部门人员信息查询任务====>{}s", stopWatch1.getTotalTimeSeconds());
		if (!ValidationUtil.isEmpty(companyModels)) {
			CompanyModel companyM = companyModels.get(0);
			Bean.copyExistPropertis(companyM, company);
			if (companyM != null) {
//				StopWatch stopWatch2 = new StopWatch();
//				stopWatch2.start("获取所在公司区域信息====>");
//				FeignClientResult<RegionModel> result = Systemctl.regionClient
//						.getRegion(Long.valueOf(companyM.getRegionSeq()));
//				stopWatch2.stop();
//				logger.info("获取所在公司区域信息====>{}s", stopWatch2.getTotalTimeSeconds());
//				company.setRegionCode(String.valueOf(result.getResult().getRegionCode()));

//				DepartmentModel departmentM;
//				// 查询当前人第一个公司下部门信息
//				StopWatch stopWatch3 = new StopWatch();
//				stopWatch3.start("获取所在部门信息====>");
//				Collection<DepartmentModel> departmentList =
//						FeignUtil.remoteCall(() -> Privilege.departmentClient.deptTreeWithoutAuth(companyM.getSequenceNbr()));
//				stopWatch3.stop();
//				logger.info("获取所在部门信息====>{}s", stopWatch3.getTotalTimeSeconds());
//				if (!ValidationUtil.isEmpty(departmentList)) {
//					departmentM = departmentList.stream().findFirst().get();
//					Bean.copyExistPropertis(departmentM, department);
//				}
//
//				StopWatch stopWatch4 = new StopWatch();
//				stopWatch4.start("获取用户角色信息====>");
//				Map<String, Object> userOrgRoleMap = FeignUtil.remoteCall(() -> Privilege.userOrgRoleClient.getme());
//				List<String> userOrgRoleList = (List<String>) userOrgRoleMap.get("roleId");
//				RoleModel roleM =
//						FeignUtil.remoteCall(() -> Privilege.roleClient.seleteOne(Long.valueOf((userOrgRoleList.get(0)))));
//				stopWatch4.stop();
//				logger.info("获取用户角色信息====>{}s", stopWatch4.getTotalTimeSeconds());
//				Bean.copyExistPropertis(roleM, role);

				StopWatch stopWatch5 = new StopWatch();
				stopWatch5.start("获取用户信息====>");
				List<AgencyUserModel> userList =
						FeignUtil.remoteCall(() -> Privilege.agencyUserClient.queryByIds(RequestContext.getExeUserId(), false));
				stopWatch5.stop();
				logger.info("获取用户信息====>{}s", stopWatch5.getTotalTimeSeconds());

				List<String> appCodes = new ArrayList<>();
//				try {
//					List<ApplicationModel> applicationModelList = Privilege.agencyUserClient.listApps(userList.get(0).getUserId()).getResult();
//					appCodes = applicationModelList.stream().map(ApplicationModel::getAppCode).collect(Collectors.toList());
//				} catch (Exception e) {
//					throw new RuntimeException("获取listApps失败");
//				}
				userList.get(0).setAppCodes(appCodes);
				ReginParams regionParams = new ReginParams();
				regionParams.setCompany(company);
				regionParams.setRole(role);
				regionParams.setDepartment(department);
				regionParams.setUserModel(userList.get(0));
				ReginParams.PersonIdentity personIdentity = new ReginParams.PersonIdentity();
				personIdentity.setBizOrgCode(companyM.getOrgCode());
				personIdentity.setCompanyBizOrgCode(companyM.getOrgCode());

				regionParams.setPersonIdentity(personIdentity);
				redisUtils.set(authToken, JSONObject.toJSONString(regionParams), redisRegionTimeSecond);
				stopWatch0.stop();
				logger.info("用户企业部门人员信息查询任务完成=======>{}", stopWatch0.getTotalTimeSeconds());
			}

		}
	}

	@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 = null;
		String product = null;
		String appKey = null;
		logger.info("fillRequestContext request =====>" + request);
		if (request != null && request.getParameterMap() != null) {
			logger.info("fillRequestContext request getParameterMap =====>" + request.getParameterMap());
			if (request.getParameterMap().get("token") != null && request.getParameterMap().get("product") != null
					&& request.getParameterMap().get("appKey") != null) {
				token = request.getParameterMap().get("token")[0];
				product = request.getParameterMap().get("product")[0];
				appKey = request.getParameterMap().get("appKey")[0];
				RequestContext.setToken(token);
				RequestContext.setProduct(product);
				RequestContext.setAppKey(appKey);
			}
		}
	}
}
