package com.winsea.svc.common.international;

import com.alipay.sofa.runtime.api.annotation.SofaReference;
import com.baomidou.mybatisplus.annotations.TableField;
import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.enums.FieldFill;
import com.baomidou.mybatisplus.mapper.BaseMapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.toolkit.StringUtils;
import com.google.common.cache.Cache;
import com.google.common.collect.Sets;
import com.yh.saas.common.support.util.ContextWrapper;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.RowBounds;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 国际化资源提供者（包装类）
 * 
 * @author SunTianjun
 */
public class InternationalResourceProviderAdapter {

	private static final String META_OBJ_PREFIX = "et";

	private Map<String, BaseMapper<?>> entityClassBaseMapperMap = new ConcurrentHashMap<>();

	@Autowired
	@Qualifier("localMinuteCache")
	private Cache<String, Object> cache;

	@Autowired
	private InternationalProperties iProperties;

	@SofaReference
	private InternationalResourceProvider iResourceProvider;

	/**
	 * 获取文本
	 * 
	 * @param resourceId 资源ID
	 * @param lang       语言
	 * @return 国际化文本
	 */
	public String getText(String resourceId, String lang) {
		if (!iProperties.isSerializationEnabled() || StringUtils.isEmpty(lang)) {
			return resourceId;
		}

		Object text = cache.getIfPresent(resourceId);
		if (null == text) {
			text = iResourceProvider.getText(resourceId, lang);
			if (null != text && text.toString().length() != 0) {
				cache.put(resourceId, text);
			}
		}
		return null != text ? text.toString() : null;
	}

	/**
	 * 设置国际化文本
	 * 
	 * @param metaObject MetaObject
	 * @param updated    是否是更新操作
	 */
	public void setInternationalText(MetaObject metaObject, boolean updated) {
		if (!iProperties.isPersistenceEnabled() || StringUtils.isEmpty(iProperties.getLang())) {
			return;
		}

		findFillFields(metaObject).forEach(field -> {

			// 如果是修改则删除上一次的资源
			Object resourceId = null;
			if (updated) {
				resourceId = removeLastResource(metaObject, field);
			}

			if (null == resourceId) {
				resourceId = internationalize(field, getFieldValByName(field.getName(), metaObject));
			}

			setFieldValByName(field.getName(), resourceId, metaObject);
		});
	}

	/**
	 * 删除上一次资源
	 * 
	 * @param metaObject         MetaObject
	 * @param internationalField 国际化字段
	 * @return 如果未删除则返回保留的资源ID
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private String removeLastResource(MetaObject metaObject, Field internationalField) {
		Object itext = getFieldValByName(internationalField.getName(), metaObject);
		// 待国际化字段内容为空直接返回
		if (null == itext) {
			return null;
		}

		Class<?> tableClass = getOriginalObjectClass(metaObject);
		BaseMapper baseMapper = getEntityClassBaseMapperMap().get(tableClass.getName());

		Object originalObj = null;
		Map<?, ?> map = (Map<?, ?>) metaObject.getOriginalObject();

		// 如果是条件更新，则只获取匹配的第一条数据进行删除
		if (map.containsKey("ew")) {
			List objs = baseMapper.selectPage(new RowBounds(0, 1), (Wrapper) map.get("ew"));

			// 查询不到则直接返回
			if (CollectionUtils.isEmpty(objs)) {
				return null;
			}

			originalObj = objs.get(0);
		}
		// 根据ID进行更新
		else {
			Set<Field> fields = getAllFields(tableClass).stream()
					.filter(field -> null != field.getAnnotation(TableId.class)).collect(Collectors.toSet());
			if (fields.isEmpty()) {
				return null;
			}

			// 根据ID查询原始对象
			String idValue = getFieldValByName(fields.iterator().next().getName(), metaObject).toString();
			originalObj = baseMapper.selectById(idValue);
		}

		String resourceId;
		try {
			resourceId = BeanUtils.getProperty(originalObj, internationalField.getName());
		} catch (Exception e) {
			return null;
		}

		if (null == resourceId) {
			return null;
		}

		// 如果当前文本与前一次文本不同则删除
		if (!itext.toString().equals(getText(resourceId, iProperties.getLang()))) {
			iResourceProvider.markDeletion(resourceId);
			return null;
		}
		// 否则返回保留的资源ID
		else {
			return resourceId;
		}
	}

	/**
	 * 查找填充字段
	 */
	private Set<Field> findFillFields(MetaObject metaObject) {
		return getAllFields(getOriginalObjectClass(metaObject)).stream()
				.filter(field -> null != field.getAnnotation(International.class)
						&& null != field.getAnnotation(TableField.class)
						&& field.getAnnotation(TableField.class).fill() != FieldFill.DEFAULT)
				.collect(Collectors.toSet());
	}

