package com.uas.search.console.b2b.util;

import com.uas.search.b2b.exception.SearchException;
import com.uas.search.b2b.service.SearchService.Table_name;
import com.uas.search.console.b2b.dao.*;
import com.uas.search.console.b2b.model.*;
import com.uas.search.console.b2b.platformmanager.dao.UserSpaceDetailSimpleInfoDao;
import com.uas.search.console.b2b.platformmanager.model.UserSpaceDetailSimpleInfo;
import com.uas.search.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.jpa.repository.JpaRepository;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;


/**
 * 实体类与单据类型的工具类
 * 
 * @author sunyj
 * @since 2016年11月10日 下午2:18:10
 */
public class ClassAndTableNameUtils {

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

	/**
	 * 需要建立索引的所有实体类
	 */
	private static List<Class<?>> entityClasses = new ArrayList<>();

	/**
	 * 需要建立索引的所有实体类的Dao类
	 */
	private static List<Class<?>> entityDaoClasses = new ArrayList<>();

	static {
		entityClasses.add(PurchaseOrderSimpleInfo.class);
		entityClasses.add(MakeOrderSimpleInfo.class);
		entityClasses.add(PurchaseAcceptSimpleInfo.class);
		entityClasses.add(PurchaseApbillSimpleInfo.class);
		entityClasses.add(PurchaseBadinSimpleInfo.class);
		entityClasses.add(PurchaseBadoutSimpleInfo.class);
		entityClasses.add(PurchaseForecastSimpleInfo.class);
		// entityClasses.add(PurchaseInquirySimpleInfo.class);
		entityClasses.add(PurchaseMrbSimpleInfo.class);
		entityClasses.add(PurchaseChangesSimpleInfo.class);
		entityClasses.add(PurchaseProofingItemSimpleInfo.class);
		entityClasses.add(PurchaseReturnSimpleInfo.class);
		entityClasses.add(MakeAcceptSimpleInfo.class);
		entityClasses.add(MakeOrderChangeItemSimpleInfo.class);
		entityClasses.add(MakeReturnSimpleInfo.class);
		entityClasses.add(SaleSendSimpleInfo.class);
		entityClasses.add(PurchaseNoticeSimpleInfo.class);
		entityClasses.add(PurcProofingapprovalSimpleInfo.class);
		entityClasses.add(PurcProofingsendSimpleInfo.class);
		entityClasses.add(PurchaseApCheckSimpleInfo.class);
		entityClasses.add(SaleArcheckSimpleInfo.class);
		entityClasses.add(SaleQuotationSimpleInfo.class);
		entityClasses.add(PurchaseInquiryItemSimpleInfo.class);
		entityClasses.add(PurchaseInquiryMouldSimpleInfo.class);
		entityClasses.add(ProdSimpleInfo.class);
		entityClasses.add(EnterpriseSearchSimpleInfo.class);
		entityClasses.add(ProductComponentSimpleInfo.class);
		entityClasses.add(PurchaseTenderSimpleInfo.class);
		entityClasses.add(DeputyOrderSimpleInfo.class);
		entityClasses.add(PagingReleaseDetailSimpleInfo.class);
		entityClasses.add(VendorSimpleInfo.class);
		entityClasses.add(CustmerSimpleInfo.class);
		entityClasses.add(ProductBrandSimpleInfo.class);
		entityClasses.add(UserSpaceDetailSimpleInfo.class);
		entityClasses.add(PurcInquirySimpleInfo.class);
		entityClasses.add(PurcInquiryItemSimpleInfo2.class);
		entityClasses.add(InvitationRecordSimpleInfo.class);
		entityClasses.add(ErpSaleTenderSimpleInfo.class);
		entityClasses.add(CartSimpleInfo.class);
		entityClasses.add(ProdNonStandardSimpleInfo.class);
		entityClasses.add(ProdStandardSimpleInfo.class);
		entityClasses.add(KindSimpleInfo.class);
		entityClasses.add(B2BSaleTenderSimpleInfo.class);
		entityClasses.add(PurchaseApBillOutSimpleInfo.class);
		entityClasses.add(SaleTenderQuestionSimpleInfo.class);
		entityClasses.add(VendorPerformanceAssessSimpleInfo.class);
        entityClasses.add(ProductUsersSimpleInfo.class);
		// TODO 其他表

		// entityClasses和entityDaoClasses对于表的添加顺序必须一致
		entityDaoClasses.add(PurchaseOrderSimpleInfoDao.class);
		entityDaoClasses.add(MakeOrderSimpleInfoDao.class);
		entityDaoClasses.add(PurchaseAcceptSimpleInfoDao.class);
		entityDaoClasses.add(PurchaseApbillSimpleInfoDao.class);
		entityDaoClasses.add(PurchaseBadinSimpleInfoDao.class);
		entityDaoClasses.add(PurchaseBadoutSimpleInfoDao.class);
		entityDaoClasses.add(PurchaseForecastSimpleInfoDao.class);
		// entityDaoClasses.add(PurchaseInquirySimpleInfoDao.class);
		entityDaoClasses.add(PurchaseMrbSimpleInfoDao.class);
		entityDaoClasses.add(PurchaseChangesSimpleInfoDao.class);
		entityDaoClasses.add(PurchaseProofingItemSimpleInfoDao.class);
		entityDaoClasses.add(PurchaseReturnSimpleInfoDao.class);
		entityDaoClasses.add(MakeAcceptSimpleInfoDao.class);
		entityDaoClasses.add(MakeOrderChangeItemSimpleInfoDao.class);
		entityDaoClasses.add(MakeReturnSimpleInfoDao.class);
		entityDaoClasses.add(SaleSendSimpleInfoDao.class);
		entityDaoClasses.add(PurchaseNoticeSimpleInfoDao.class);
		entityDaoClasses.add(PurcProofingapprovalSimpleInfoDao.class);
		entityDaoClasses.add(PurcProofingsendSimpleInfoDao.class);
		entityDaoClasses.add(PurchaseApcheckSimpleInfoDao.class);
		entityDaoClasses.add(SaleArcheckSimpleInfoDao.class);
		entityDaoClasses.add(SaleQuotationSimpleInfoDao.class);
		entityDaoClasses.add(PurchaseInquiryItemSimpleInfoDao.class);
		entityDaoClasses.add(PurchaseInquiryMouldSimpleInfoDao.class);
		entityDaoClasses.add(ProdSimpleInfoDao.class);
		entityDaoClasses.add(EnterpriseSearchSimpleInfoDao.class);
		entityDaoClasses.add(ProductComponentSimpleInfoDao.class);
		entityDaoClasses.add(PurchaseTenderSimpleInfoDao.class);
		entityDaoClasses.add(DeputyOrderSimpleInfoDao.class);
		entityDaoClasses.add(PagingReleaseDetailSimpleInfoDao.class);
		entityDaoClasses.add(VendorSimpleInfoDao.class);
		entityDaoClasses.add(CustmerSimpleInfoDao.class);
		entityDaoClasses.add(ProductBrandSimpleInfoDao.class);
		entityDaoClasses.add(UserSpaceDetailSimpleInfoDao.class);
		entityDaoClasses.add(PurcInquirySimpleInfoDao.class);
		entityDaoClasses.add(PurcInquiryItemSimpleInfo2Dao.class);
		entityDaoClasses.add(InvitationRecordSimpleInfoDao.class);
		entityDaoClasses.add(ErpSaleTenderSimpleInfoDao.class);
		entityDaoClasses.add(CartSimpleInfoDao.class);
		entityDaoClasses.add(ProdNonStandardSimpleInfoDao.class);
		entityDaoClasses.add(ProdStandardSimpleInfoDao.class);
		entityDaoClasses.add(KindSimpleInfoDao.class);
		entityDaoClasses.add(B2BSaleTenderSimpleInfoDao.class);
		entityDaoClasses.add(PurchaseApBillOutSimpleInfoDao.class);
		entityDaoClasses.add(SaleTenderQuestionSimpleInfoDao.class);
		entityDaoClasses.add(VendorPerformanceAssessSimpleInfoDao.class);
        entityDaoClasses.add(ProductUsersSimpleInfoDao.class);
		// TODO 其他表的Dao
	}

