package com.yeejoin.precontrol.common.exception.handle;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.yeejoin.precontrol.common.annotations.ImagePrefix;
import com.yeejoin.precontrol.common.annotations.TableFormat;
import com.yeejoin.precontrol.common.entity.publics.CommonResponse;
import com.yeejoin.precontrol.common.utils.ExceptionResult;
import com.yeejoin.precontrol.common.utils.RedisUtil;
import com.yeejoin.precontrol.common.utils.StringUtil;
import com.yeejoin.precontrol.common.vo.TaskPageVo;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import org.typroject.tyboot.core.restful.utils.ResponseModel;

import java.lang.reflect.Field;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

/**
 * @description: 全局统一响应处理
 * @author: duanwei
 * @create: 2019-08-28 20:07
 **/
@RestControllerAdvice
@Slf4j
public class GlobalResponseHandler implements ResponseBodyAdvice<Object> {
	@Autowired
	RedisUtil redisUtil;

	@Value("${fileserver.domain}")
	private String fileserver;

	final static String SWAGGER_URL = "/v2/api-docs";
	final static String SWAGGER_RESOURCES = "swagger";
	final static String CSRF = "csrf";
	final static String ACCESS = "/access/";
	final static String ERROR = "error";
	final static String CHECK_TOKEN = "/safe/checkToken";
	final static String ECHO = "/precontrol/echo";
	static LinkedList<String> filterUrl = new LinkedList<>();

	{
		filterUrl.add(SWAGGER_URL);
		filterUrl.add(SWAGGER_RESOURCES);
		filterUrl.add(CSRF);
		filterUrl.add(ACCESS);
		filterUrl.add(CHECK_TOKEN);
		filterUrl.add(ECHO);
	}

