package com.uas.search.service.impl;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.apache.lucene.document.Document;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.NumericRangeQuery;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import com.uas.search.util.CollectionUtils;
import com.uas.search.util.StringUtils;

import com.uas.search.constant.SearchConstants;
import com.uas.search.constant.model.BaseOrder;
import com.uas.search.constant.model.BaseOrderDetail;
import com.uas.search.constant.model.PageParams;
import com.uas.search.constant.model.PageParams.FilterField;
import com.uas.search.constant.model.SPage;
import com.uas.search.exception.SearchException;
import com.uas.search.model.OrderInvoice;
import com.uas.search.model.Order;
import com.uas.search.model.PurchaseInvoice;
import com.uas.search.model.Purchase;
import com.uas.search.service.OrderSearchService;
import com.uas.search.util.DocumentToObjectUtils;
import com.uas.search.util.OrderSearchUtils;
import com.uas.search.util.SearchUtils;

/**
 * 单据搜索实现
 * 
 * @author sunyj
 * @since 2016年10月18日 下午5:59:12
 */
@Service
public class OrderSearchServiceImpl implements OrderSearchService {

	/**
	 * 默认的页码
	 */
	private static final int PAGE_INDEX = 1;

	/**
	 * 默认每页的大小
	 */
	private static final int PAGE_SIZE = 20;

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

	@Override
	public SPage<BaseOrder> getOrderIdsWithDetail(String keyword, Table_name tableName, PageParams params) {
		if (tableName == Table_name.ORDER) {
			return searchOrderIds(keyword, params);
		} else if (tableName == Table_name.ORDER_INVOICE) {
			return searchOrderInvoiceIds(keyword, params);
		} else if (tableName == Table_name.PURCHASE) {
			return searchPurchaseIds(keyword, params);
		} else if (tableName == Table_name.PURCHASE_INVOICE) {
			return searchPurchaseInvoiceIds(keyword, params);
		}
		throw new SearchException("tableName不合法");
	}

	@Override
	public SPage<BaseOrder> searchOrderIds(String keyword, PageParams params) {
		return searchMainOrder(keyword, params, SearchConstants.ORDER_TABLE_NAME);
	}

	@Override
	public SPage<BaseOrder> searchOrderInvoiceIds(String keyword, PageParams params) {
		return searchMainOrder(keyword, params, SearchConstants.ORDER_INVOICE_TABLE_NAME);
	}

	@Override
	public SPage<BaseOrder> searchPurchaseIds(String keyword, PageParams params) {
		return searchMainOrder(keyword, params, SearchConstants.PURCHASE_TABLE_NAME);
	}

	@Override
	public SPage<BaseOrder> searchPurchaseInvoiceIds(String keyword, PageParams params) {
		return searchMainOrder(keyword, params, SearchConstants.PURCHASE_INVOICE_TABLE_NAME);
	}