	/**
	 * 获取所有（需要建立索引）的实体类
	 * 
	 * @return
	 */
	public static List<Class<?>> getEntityClasses() {
		return entityClasses;
	}

	/**
	 * 获取所有（需要建立索引）的实体类的Dao类
	 * 
	 * @return
	 */
	public static List<Class<?>> getEntityDaoClasses() {
		return entityDaoClasses;
	}

	/**
	 * 获取所有（需要建立索引）的单据类型
	 * 
	 * @return
	 */
	public static List<Table_name> getTableNames() {
		List<Table_name> tableNames = new ArrayList<>();
		for (Class<?> clazz : entityClasses) {
			tableNames.add(toTableName(clazz));
		}
		return tableNames;
	}

	/**
	 * 获取实体类clazz对应的Dao
	 * 
	 * @param clazz
	 *            实体类型
	 * @return 实体类的Dao
	 * @throws ClassNotFoundException
	 */
	@SuppressWarnings("unchecked")
	public static <T> JpaRepository<T, Long> getDao(Class<T> clazz) {
		if (checkEntityClass(clazz)) {
			return (JpaRepository<T, Long>) ContextUtils
					.getBean(getEntityDaoClasses().get(getEntityClasses().indexOf(clazz)));
		}
		throw new SearchException("并非合法的实体类：" + clazz.getName());
	}

