package com.pcloud.common.utils.solr;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.commons.collections.MapUtils;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.impl.CloudSolrClient;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
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.stereotype.Component;
import com.pcloud.common.utils.DateUtils;
import com.pcloud.common.utils.ListUtils;

/**
 * Description solr全文检索工具类
 * @author PENG
 * @date 2018/4/23
 */
@Component("solrUtils")
public class SolrUtils {

    private static Logger LOGGER = LoggerFactory.getLogger(SolrUtils.class);

    private static final SimpleDateFormat UTC_FULL_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");

    private static String defaultCollection;

    private static CloudSolrClient solrClient;

    /**
     * 新增或修改索引（id主键存在时为更新）
     * @param solrDto solr索引DTO
     * @return 是否成功
     */
    public static Boolean add(SolrDto solrDto) {
        return add(getDefaultCollection(), solrDto);
    }

    /**
     * 新增或修改索引（id主键存在时为更新）
     * @param collection 索引集
     * @param solrDto    solr索引DTO
     * @return 是否成功
     */
    public static Boolean add(String collection, SolrDto solrDto) {
        try {
            solrClient.addBean(collection, solrDto);
            solrClient.commit(collection);
        } catch (Exception e) {
            LOGGER.error("solr新增索引失败:" + e.getMessage(), e);
            return false;
        }
        return true;
    }

    /**
     * 批量新增或修改索引（id主键存在时为更新）
     * @param list solr索引DTO列表
     * @return 是否成功
     */
    public static Boolean add(List<SolrDto> list) {
        return add(getDefaultCollection(), list);
    }

    /**
     * 批量新增或修改索引（id主键存在时为更新）
     * @param collection 索引集
     * @param list       solr索引DTO列表
     * @return 是否成功
     */
    public static Boolean add(String collection, List<SolrDto> list) {
        try {
            solrClient.addBeans(collection, list);
            solrClient.commit(collection);
        } catch (Exception e) {
            LOGGER.error("solr批量新增索引失败:" + e.getMessage(), e);
            return false;
        }
        return true;
    }

    /**
     * 根据id主键删除索引
     * @param id id主键
     * @return 是否成功
     */
    public static Boolean deleteById(String id) {
        return deleteById(getDefaultCollection(), id);
    }

    /**
     * 根据id主键删除索引
     * @param collection 索引集
     * @param id         id主键
     * @return 是否成功
     */
    public static Boolean deleteById(String collection, String id) {
        try {
            solrClient.deleteById(collection, id);
            solrClient.commit(collection);
        } catch (Exception e) {
            LOGGER.error("solr根据id删除索引失败:" + e.getMessage(), e);
            return false;
        }
        return true;
    }

    /**
     * 删除查询结果索引集
     * @param query 查询，删除全部为*:*
     * @return 是否成功
     */
    public static Boolean deleteByQuery(String query) {
        return deleteByQuery(getDefaultCollection(), query);
    }

    /**
     * 删除查询结果索引集
     * @param collection 索引集
     * @param query      查询，删除全部为*:*
     * @return 是否成功
     */
    public static Boolean deleteByQuery(String collection, String query) {
        try {
            solrClient.deleteByQuery(collection, query);
            solrClient.commit(collection);
        } catch (Exception e) {
            LOGGER.error("solr删除查询出的索引失败:" + e.getMessage(), e);
            return false;
        }
        return true;
    }

    /**
     * 根据id主键获取索引
     * @param id id主键
     * @return SolrDto
     */
    public static SolrDto getById(String id) {
        return getById(getDefaultCollection(), id);
    }

    /**
     * 根据id主键获取索引
     * @param collection 索引集
     * @param id         id主键
     * @return SolrDto
     */
    public static SolrDto getById(String collection, String id) {
        try {
            SolrDocument solrDocument = solrClient.getById(collection, id);
            if (null != solrDocument) {
                return transSolrDocument(solrDocument);
            } else {
                return null;
            }
        } catch (Exception e) {
            LOGGER.error("solr根据id查询索引失败:" + e.getMessage(), e);
            return null;
        }
    }

    /**
     * 根据id主键列表批量获取索引
     * @param ids id主键列表
     * @return SolrDto Map
     */
    public static Map<String, SolrDto> getById(List<String> ids) {
        return getById(getDefaultCollection(), ids);
    }

    /**
     * 根据id主键列表批量获取索引
     * @param collection 索引集
     * @param ids        id主键列表
     * @return SolrDto Map
     */
    public static Map<String, SolrDto> getById(String collection, List<String> ids) {
        try {
            if (ListUtils.isEmpty(ids)) {
                return null;
            }
            SolrDocumentList documents = solrClient.getById(collection, ids);
            if (null != documents) {
                List<SolrDto> list = new ArrayList<>();
                for (SolrDocument document : documents) {
                    list.add(transSolrDocument(document));
                }
                return list.stream().collect(Collectors.toMap(SolrDto::getId, Function.identity()));
            } else {
                return null;
            }
        } catch (Exception e) {
            LOGGER.error("solr根据id批量查询索引失败:" + e.getMessage(), e);
            return null;
        }
    }

    /**
     * 查询
     * @param param 查询参数
     * @return SolrResult
     */
    public static SolrResult search(SolrQueryParam param) {
        return search(getDefaultCollection(), param);
    }

