package org.nl.system.service.lucene.impl;

import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.db.PageResult;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.*;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.BytesRef;
import org.nl.common.exception.BadRequestException;
import org.nl.config.lucene.LuceneAppender;
import org.nl.config.lucene.Searcher;
import org.nl.system.service.lucene.LuceneService;
import org.nl.system.service.lucene.dto.LogQuery;
import org.nl.system.service.lucene.dto.LogRepositoryDTO;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.wltea.analyzer.lucene.IKAnalyzer;

import java.nio.file.Paths;
import java.util.*;


@Service
@RequiredArgsConstructor
@Slf4j
public class LuceneServiceImpl implements LuceneService {



    static String[] INFO_LEVEL = new String[]{"DEBUG","INFO","WARN","ERROR"};

    @Value("${lucene.index.path}")
    private String indexUrl;



    @Override
    public JSONObject query(LogQuery logQuery) {
        JSONObject res = new JSONObject();
        PageResult page = new PageResult();
        page.setPage(logQuery.getPage());
        page.setPageSize(logQuery.getSize());
        try {
            FSDirectory directory = FSDirectory.open(Paths.get(indexUrl));
            DirectoryReader open = DirectoryReader.open(directory);
            IndexSearcher searcher = new IndexSearcher(open);
            // 创建排序对象,需要排序字段SortField，参数：字段的名称、字段的类型、是否反转如果是false，升序。true降序
            BooleanQuery.Builder booleanQueryBuilder = new BooleanQuery.Builder();
            //时间范围查询
            Date startDate = logQuery.getStartTime();
            Date endDate =  logQuery.getEndTime();

            if (startDate == null){
                Calendar calendar=Calendar.getInstance();
                calendar.set(1970, 0, 1);
                startDate = calendar.getTime(); }
            if (endDate == null){ endDate = new DateTime(); }
            TermRangeQuery termRangeQuery = new TermRangeQuery(
                    "requestTime",
                    new BytesRef(DateUtil.format(startDate, "yyyy-MM-dd HH:mm:ss.SSS")),
                    new BytesRef(DateUtil.format(endDate, "yyyy-MM-dd HH:mm:ss.SSS")), true, true);
            booleanQueryBuilder.add(termRangeQuery, BooleanClause.Occur.MUST);

            // 字段之间的与或非关系，MUST表示and，MUST_NOT表示not，SHOULD表示or，有几个fields就必须有几个clauses
            if (ObjectUtil.isNotEmpty(logQuery.getTraceId())){
                TermQuery termQuery = new TermQuery(new Term("traceId", logQuery.getTraceId()));
                booleanQueryBuilder.add(termQuery, BooleanClause.Occur.MUST);
            }
            if (ObjectUtil.isNotEmpty(logQuery.getLogLevel())){
                TermQuery termQuery = new TermQuery(new Term("logLevel", logQuery.getLogLevel()));
                booleanQueryBuilder.add(termQuery, BooleanClause.Occur.MUST);
            }
            if (ObjectUtil.isNotEmpty(logQuery.getSystem())) {
                TermQuery termQuery = new TermQuery(new Term("system", logQuery.getSystem()));
                booleanQueryBuilder.add(termQuery, BooleanClause.Occur.MUST);
            }
            if (ObjectUtil.isNotEmpty(logQuery.getRequestMethod())) {
                TermQuery termQuery = new TermQuery(new Term("requestMethod", logQuery.getRequestMethod()));
                booleanQueryBuilder.add(termQuery, BooleanClause.Occur.MUST);
            }
            if (Boolean.TRUE.equals(logQuery.getIsRequest())) {
                Term traceid = new Term("traceId");
                TermQuery termQuery = new TermQuery(traceid);
                booleanQueryBuilder.add(termQuery, BooleanClause.Occur.MUST_NOT);
            }
            if (ObjectUtil.isNotEmpty(logQuery.getMessage())){
                //查询解析器
                QueryParser queryParser = new QueryParser("message", new IKAnalyzer(true));
                Query query = queryParser.parse(logQuery.getMessage());
                booleanQueryBuilder.add(query, BooleanClause.Occur.MUST);
            }

            TopFieldCollector collector = TopFieldCollector.create(new Sort(new SortField("time", SortField.Type.LONG,true)), 20000, 0);
            searcher.search(booleanQueryBuilder.build(), collector);
            TopDocs topDocs = collector.topDocs((logQuery.getPage()-1)*logQuery.getSize(), logQuery.getSize());
            int totalSize = collector.getTotalHits();
            ScoreDoc[] scoreDocs = topDocs.scoreDocs;

            List<LogRepositoryDTO> list = new ArrayList<>();
            for (ScoreDoc scoreDoc : scoreDocs) {
                Document doc = open.document(scoreDoc.doc);
                LogRepositoryDTO dto = new LogRepositoryDTO();
                dto.setSystem(doc.get("system"));
                dto.setTraceId(doc.get("traceId"));
                dto.setLogger(doc.get("logger"));
                dto.setLogLevel(doc.get("logLevel"));
                dto.setMessage(doc.get("message"));
                dto.setRequestIp(doc.get("requestIp"));
                dto.setRequestMethod(doc.get("requestMethod"));
                dto.setRequestTime(doc.get("requestTime"));
                dto.setThread(doc.get("thread"));
                list.add(dto);
            }
            open.close();
            directory.close();
            page.addAll(list);
            page.setTotal(scoreDocs.length);
            res.put("total", totalSize);
        }catch (Exception ex){
            ex.printStackTrace();
        }
        res.put("page",page);
        return res;

    }



    @Override
    public void clearLogs(LogQuery query) {
        try {
            IndexWriter indexWriter = LuceneAppender.indexWriter;
            if (indexWriter != null){
                BooleanQuery.Builder booleanQueryBuilder = new BooleanQuery.Builder();
                DateTime offset = DateUtil.offset(new Date(), DateField.HOUR_OF_DAY, -10);
                TermRangeQuery termRangeQuery = new TermRangeQuery(
                        "requestTime",null,
                        new BytesRef(DateUtil.format(offset, "yyyy-MM-dd HH:mm:ss.SSS")), true, true);
                booleanQueryBuilder.add(termRangeQuery,BooleanClause.Occur.MUST);
                indexWriter.deleteDocuments(termRangeQuery);
                indexWriter.commit();
            }
        }catch (Exception ex){
            throw new BadRequestException("删除失败："+ex.getMessage());
        }
    }

    @Override
    public JSONArray getLabelsValues(String type) {
        JSONArray result = new JSONArray();
        for (String v : INFO_LEVEL) {
            JSONObject item = new JSONObject();
            item.put("label", v);
            item.put("value", v);
            result.add(item);
        }
        return result;
    }





    @Override
    public Map<String, Object> getAll(Map whereJson, Pageable page) {
        JSONObject jo = new JSONObject();
        try {
            JSONObject jsonObject = (JSONObject) Searcher.search(indexUrl, "", whereJson);
            JSONArray array = jsonObject.getJSONArray("content");
            int totalElements = Integer.parseInt(jsonObject.get("totalElements").toString());
            jo.put("content", array);
            jo.put("totalElements", totalElements);
        } catch (Exception e) {
            log.error("索引查询为空", e);
            throw new NullPointerException("索引查询为空");
        }
        return jo;
    }

}