	/**
	 * 检查是否是合法的实体类（需要建立索引的实体类才是合法的）
	 * 
	 * @param clazz
	 *            实体类型
	 * @return true，如果实体类合法
	 */
	private static boolean checkEntityClass(Class<?> clazz) {
		if (clazz != null) {
			List<Class<?>> clazzes = getEntityClasses();
			if (clazzes.contains(clazz)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 根据单据类型获取每个单据可以搜索的字段列名（索引的field名）
	 * 
	 * @param tableName
	 *            单据类型
	 * @return 可以搜索的字段列名
	 */
	public static List<String> getKeywordFields(Table_name tableName) {
		return getKeywordFields(toClass(tableName));
	}

	/**
	 * 根据实体类名获取每个单据可以搜索的字段列名（索引的field名）
	 * 
	 * @param clazz
	 *            实体类型
	 * @return 可以搜索的字段列名
	 */
	@SuppressWarnings("unchecked")
	public static List<String> getKeywordFields(Class<?> clazz) {
		if (checkEntityClass(clazz)) {
			try {
				Method method = clazz.getDeclaredMethod("getKeywordFields");
				List<String> keywordFields = (List<String>) method.invoke(clazz);
				List<String> result = new ArrayList<>();
				// 返回之前先拼接索引域名
				for (String keywordField : keywordFields) {
					result.add(combineField(toTableName(clazz), keywordField));
				}
				return result;
			} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException
					| InvocationTargetException e) {
				throw new SearchException(e).setDetailedMessage(e);
			}
		}
		throw new SearchException("并非合法的实体类：" + clazz.getName());
	}

	/**
	 * 获取每个单据的id字段的列名（索引的field名）
	 * 
	 * @param tableName
	 *            单据类型
	 * @return id字段的列名
	 */
	public static String getIdField(Table_name tableName) {
		return getIdField(toClass(tableName));
	}

	/**
	 * 根据实体类型获取每个单据的id字段的列名（索引的field名）
	 * 
	 * @param clazz
	 *            实体类型
	 * @return id字段的列名
	 */
	public static String getIdField(Class<?> clazz) {
		if (checkEntityClass(clazz)) {
			try {
				Field field = clazz.getDeclaredField("ID_FIELD");
				// 返回之前先拼接索引域名
				return combineField(toTableName(clazz), field.get(clazz).toString());
			} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
				logger.error("实体类中没有ID_FIELD变量：" + clazz.getName());
				throw new SearchException(e).setDetailedMessage(e);
			}
		}
		throw new SearchException("并非合法的实体类：" + clazz.getName());
	}

	/**
	 * 获取每个单据的date（只进行时间范围筛选，不进行搜索）字段的列名（索引的field名）
	 * 
	 * @param tableName
	 *            单据类型
	 * @return date字段的列名
	 */
	public static String getDateField(Table_name tableName) {
		return getDateField(toClass(tableName));
	}

	/**
	 * 根据实体类型获取每个单据的date（只进行时间范围筛选，不进行搜索）字段的列名（索引的field名）
	 * 
	 * @param clazz
	 *            实体类型
	 * @return date字段的列名
	 */
	public static String getDateField(Class<?> clazz) {
		if (checkEntityClass(clazz)) {
			try {
				Field field = clazz.getDeclaredField("DATE_FIELD");
				// 返回之前先拼接索引域名
				return combineField(toTableName(clazz), field.get(clazz).toString());
			} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
				logger.error("实体类中没有DATE_FIELD变量：" + clazz.getName());
				throw new SearchException(e).setDetailedMessage(e);
			}
		}
		throw new SearchException("并非合法的实体类：" + clazz.getName());
	}