	/**
	 * 针对方法以及对象
	 *
	 * @param methodParameter
	 * @param aClass
	 * @return
	 */
	@Override
	public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
		// 获取当前处理请求的controller的方法
		String methodName = methodParameter.getMethod().getName();
		// 不拦截/不需要处理返回值 的方法
		String method = "loginCheck";
		// 拦截 false
		// return !method.equals(methodName);
		return true;
	}

	/**
	 * @param body
	 * @param returnType
	 * @param selectedContentType
	 * @param selectedConverterType
	 * @param request
	 * @param response
	 * @return
	 */
	@SneakyThrows
	@Override
	public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
			Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
			ServerHttpResponse response) {
		long startTime = System.currentTimeMillis();
		// 过滤放行
		for (String s : filterUrl) {
			if (request.getURI().toString().contains(s)) {
				return body;
			}
		}
		// 数据层format处理
		if (body != null && body instanceof Object) {
			if (body instanceof IPage || body instanceof Page) {
				List<Object> records = ((IPage) body).getRecords();
				if (StringUtil.isNotEmpty(records)) {
					setFormatName(records);
				}
			} else if (body instanceof Collection) {
				List<Object> records = (List<Object>) body;
				if (!records.isEmpty()) {
					setFormatName(records);
				}
			} else {
				setFormateByOne(body);
				setImagePathValueByCollection(body);
			}
		}
		ResponseModel res = new ResponseModel();
		res.setResult(body);
		res.setDevMessage("SUCCESS");
		res.setStatus(HttpStatus.OK.value());

		if (body instanceof CommonResponse || body instanceof ExceptionResult || body instanceof ResponseModel) {
			return body;
		}
		// 保证null情况下 对象不会转换错误
		if (null == body) {
			return JSON.toJSONString(body);
		}
		// 处理返回值是String的情况 对象不会转换错误
		if (body instanceof String) {
			return JSON.toJSONString(res);
		}

		long endTime = System.currentTimeMillis();
		long l = endTime - startTime;
		log.info("全局响应方法请求时间:" + l + "ms");
		return res;

	}

	private void setFormateByOne(Object body) throws IllegalAccessException {
		Field[] declaredFields = body.getClass().getDeclaredFields();
		for (Field declaredField : declaredFields) {
			TableFormat tableFormat = declaredField.getAnnotation(TableFormat.class);
			if (tableFormat != null) {
				declaredField.setAccessible(true);
				Long idValue = (Long) declaredField.get(body);
				if (idValue != null) {
					// 表对象名称
					String entity = tableFormat.entityName();
					// 表对象的属性名
					String fieldName = tableFormat.fieldName();
					// 要修改属性名
					String operFieldName = tableFormat.operFieldName();
					if (redisUtil.hasKey(entity + "_" + fieldName + "_" + idValue)) {
						String redisValue = redisUtil.get(entity + "_" + fieldName + "_" + idValue).toString();
						for (Field operField : declaredFields) {
							String name = operField.getName();
							if (name.equals(operFieldName)) {
								operField.setAccessible(true);
								operField.set(body, redisValue);
							} else {
								continue;
							}
						}
					}
				}
			}
		}
	}

	/**
	 * 对象设置formatName
	 *
	 * @param records
	 */
	private void setFormatName(List<Object> records) {
		if (records == null) {
			return;
		}
		records.forEach(object -> {
			if(object.getClass().equals(TaskPageVo.class))
			{
				return;
			}
			Field[] declaredFields = object.getClass().getDeclaredFields();
			for (Field declaredField : declaredFields) {
				TableFormat tableFormat = declaredField.getAnnotation(TableFormat.class);
				if (tableFormat != null) {
					try {
						declaredField.setAccessible(true);
						Long idValue = (Long) declaredField.get(object);
						if (idValue != null) {
							// 表对象名称
							String entity = tableFormat.entityName();
							// 表对象的属性名
							String fieldName = tableFormat.fieldName();
							// 要修改属性名
							String operFieldName = tableFormat.operFieldName();
							if (redisUtil.hasKey(entity + "_" + fieldName + "_" + idValue)) {
								String redisValue = redisUtil.get(entity + "_" + fieldName + "_" + idValue).toString();
								for (Field operField : declaredFields) {
									String name = operField.getName();
									if (name.equals(operFieldName)) {
										operField.setAccessible(true);
										operField.set(object, redisValue);
									} else {
										continue;
									}
								}
							}
						}
					} catch (Exception e) {
						log.error("redis format class name error");
					}
				}
			}
			// 设置图片路径
			try {
				setImagePathValueByCollection(object);
			} catch (Exception e) {
				log.error("format iamgePath  error");
			}
		});

	}

	/**
	 * 根据传入对象进行反射 递归添加路径
	 *
	 * @param aClass
	 * @throws IllegalAccessException
	 */
	private void setImagePathValueByCollection(Object aClass) throws IllegalAccessException, NoSuchFieldException {
		if (aClass != null) {
			Class<?> r = aClass.getClass();
			if(r.equals(TaskPageVo.class))
			{
				return;
			}
			Field[] declaredFields = r.getDeclaredFields();
			if (declaredFields != null && declaredFields.length > 0) {
				for (Field declaredField : declaredFields) {
					declaredField.setAccessible(true);
					ImagePrefix imagePrefix = declaredField.getAnnotation(ImagePrefix.class);
					if (imagePrefix != null) {
						String typeName = declaredField.getType().getTypeName();
						Object path = declaredField.get(aClass);
						if (path != null) {
							String pathValue = declaredField.get(aClass).toString();
							if ("java.lang.String".equals(typeName)) {
								String filePathValue = fileserver + pathValue;
								declaredField.setAccessible(true);
								declaredField.set(aClass, filePathValue);
							} else if ("java.util.List".equals(typeName)) {
								// 集合对象 进行循环 取出当前数据
								List<Class> aClass1 = (List<Class>) declaredField.get(aClass);
								if (StringUtil.isNotEmpty(aClass1)) {
									Object aClassTemp = aClass1.get(0);
									String typeName1 = aClassTemp.getClass().getTypeName();
									if ("java.lang.String".equals(typeName1)) {
										// 代表数组是纯字符串
										List<String> copyList = new LinkedList<>();
										for (int i = 0; i < aClass1.size(); i++) {
											String aClass2 = String.valueOf(aClass1.get(i));
											String filePathValue = fileserver + aClass2;
											copyList.add(filePathValue);
										}
										declaredField.setAccessible(true);
										declaredField.set(aClass, copyList);
									} else {
										for (Object object : aClass1) {
											setImagePathValueByCollection(object);
										}
									}
								}
							} else {
								// 这个注解只能加到String或者List或者对象上 如果是对象上 要考虑对象内部是否有数组
								Object aClass1 = declaredField.get(aClass);
								setImagePathValueByCollection(aClass1);
							}
						}
					}
				}
			}
		}
	}

}