	/**
	 * 搜索主索引（相对于明细来说）
	 * 
	 * @param keyword
	 *            关键词
	 * @param params
	 *            其他信息，可能有分页信息，filter中可能有可以过滤的信息
	 * @param tableName
	 *            主表名
	 * @return 搜索结果
	 */
	private SPage<BaseOrder> searchMainOrder(String keyword, PageParams params, String tableName) {
		IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(tableName);
		// 获取该表keyword可以搜索的域
		List<String> keywordFields = OrderSearchUtils.getKeywordFields(tableName);

		SPage<BaseOrder> sPage = new SPage<>();
		BooleanQuery booleanQuery = new BooleanQuery();

		if (keyword == null) {
			keyword = "";
		}
		// 关键词带空格，进行与操作
		String[] strs = keyword.split(" ");
		for (String str : strs) {
			// keyword可能是哪些域
			BooleanQuery booleanQuery2 = new BooleanQuery();
			for (String keywordField : keywordFields) {
				booleanQuery2.add(SearchUtils.getRegexpQuery(keywordField, str), BooleanClause.Occur.SHOULD);
			}
			booleanQuery.add(booleanQuery2, BooleanClause.Occur.MUST);
		}

		// 分页信息
		if (params == null) {
			sPage.setPage(PAGE_INDEX);
			sPage.setSize(PAGE_SIZE);
			sPage.setFirst(true);
		} else {
			int page = params.getPage();
			if (page > 0) {
				sPage.setPage(page);
			} else {
				sPage.setPage(PAGE_INDEX);
				sPage.setFirst(true);
			}
			int size = params.getSize();
			if (size > 0) {
				sPage.setSize(size);
			} else {
				sPage.setSize(PAGE_SIZE);
			}

			Map<FilterField, Object> filters = params.getFilters();
			if (!CollectionUtils.isEmpty(filters)) {
				// 如果filter中有订单状态
				if (!StringUtils.isEmpty(filters.get(FilterField.ORDER_STATUS))) {
					@SuppressWarnings("unchecked")
					List<Integer> statusList = (List<Integer>) filters.get(FilterField.ORDER_STATUS);
					BooleanQuery booleanQuery2 = new BooleanQuery();
					for (Integer status : statusList) {
						booleanQuery2.add(
								new TermQuery(
										new Term(OrderSearchUtils.getStatusField(tableName), String.valueOf(status))),
								BooleanClause.Occur.SHOULD);
					}
					booleanQuery.add(booleanQuery2, BooleanClause.Occur.MUST);
				}
				// 如果filter中有买方个人uu
				if (!StringUtils.isEmpty(filters.get(FilterField.ORDER_BUYER_UU))) {
					booleanQuery.add(new TermQuery(new Term(OrderSearchUtils.getBuyeruuField(tableName),
							filters.get(FilterField.ORDER_BUYER_UU).toString())), BooleanClause.Occur.MUST);
				}
				// 如果filter中有买方企业uu
				if (!StringUtils.isEmpty(filters.get(FilterField.ORDER_BUYER_EN_UU))) {
					booleanQuery.add(new TermQuery(new Term(OrderSearchUtils.getBuyerenuuField(tableName),
							filters.get(FilterField.ORDER_BUYER_EN_UU).toString())), BooleanClause.Occur.MUST);
				}
				// 如果filter中有卖方企业uu
				if (!StringUtils.isEmpty(filters.get(FilterField.ORDER_SELLER_EN_UU))) {
					booleanQuery.add(new TermQuery(new Term(OrderSearchUtils.getSellerenuuField(tableName),
							filters.get(FilterField.ORDER_SELLER_EN_UU).toString())), BooleanClause.Occur.MUST);
				}
				// 如果最小时间和最大时间至少传了一个参数，说明需要对时间范围进行限定
				if (!StringUtils.isEmpty(filters.get(FilterField.ORDER_MIN_TIME))
						|| !StringUtils.isEmpty(filters.get(FilterField.ORDER_MAX_TIME))) {
					// 最小日期初始化为2016.1.1
					Calendar calendar = Calendar.getInstance();
					calendar.set(2016, 0, 1);
					// 最大日期初始化为现在
					long minTime = calendar.getTimeInMillis();
					long maxTime = new Date().getTime();
					if (!StringUtils.isEmpty(filters.get(FilterField.ORDER_MIN_TIME))) {
						minTime = Long.parseLong(filters.get(FilterField.ORDER_MIN_TIME).toString());
					}
					if (!StringUtils.isEmpty(filters.get(FilterField.ORDER_MAX_TIME))) {
						maxTime = Long.parseLong(filters.get(FilterField.ORDER_MAX_TIME).toString());
					}
					booleanQuery.add(NumericRangeQuery.newLongRange(SearchConstants.CREATETIME_FIELD, minTime, maxTime,
							true, true), BooleanClause.Occur.MUST);
				}
			}
		}
		logger.info(booleanQuery.toString());

		try {
			TopDocs topDocs;
			// 如果页码不为1
			if (sPage.getPage() > 1) {
				TopDocs previousTopDocs = indexSearcher.search(booleanQuery, (sPage.getPage() - 1) * sPage.getSize());
				int totalHits = previousTopDocs.totalHits;
				ScoreDoc[] previousScoreDocs = previousTopDocs.scoreDocs;
				// 如果主表中未匹配到结果，再搜索其明细表
				if (totalHits == 0) {
					return searchDetails(keyword, params, tableName);
				} else {
					if ((sPage.getPage() - 1) * sPage.getSize() >= totalHits) {
						throw new SearchException("页码过大：元素总数量为" + totalHits);
					}
					topDocs = indexSearcher.searchAfter(previousScoreDocs[previousScoreDocs.length - 1], booleanQuery,
							sPage.getSize());
				}
			} else {
				sPage.setFirst(true);
				topDocs = indexSearcher.search(booleanQuery, sPage.getSize());
			}
			// 如果主表中未匹配到结果，再搜索其明细表
			int totalHits = topDocs.totalHits;
			if (totalHits < 1) {
				return searchDetails(keyword, params, tableName);
			}

			// 设置总元素个数、页数等信息
			sPage.setTotalElement(totalHits);
			int totalPage = (int) Math.ceil(totalHits / (1.0 * sPage.getSize()));
			sPage.setTotalPage(totalPage);
			if (totalPage == sPage.getPage()) {
				sPage.setLast(true);
			}

			ScoreDoc[] scoreDocs = topDocs.scoreDocs;
			List<BaseOrder> baseOrders = new ArrayList<>();
			for (ScoreDoc scoreDoc : scoreDocs) {
				Document document = indexSearcher.doc(scoreDoc.doc);
				Long id = Long.valueOf(document.get(OrderSearchUtils.getIdField(tableName)));
				String code = document.get(OrderSearchUtils.getCodeField(tableName));
				BaseOrder baseOrder = new BaseOrder(id, code);
				List<BaseOrderDetail> details = DocumentToObjectUtils
						.toDetailsList(document.get(OrderSearchUtils.getDetailsField(tableName)));
				if (!CollectionUtils.isEmpty(details)) {
					baseOrder.setDetails(details);
				}
				baseOrders.add(baseOrder);
			}
			sPage.setContent(baseOrders);
		} catch (NumberFormatException | IOException e) {
			throw new SearchException(e).setDetailedMessage(e);
		} finally {
			SearchUtils.releaseIndexSearcher(indexSearcher);
		}
		return sPage;
	}