	/**
	 * 获取实体id字段的值
	 * 
	 * @param object
	 *            实体对象
	 * @return id
	 */
	public static <T> Long getId(T object) {
		Class<?> clazz = object.getClass();
		if (checkEntityClass(clazz)) {
			try {
				Field field = clazz.getDeclaredField("id");
				field.setAccessible(true);
				return (Long) field.get(object);
			} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
				logger.error("实体类中没有id变量：" + clazz.getName());
				throw new SearchException(e).setDetailedMessage(e);
			}
		}
		throw new SearchException("并非合法的实体类：" + clazz.getName());
	}

	/**
	 * 创建对象
	 * 
	 * @param <T>
	 * @param clazz
	 *            所创建对象的类型
	 * @param id
	 *            对象的id
	 * @return 创建的对象
	 */
	public static <T> T createObject(Class<T> clazz, Long id) {
		if (checkEntityClass(clazz)) {
			try {
				T element = clazz.newInstance();
				Field field = clazz.getDeclaredField("id");
				field.setAccessible(true);
				field.set(element, id);
				return element;
			} catch (IllegalArgumentException e) {
				logger.error("", e);
			} catch (IllegalAccessException e) {
				logger.error("", e);
			} catch (NoSuchFieldException e) {
				logger.error("", e);
			} catch (SecurityException e) {
				logger.error("", e);
			} catch (InstantiationException e) {
				logger.error("", e);
			}
		}
		logger.error("创建对象失败：clazz=" + clazz + ",id=" + id);
		return null;
	}

	/**
	 * 根据实体类获取对应的单据类型
	 * 
	 * @param clazz
	 *            实体类型
	 * @return 单据类型
	 */
	public static Table_name toTableName(Class<?> clazz) {
		if (checkEntityClass(clazz)) {
			try {
				Field field = clazz.getDeclaredField("TABLE_NAME");
				return Table_name.valueOf(field.get(clazz).toString());
			} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
				logger.error("实体类中没有TABLE_NAME变量：" + clazz.getName());
				throw new SearchException(e).setDetailedMessage(e);
			}
		}
		throw new SearchException("并非合法的实体类：" + clazz.getName());
	}

	/**
	 * 根据单据类型获取对应的实体类型
	 * 
	 * @param tableName
	 *            单据类型
	 * @return 实体类型
	 */
	public static Class<?> toClass(Table_name tableName) {
		if (tableName == null) {
			return null;
		}
		List<Class<?>> classes = getEntityClasses();
		for (Class<?> clazz : classes) {
			if (tableName == toTableName(clazz)) {
				return clazz;
			}
		}
		throw new SearchException("并非合法的表：" + tableName.value());
	}

	/**
	 * 获取索引域名，因为不同的表可能会有相同名称的域（即数据库表的列名完全相同），因此，不能用字段名称作为索引的Field域名，
	 * 需要拼接上表名才能保证每个索引域名独一无二
	 * 
	 * @param tableName
	 *            表名
	 * @param field
	 *            数据库表中字段名称
	 * @return 拼接后的索引域名
	 */
	public static String combineField(Table_name tableName, String field) {
		if (tableName == null) {
			return null;
		}
		return combineField(tableName.value(), field);
	}

	/**
	 * 获取索引域名，因为不同的表可能会有相同名称的域（即数据库表的列名完全相同），因此，不能用字段名称作为索引的Field域名，
	 * 需要拼接上表名才能保证每个索引域名独一无二
	 * 
	 * @param tableName
	 *            表名
	 * @param field
	 *            数据库表中字段名称
	 * @return 拼接后的索引域名
	 */
	public static String combineField(String tableName, String field) {
		if (StringUtils.isEmpty(tableName) || StringUtils.isEmpty(field)) {
			return null;
		}
		return tableName.toLowerCase() + "." + field.toLowerCase();
	}

}
