package com.platform.modules.live.service.impl;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.platform.common.utils.DateUtils;
import com.platform.modules.live.constants.SceneConstants;
import com.platform.modules.live.entity.LiveMultimediaEntity;
import com.platform.modules.live.entity.LiveReportEntity;
import com.platform.modules.live.entity.LiveSceneChannelEntity;
import com.platform.modules.live.entity.LiveSceneEntity;
import com.platform.modules.live.service.LiveMultimediaService;
import com.platform.modules.live.service.LiveSceneChannelService;
import com.platform.modules.live.service.LuceneService;
import com.platform.modules.live.utils.LuceneTypeEnum;
import com.platform.modules.live.utils.ReportTypeEnum;
import org.apache.commons.lang.StringUtils;
import org.apache.lucene.document.*;
import org.apache.lucene.index.*;
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
import org.apache.lucene.search.*;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.util.HtmlUtils;
import org.wltea.analyzer.lucene.IKAnalyzer;

import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.text.DecimalFormat;
import java.util.*;

/**
 * @author TROY
 * @version V1.0
 * @ClassName: LuceneServiceImpl
 * @Description: TODO
 * @date 2019/2/20 19:46
 */
@Service
@Transactional
public class LuceneServiceImpl implements LuceneService {

    @Autowired
    private LiveSceneChannelService liveSceneChannelService;
    @Autowired
    private LiveMultimediaService liveMultimediaService;

    @Value("${platform.lucene.path:/lucene/data/}")
    private String INDEX_PATH;