	/**
	 * 国际化
	 * 
	 * @param fieldName  字段名
	 * @param fieldValue 字段值
	 * @return 国际化资源ID
	 */
	private Object internationalize(Field field, Object fieldValue) {
		if (null == fieldValue || fieldValue.toString().equals(StringUtils.EMPTY)) {
			return fieldValue;
		}

		// 国际化资源类型
		String type = field.getAnnotation(International.class).type();
		return iResourceProvider.create(fieldValue.toString(), type, iProperties.getLang());
	}

	/**
	 * 设置填充对象值
	 * 
	 * @param fieldName  字段名
	 * @param fieldVal   字段值
	 * @param metaObject 待填充对象
	 */
	private void setFieldValByName(String fieldName, Object fieldVal, MetaObject metaObject) {
		if (metaObject.hasSetter(fieldName) && metaObject.hasGetter(fieldName)) {
			metaObject.setValue(fieldName, fieldVal);
		} else if (metaObject.hasGetter(META_OBJ_PREFIX)) {
			Object et = metaObject.getValue(META_OBJ_PREFIX);
			if (et != null) {
				MetaObject etMeta = SystemMetaObject.forObject(et);
				if (etMeta.hasSetter(fieldName)) {
					etMeta.setValue(fieldName, fieldVal);
				}
			}
		}
	}

	/**
	 * 从待填充对象获取值
	 * 
	 * @param fieldName  字段名
	 * @param metaObject 待填充对象
	 */
	private Object getFieldValByName(String fieldName, MetaObject metaObject) {
		if (metaObject.hasGetter(fieldName)) {
			return metaObject.getValue(fieldName);
		} else if (metaObject.hasGetter(META_OBJ_PREFIX + "." + fieldName)) {
			return metaObject.getValue(META_OBJ_PREFIX + "." + fieldName);
		}
		return StringUtils.EMPTY;
	}

	/**
	 * 获取所有父类
	 */
	private Set<Class<?>> getAllSuperTypes(final Class<?> type) {
		Set<Class<?>> result = Sets.newLinkedHashSet();
		if (type != null && !type.equals(Object.class)) {
			result.add(type);
			result.addAll(getAllSuperTypes(type.getSuperclass()));
			for (Class<?> ifc : type.getInterfaces())
			{
				result.addAll(getAllSuperTypes(ifc));
			}
		}
		return result;
	}

	/**
	 * 获取所有声明字段，包含父类声明的
	 */
	private Set<Field> getAllFields(final Class<?> type) {
		Set<Field> result = Sets.newHashSet();
		for (Class<?> t : getAllSuperTypes(type))
		{
			result.addAll(Sets.newHashSet(t.getDeclaredFields()));
		}
		return result;
	}

	/**
	 * 获取原始对象类
	 */
	private Class<?> getOriginalObjectClass(MetaObject metaObject) {
		if (metaObject.getOriginalObject() instanceof Map) {
			return ((Map<?, ?>) metaObject.getOriginalObject()).get(META_OBJ_PREFIX).getClass();
		} else {
			return metaObject.getOriginalObject().getClass();
		}
	}

	/**
	 * 获取实体类和对应Mapper映射
	 */
	private Map<String, BaseMapper<?>> getEntityClassBaseMapperMap() {
		if (entityClassBaseMapperMap.isEmpty()) {
			ContextWrapper.getApplicationContext().getBeansOfType(BaseMapper.class).values().forEach(baseMapper -> {
				try {
					Class<?> entityClass = (Class<?>) ((ParameterizedType) (((Class<?>) baseMapper.getClass()
							.getGenericInterfaces()[0]).getGenericInterfaces()[0])).getActualTypeArguments()[0];
					entityClassBaseMapperMap.put(entityClass.getName(), baseMapper);
				} catch (ClassCastException ex) {
					// ignore
				}
			});
		}
		return entityClassBaseMapperMap;
	}
}