    /**
     * 查询
     * @param collection 索引集
     * @param param      查询参数
     * @return SolrResult
     */
    public static SolrResult search(String collection, SolrQueryParam param) {
        try {
            SolrQuery solrQuery = formatQueryParam(param);
            solrQuery.set("wt", "json");
            QueryResponse response = solrClient.query(collection, solrQuery);
            SolrDocumentList documents = response.getResults();
            LOGGER.info("solr查询结果：" + documents.toString());
            SolrResult solrResult = new SolrResult();
            solrResult.setTotalCount(Integer.parseInt(documents.getNumFound() + ""));
            solrResult.setMaxScore(documents.getMaxScore());
            List<SolrDto> list = new ArrayList<>();
            for (SolrDocument document : documents) {
                SolrDto solrDto = transSolrDocument(document);
                list.add(solrDto);
            }
            solrResult.setList(list);
            return solrResult;
        } catch (Exception e) {
            LOGGER.error("solr查询索引失败:" + e.getMessage(), e);
            return null;
        }
    }

    /**
     * 将solr返回的document转换成solrDto
     * @param document solr文档
     * @return SolrDto
     */
    private static SolrDto transSolrDocument(SolrDocument document) {
        SolrDto solrDto = new SolrDto();
        solrDto.setId((String) document.get("id"));
        solrDto.setSaleId((Long) document.get("saleId"));
        solrDto.setSaleCode((String) document.get("saleCode"));
        // text_general类型返回的是列表，特此处理
        Object title = document.get("title");
        if (null != title) {
            List<String> titleList = (List<String>) title;
            solrDto.setTitle(titleList.get(0));
        }
        solrDto.setTypeId((Long) document.get("typeId"));
        solrDto.setTypeCode((String) document.get("typeCode"));
        solrDto.setTypeName((String) document.get("typeName"));
        solrDto.setStartDate((Date) document.get("startDate"));
        solrDto.setEndDate((Date) document.get("endDate"));
        solrDto.setCoverImg((String) document.get("coverImg"));
        Object description = document.get("description");
        if (null != description) {
            List<String> descriptionList = (List<String>) description;
            solrDto.setDescription(descriptionList.get(0));
        }
        solrDto.setCreatedUser((Long) document.get("createdUser"));
        solrDto.setCreatedDate((Date) document.get("createdDate"));
        solrDto.setLastModifiedDate((Date) document.get("lastModifiedDate"));
        solrDto.setChannelId((Long) document.get("channelId"));
        return solrDto;
    }

    /**
     * 组装查询参数
     * @param param solr查询参数
     * @return SolrQuery
     */
    private static SolrQuery formatQueryParam(SolrQueryParam param) {
        SolrQuery solrQuery = new SolrQuery();
        if (null == param) {
            param = new SolrQueryParam();
        }
        if (null != param.getQ()) {
            solrQuery.set("q", param.getQ());
        } else {
            solrQuery.set("q", "*:*");
        }
        if (!ListUtils.isEmpty(param.getFq())) {
            for (String fq : param.getFq()) {
                solrQuery.addFilterQuery(fq);
            }
        }
        if (!MapUtils.isEmpty(param.getSort())) {
            for (String key : param.getSort().keySet()) {
                solrQuery.addSort(key, param.getSort().get(key));
            }
        }
        if (null != param.getStart() && null != param.getRows()) {
            solrQuery.setStart(param.getStart() * param.getRows());
            solrQuery.setRows(param.getRows());
        } else {
            solrQuery.setStart(0);
            solrQuery.setRows(20);
        }
        if (null != param.getDf()) {
            solrQuery.set("df", param.getDf());
        }
        if (null != param.getFl()) {
            solrQuery.set("fl", param.getFl());
        }
        return solrQuery;
    }

    /**
     * 获取UTC全时间字符串，包含T和Z
     * @return 格式化时间字符串
     */
    public static String getUTCFullDateStr() {
        Date date = new Date();
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.HOUR_OF_DAY, -8);
        return UTC_FULL_DATE_FORMAT.format(calendar.getTime());
    }

    /**
     * 将时间转换成UTC全时间字符串，包含T和Z
     * @param date 时间
     * @return 格式化时间字符串
     */
    public static String formatUTCFullDate(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.HOUR_OF_DAY, -8);
        return UTC_FULL_DATE_FORMAT.format(calendar.getTime());
    }

    /**
     * 获取UTC全时间字符串，包含T和Z，时分秒自动设为00:00:00之后，然后减去8小时（solr时区问题）
     * @return 格式化时间字符串
     */
    public static String getUTCShortDateStr() {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(DateUtils.dateFormat(new Date()));
        calendar.add(Calendar.HOUR_OF_DAY, -8);
        return UTC_FULL_DATE_FORMAT.format(calendar.getTime());
    }

    /**
     * 将时间转换成UTC全时间字符串，包含T和Z，时分秒自动设为00:00:00之后，然后减去8小时（solr时区问题）
     * @param date 时间
     * @return 格式化时间字符串
     */
    public static String formatUTCShortDate(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(DateUtils.dateFormat(date));
        calendar.add(Calendar.HOUR_OF_DAY, -8);
        return UTC_FULL_DATE_FORMAT.format(calendar.getTime());
    }

    /**
     * 获取默认集合
     */
    private static String getDefaultCollection() {
        return null == solrClient.getDefaultCollection() ? defaultCollection : solrClient.getDefaultCollection();
    }

    @Value("${solr.default.collection}")
    public void setDefaultCollection(String defaultCollection) {
        SolrUtils.defaultCollection = defaultCollection;
    }

    @Autowired
    public void setSolrClient(SolrClient solrClient) {
        SolrUtils.solrClient = (CloudSolrClient) solrClient;
        SolrUtils.solrClient.setDefaultCollection(defaultCollection);
    }
}