    /**
     * lucene检索
     *
     * @param type    内容类型 LuceneTypeEnum
     * @param params  过滤参数map
     * @param page    页数 从1开始
     * @param size    返回数据
     * @param reverse 排序是否正序
     */
    @Override
    public List<Document> search(String type, Map<String, String> params, int page, int size, boolean reverse) {
        if (!LuceneTypeEnum.contain(type)) {
            return null;
        }
        IndexReader reader = null;
        List list = null;
        try {
            String filePath = INDEX_PATH + type + File.separator;
            dirCheck(filePath);

            FSDirectory fsd = FSDirectory.open(Paths.get(filePath));
            if (fsd.listAll().length == 0) {
                return list;
            }

            reader = DirectoryReader.open(fsd);
            IndexSearcher searcher = new IndexSearcher(reader);

            // 现场和报道排序优先级 置顶 > 排序 > 报道时间
            List<SortField> sortList = new ArrayList<>();
            if (LuceneTypeEnum.LUCENE_SCENE.getType().equals(type)) {
                if (StringUtils.isNotBlank(params.get("cid"))) {
                    Integer cid = Integer.valueOf(params.get("cid"));
                    sortList.add(new SortedNumericSortField("dragSort" + cid, SortField.Type.INT, true));
                }
            }

            Integer source = StringUtils.isNotBlank(params.get("source")) ? Integer.valueOf(params.get("source")) : null;
            params.remove("source");
            sortList.add(new SortedNumericSortField("top", SortField.Type.INT, true));
            sortList.add(new SortedNumericSortField("priority", SortField.Type.INT, true));
            if (LuceneTypeEnum.LUCENE_SCENE.getType().equals(type) && source == null) {//source为空时为客户端请求，为null
                sortList.add(new SortedNumericSortField("sortstatus", SortField.Type.INT, false));
            }
            sortList.add(new SortedNumericSortField("sort", SortField.Type.LONG, reverse));
            sortList.add(new SortedNumericSortField("sortid", SortField.Type.INT, reverse));
            SortField[] sortArr = new SortField[sortList.size()];
            Sort sort = new Sort(sortList.toArray(sortArr));

            ScoreDoc[] scoreDocs = null;
            if (null == params || params.size() <= 0) {
                TopFieldCollector collector = TopFieldCollector.create(sort, page * size, false, false, false, false);
                searcher.search(new MatchAllDocsQuery(), collector);
                scoreDocs = collector.topDocs((page - 1) * size, size).scoreDocs;
            } else {
                int arrSize = params.keySet().size();
                String[] fields = new String[arrSize];
                fields = params.keySet().toArray(fields);
                String[] stringQuery = new String[arrSize];
                stringQuery = params.values().toArray(stringQuery);
                BooleanClause.Occur[] occ = new BooleanClause.Occur[arrSize];
                Arrays.fill(occ, BooleanClause.Occur.MUST);
                Query query = MultiFieldQueryParser.parse(stringQuery, fields, occ, new IKAnalyzer());
                TopFieldDocs fieldDocs = searcher.search(query, page * size, sort);
                if (fieldDocs.totalHits < ((page - 1) * size)) {
                    scoreDocs = new ScoreDoc[]{};
                } else {
                    scoreDocs = Arrays.copyOfRange(fieldDocs.scoreDocs, (page - 1) * size, fieldDocs.scoreDocs.length);
                }
            }


            list = new ArrayList();
            for (ScoreDoc sd : scoreDocs) {
                Document doc = searcher.doc(sd.doc);
                list.add(doc);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return list;
    }

    @Override
    public void createIndex(Object object, boolean create) throws IOException {
        if (object == null) {
            return;
        }
        String type;
        if (object instanceof LiveSceneEntity) {
            type = LuceneTypeEnum.LUCENE_SCENE.getType();
        } else if (object instanceof LiveReportEntity) {
            type = LuceneTypeEnum.LUCENE_REPORT.getType();
        } else {
            // 限制其他内容处理
            return;
        }
        String indexPath = INDEX_PATH + type + File.separator;
        dirCheck(indexPath);

        Directory indexDir = FSDirectory.open(Paths.get(indexPath));
        IndexWriterConfig iwc = new IndexWriterConfig(new IKAnalyzer());


        if (create) {
            iwc.setOpenMode(IndexWriterConfig.OpenMode.CREATE);
        } else {
            iwc.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND);
        }
        IndexWriter writer = null;
        try {
            writer = new IndexWriter(indexDir, iwc);
            if (LuceneTypeEnum.LUCENE_SCENE.getType().equals(type)) {
                indexSceneDoc(writer, (LiveSceneEntity) object);
            } else if (LuceneTypeEnum.LUCENE_REPORT.getType().equals(type)) {
                indexReportDoc(writer, (LiveReportEntity) object);
            }
        } finally {
            if (null != writer) {
                writer.close();
            }
        }
    }

    @Override
    public void deleteIndex(String type, String name, String value) throws IOException {
        if (!LuceneTypeEnum.contain(type) || StringUtils.isBlank(name) || StringUtils.isBlank(value)) {
            return;
        }
//		String indexPath = realPathResolver.get(INDEX_PATH + type + File.separator);
        String indexPath = INDEX_PATH + type + File.separator;
        dirCheck(indexPath);

        Directory indexDir = FSDirectory.open(Paths.get(indexPath));
        IndexWriterConfig iwc = new IndexWriterConfig(new IKAnalyzer());
        IndexWriter writer = null;
        try {
            writer = new IndexWriter(indexDir, iwc);
            writer.deleteDocuments(new Term(name, value));
        } finally {
            if (null != writer) {
                writer.close();
            }
        }
    }

    private synchronized void indexSceneDoc(IndexWriter writer, LiveSceneEntity scene) throws IOException {
        // TODO 索引参数变动需要根据情况调整 livesceneservice update 参数，确保更新参数一致
        Document doc = new Document();

        // 20200320_Mxy 添加拖动排序索引
        List<Integer> channelList = scene.getChannelIdList();

        if (null != channelList && channelList.size() > 0) {
            channelList.forEach(channel -> {
                EntityWrapper<LiveSceneChannelEntity> wrapper = new EntityWrapper<>();
                wrapper.eq("scene_id", scene.getId());
                wrapper.eq("channel_id", channel);
                LiveSceneChannelEntity entity = liveSceneChannelService.selectOne(wrapper);
                if (null != entity) {
                    doc.add(new SortedNumericDocValuesField("dragSort" + channel, entity.getDragSort()));
                }
            });
        }

        doc.add(new StringField("id", "" + scene.getId(), Field.Store.YES));
        doc.add(new StringField("thumb", StringUtils.isNotBlank(scene.getTitleImage()) ? scene.getTitleImage() : "",
                Field.Store.YES));
        doc.add(new TextField("title", scene.getTitle(), Field.Store.YES));
        doc.add(new StringField("norb", "live", Field.Store.YES));
        doc.add(new StringField("type", null == scene.getSceneType() ? "" : String.valueOf(scene.getSceneType()), Field.Store.YES));
//		// 20190329 TROY 栏目信息
//		doc.add(new StringField("cid", null==scene.getChannelId()?"":String.valueOf(scene.getChannelId()), Field.Store.YES));
//		doc.add(new StringField("cname", StringUtils.isBlank(scene.getChannelName())?"":scene.getChannelName(), Field
//					.Store.YES));
        // 20190417 weiyan 栏目信息
        doc.add(new StringField("cnames", null == scene.getChannelsName() ? "" : String.valueOf(scene.getChannelsName()), Field.Store.YES));
        String cids = "";
        if (scene.getChannelIdList() != null && scene.getChannelIdList().size() > 0) {
            for (Integer id : scene.getChannelIdList()) {
                doc.add(new StringField("cid", null == id ? "" : String.valueOf(id), Field.Store.YES));
                cids = cids + "，" + id;
            }
        }
        if (scene.getChannelNameList() != null && scene.getChannelNameList().size() > 0) {
            for (String name : scene.getChannelNameList()) {
                doc.add(new StringField("cname", StringUtils.isBlank(name) ? "" : name, Field.Store.YES));
            }
        }

        // 现场状态
        // 20190320 TROY 直播状态
        String status = (null == scene.getLiveState()) ? String.valueOf(SceneConstants.LIVE_STATE_NOTICE) : String.valueOf(scene.getLiveState());
        doc.add(new StringField("status", status, Field.Store.YES));
        // 直播时间
        Long liveStartDate;
        if (null != scene.getLiveStart()) {
            liveStartDate = scene.getLiveStart().getTime();
        } else if (null != scene.getCreateDate()) {
            liveStartDate = scene.getCreateDate().getTime();
        } else {
            liveStartDate = System.currentTimeMillis();
        }
        doc.add(new StringField("time", DateUtils.format(new Date(liveStartDate), DateUtils.DATE_TIME_MINUTE_PATTERN), Field.Store.YES));
        // 直播时间排序
        doc.add(new SortedNumericDocValuesField("sort", liveStartDate));
        // 置顶
        doc.add(new SortedNumericDocValuesField("top", null == scene.getIsTop() ? 0 : scene.getIsTop()));
        // 排序
        doc.add(new SortedNumericDocValuesField("priority", null == scene.getPriority() ? 0 : scene.getPriority()));
        // ID排序
        doc.add(new SortedNumericDocValuesField("sortid", scene.getId()));
        // 直播状态排序
        doc.add(new SortedNumericDocValuesField("sortstatus", (null == scene.getLiveState()) ? SceneConstants.LIVE_STATE_NOTICE : scene.getLiveState()));

        if (writer.getConfig().getOpenMode() == IndexWriterConfig.OpenMode.CREATE) {
            writer.addDocument(doc);
        } else {
            writer.updateDocument(new Term("id", "" + scene.getId()), doc);
        }
    }

    private synchronized void indexReportDoc(IndexWriter writer, LiveReportEntity report) throws IOException {
        Document doc = new Document();
        // 报道ID
        doc.add(new StringField("id", String.valueOf(report.getId()), Field.Store.YES));
        // 现场ID
        doc.add(new StringField("sceneId", String.valueOf(report.getSceneId()), Field.Store.NO));
        // 报道类型
        doc.add(new StringField("reportType", null != report.getReportType() ? String.valueOf(report.getReportType())
                : String.valueOf(ReportTypeEnum.TEXT.getKey()),
                Field.Store.YES));
        Date reportDate;
        if (null != report.getReportDate()) {
            reportDate = report.getReportDate();
        } else if (null != report.getCreateDate()) {
            reportDate = report.getCreateDate();
        } else {
            reportDate = new Date();
        }
        // 报道时间
        doc.add(new StringField("reportDate", DateUtils.format(reportDate, DateUtils.DATE_TIME_MINUTE_PATTERN), Field.Store.YES));
        // 报道记者
        doc.add(new StringField("reporterName", StringUtils.isNotBlank(report.getReporterName()) ? report.getReporterName() : "", Field
                .Store.YES));
        // 报道内容
        doc.add(new StringField("content", StringUtils.isNotBlank(report.getContent()) ? report.getContent() : "", Field
                .Store.YES));
        // 相关报道
        doc.add(new StringField("contentHref", StringUtils.isNotBlank(report.getContentHref()) ? HtmlUtils.htmlUnescape(report
                .getContentHref()) : "", Field
                .Store.YES));
        // 报道资源
        doc.add(new StringField("mediaUrl", StringUtils.isNotBlank(report.getMediaUrl()) ? report.getMediaUrl() : "", Field
                .Store.YES));
        // 图片资源
        doc.add(new StringField("imgList", StringUtils.isNotBlank(report.getImgList()) ? report.getImgList() : "", Field
                .Store.YES));
        // 置顶
        doc.add(new SortedNumericDocValuesField("top", null == report.getIsTop() ? 0 : report.getIsTop()));
        // 排序
        doc.add(new SortedNumericDocValuesField("priority", null == report.getPriority() ? 0 : report.getPriority()));
        // 根据报道时间排序
        doc.add(new SortedNumericDocValuesField("sort", reportDate.getTime()));
        // ID排序
        doc.add(new SortedNumericDocValuesField("sortid", report.getId()));
        // 20200421_Mxy 音频时长
        if (null != report.getReportType() && ReportTypeEnum.AUDIO.getKey().equals(report.getReportType()) && StringUtils.isNotBlank(report.getMediaUrl())) {
            LiveMultimediaEntity liveMultimediaEntity = liveMultimediaService.getByMediaUrl(report.getMediaUrl());
            if (null != liveMultimediaEntity) {
                doc.add(new StringField("audioDuration", String.valueOf(liveMultimediaEntity.getMediaDuration()), Field.Store.YES));
            }
        }
        // 20200430_Mxy 相关报道
        doc.add(new StringField("relatedReport", StringUtils.isNotBlank(report.getRelatedReport()) ? report.getRelatedReport() : "", Field.Store.YES));
        // 20210301_Matt 图文报道图片宽高
        if (null != report.getReportType() && ReportTypeEnum.PICTURE.getKey().equals(report.getReportType()) && StringUtils.isNotBlank(report.getImgList())) {
            String[] imgUrls = StringUtils.split(report.getImgList(), ",");
            if (imgUrls != null && imgUrls.length > 0) {
                StringBuilder sb = new StringBuilder();

                for (String imgUrl : imgUrls) {
                    LiveMultimediaEntity liveMultimediaEntity = liveMultimediaService.getByMediaUrl(imgUrl);
                    if (liveMultimediaEntity != null) {

                        Double width = new Double(liveMultimediaEntity.getMediaWidth());
                        Double height = new Double(liveMultimediaEntity.getMediaHeight());

                        if (StringUtils.isBlank(String.valueOf(sb))) {
                            if (width.equals(height)) {
                                sb.append("1:1");
                            } else if (width > height) {
                                Double proportion = Double.parseDouble(new DecimalFormat("#.00").format(width / height));
                                sb.append(proportion).append(":1");
                            } else if (height > width) {
                                Double proportion = Double.parseDouble(new DecimalFormat("#.00").format(height / width));
                                sb.append("1:").append(proportion);
                            }
                        } else {
                            if (width.equals(height)) {
                                sb.append(",1:1");
                            } else if (width > height) {
                                Double proportion = Double.parseDouble(new DecimalFormat("#.00").format(width / height));
                                sb.append(",").append(proportion).append(":1");
                            } else if (height > width) {
                                Double proportion = Double.parseDouble(new DecimalFormat("#.00").format(height / width));
                                sb.append(",").append("1:").append(proportion);
                            }
                        }
                    }
                }

                doc.add(new StringField("widthAndHeight", String.valueOf(sb), Field.Store.YES));
            }
        }

        if (writer.getConfig().getOpenMode() == IndexWriterConfig.OpenMode.CREATE) {
            writer.addDocument(doc);
        } else {
            writer.updateDocument(new Term("id", "" + report.getId()), doc);
        }
    }

    private void dirCheck(String filePath) {
        File file = new File(filePath);
        if (!file.exists()) {
            file.mkdirs();
        }
    }
}