	/**
	 * 搜索明细索引
	 * 
	 * @param keyword
	 *            关键词
	 * @param params
	 *            其他信息，可能有分页信息，filter中可能有可以过滤的信息
	 * @param tableName
	 *            明细表名
	 * @return 搜索结果
	 */
	private SPage<BaseOrder> searchDetails(String keyword, PageParams params, String tableName) {
		// 与主表不同，不允许keyword为空（不允许利用正则表达式获取所有明细索引，原因是正则搜索与分词有冲突）
		if (SearchUtils.isKeywordInvalid(keyword)) {
			throw new SearchException("搜索关键词无效：" + keyword);
		}
		IndexSearcher indexSearcher = SearchUtils.getIndexSearcher(tableName);

		SPage<BaseOrder> sPage = new SPage<>();
		BooleanQuery booleanQuery = new BooleanQuery();

		// 关键词带空格，进行与操作
		String[] strs = keyword.split(" ");
		for (String str : strs) {
			booleanQuery.add(SearchUtils.getBooleanQuery(OrderSearchUtils.getDetailsField(tableName), str),
					BooleanClause.Occur.MUST);
		}

		// 分页信息
		if (params == null) {
			sPage.setPage(PAGE_INDEX);
			sPage.setSize(PAGE_SIZE);
			sPage.setFirst(true);
		} else {
			int page = params.getPage();
			if (page > 0) {
				sPage.setPage(page);
			} else {
				sPage.setPage(PAGE_INDEX);
				sPage.setFirst(true);
			}
			int size = params.getSize();
			if (size > 0) {
				sPage.setSize(size);
			} else {
				sPage.setSize(PAGE_SIZE);
			}

			Map<FilterField, Object> filters = params.getFilters();
			if (!CollectionUtils.isEmpty(filters)) {
				// 如果最小时间和最大时间至少传了一个参数，说明需要对时间范围进行限定
				if (!StringUtils.isEmpty(filters.get(FilterField.ORDER_MIN_TIME))
						|| !StringUtils.isEmpty(filters.get(FilterField.ORDER_MAX_TIME))) {
					// 最小日期初始化为2016.1.1
					Calendar calendar = Calendar.getInstance();
					calendar.set(2016, 0, 1);
					// 最大日期初始化为现在
					long minTime = calendar.getTimeInMillis();
					long maxTime = new Date().getTime();
					if (!StringUtils.isEmpty(filters.get(FilterField.ORDER_MIN_TIME))) {
						minTime = Long.parseLong(filters.get(FilterField.ORDER_MIN_TIME).toString());
					}
					if (!StringUtils.isEmpty(filters.get(FilterField.ORDER_MAX_TIME))) {
						maxTime = Long.parseLong(filters.get(FilterField.ORDER_MAX_TIME).toString());
					}
					booleanQuery.add(NumericRangeQuery.newLongRange(SearchConstants.CREATETIME_FIELD, minTime, maxTime,
							true, true), BooleanClause.Occur.MUST);
				}
			}
		}
		logger.info(booleanQuery.toString());

		try {
			TopDocs topDocs = null;
			// 如果页码不为1
			if (sPage.getPage() > 1) {
				TopDocs previousTopDocs = indexSearcher.search(booleanQuery, (sPage.getPage() - 1) * sPage.getSize());
				int totalHits = previousTopDocs.totalHits;
				ScoreDoc[] previousScoreDocs = previousTopDocs.scoreDocs;
				if (totalHits != 0) {
					if ((sPage.getPage() - 1) * sPage.getSize() >= totalHits) {
						throw new SearchException("页码过大：元素总数量为" + totalHits);
					}
					topDocs = indexSearcher.searchAfter(previousScoreDocs[previousScoreDocs.length - 1], booleanQuery,
							sPage.getSize());
				}
			} else {
				sPage.setFirst(true);
				topDocs = indexSearcher.search(booleanQuery, sPage.getSize());
			}

			// 设置总元素个数、页数等信息
			if (topDocs == null || topDocs.totalHits < 1) {
				sPage.setTotalElement(0);
				sPage.setFirst(true);
				sPage.setLast(true);
				return sPage;
			}
			sPage.setTotalElement(topDocs.totalHits);
			int totalPage = (int) Math.ceil(topDocs.totalHits / (1.0 * sPage.getSize()));
			sPage.setTotalPage(totalPage);
			if (totalPage == sPage.getPage()) {
				sPage.setLast(true);
			}

			ScoreDoc[] scoreDocs = topDocs.scoreDocs;
			List<BaseOrder> baseOrders = new ArrayList<>();
			for (ScoreDoc scoreDoc : scoreDocs) {
				Document document = indexSearcher.doc(scoreDoc.doc);
				Long id = Long.valueOf(document.get(OrderSearchUtils.getIdField(tableName)));
				String code = document.get(OrderSearchUtils.getCodeField(tableName));
				BaseOrder baseOrder = new BaseOrder(id, code);
				List<BaseOrderDetail> details = DocumentToObjectUtils
						.toDetailsList(document.get(OrderSearchUtils.getDetailsField(tableName)));
				if (!CollectionUtils.isEmpty(details)) {
					baseOrder.setDetails(details);
				}
				baseOrders.add(baseOrder);
			}
			sPage.setContent(baseOrders);
		} catch (NumberFormatException | IOException e) {
			throw new SearchException(e).setDetailedMessage(e);
		} finally {
			SearchUtils.releaseIndexSearcher(indexSearcher);
		}
		return sPage;
	}

	@Override
	public Order getOrder(Long id) {
		return DocumentToObjectUtils.toOrder(
				SearchUtils.getDocumentById(SearchConstants.ORDER_TABLE_NAME, SearchConstants.ORDER_ID_FIELD, id));
	}

	@Override
	public OrderInvoice getOrderInvoice(Long id) {
		return DocumentToObjectUtils.toOrderInvoice(SearchUtils
				.getDocumentById(SearchConstants.ORDER_INVOICE_TABLE_NAME, SearchConstants.ORDER_INVOICE_ID_FIELD, id));
	}

	@Override
	public Purchase getPurchase(Long id) {
		return DocumentToObjectUtils.toPurchase(SearchUtils.getDocumentById(SearchConstants.PURCHASE_TABLE_NAME,
				SearchConstants.PURCHASE_ID_FIELD, id));
	}

	@Override
	public PurchaseInvoice getPurchaseInvoice(Long id) {
		return DocumentToObjectUtils.toPurchaseInvoice(SearchUtils.getDocumentById(
				SearchConstants.PURCHASE_INVOICE_TABLE_NAME, SearchConstants.PURCHASE_INVOICE_ID_FIELD, id));
	}

}
