package cn.reghao.tnb.search.app.lucene;

import cn.reghao.tnb.search.api.dto.IndexCount;
import cn.reghao.tnb.search.app.config.AppProperties;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.*;
import org.apache.lucene.search.*;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.springframework.stereotype.Service;
import org.wltea.analyzer.lucene.IKAnalyzer;

import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

/**
 * @author reghao
 * @date 2023-03-02 09:28:58
 */
@Service
public class LuceneIndex {
    private final String nativeLuceneDir;
    private final Analyzer luceneAnalyzer;

    public LuceneIndex(AppProperties appProperties) {
        this.nativeLuceneDir = String.format("%s/%s", appProperties.getBaseDir(), appProperties.getNativeLuceneDirname());
        this.luceneAnalyzer = new IKAnalyzer();
    }

    private IndexWriter getIndexWriter(String indexName) throws IOException {
        String indexDir = String.format("%s/%s", nativeLuceneDir, indexName);
        File dir = new File(indexDir);
        Directory indexDirectory = FSDirectory.open(dir.toPath());
        IndexWriterConfig indexWriterConfig = new IndexWriterConfig(this.luceneAnalyzer);
        return new IndexWriter(indexDirectory, indexWriterConfig);
    }

    private IndexReader getIndexReader(String indexName) throws IOException {
        String indexDir = String.format("%s/%s", nativeLuceneDir, indexName);
        Directory directory = FSDirectory.open(Paths.get(indexDir));
        return DirectoryReader.open(directory);
    }

    private IndexReader getIndexReader(IndexWriter indexWriter) throws IOException {
        return DirectoryReader.open(indexWriter);
    }

    public void createIndex(String indexName, Document document) throws IOException {
        IndexWriter indexWriter = getIndexWriter(indexName);
        indexWriter.addDocument(document);
        indexWriter.commit();
        indexWriter.close();
    }

    public void createIndex(String indexName, List<Document> list) throws IOException {
        IndexWriter indexWriter = getIndexWriter(indexName);
        list.forEach(doc -> {
            try {
                indexWriter.addDocument(doc);
            } catch (IOException e) {
                e.printStackTrace();
            }
        });

        indexWriter.commit();
        indexWriter.close();
    }

    public void updateIndex(String indexName, Document document) throws IOException {
        IndexWriter indexWriter = getIndexWriter(indexName);
        // Lucene 没有提供更新接口, 更新操作实际是先删除后再添加
        String fieldName = "id";
        String fieldValue = document.get("id");
        indexWriter.updateDocument(new Term(fieldName, fieldValue), document);
        indexWriter.commit();
        indexWriter.close();
    }

    public void addOrUpdateIndex(String indexName, Document document) throws IOException {
        IndexWriter indexWriter = getIndexWriter(indexName);
        IndexReader indexReader = getIndexReader(indexWriter);
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);

        String id = document.get("id");
        Query query = new TermQuery(new Term("id", id));
        TopDocs topDocs = indexSearcher.search(query, 1);
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;

        if (scoreDocs.length == 1) {
            String fieldName = "id";
            String fieldValue = document.get("id");
            indexWriter.updateDocument(new Term(fieldName, fieldValue), document);
        } else {
            indexWriter.addDocument(document);
        }
        indexWriter.commit();
        indexWriter.close();
    }

    public void batchAddOrUpdateIndex(String indexName, List<Document> documents) throws IOException {
        IndexWriter indexWriter = getIndexWriter(indexName);
        IndexReader indexReader = getIndexReader(indexWriter);
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);

        List<String> failedList = new ArrayList<>();
        for (Document document : documents) {
            String id = document.get("id");
            try {
                Query query = new TermQuery(new Term("id", id));
                TopDocs topDocs = indexSearcher.search(query, 1);
                ScoreDoc[] scoreDocs = topDocs.scoreDocs;
                if (scoreDocs.length == 1) {
                    String fieldName = "id";
                    String fieldValue = document.get("id");
                    indexWriter.updateDocument(new Term(fieldName, fieldValue), document);
                } else {
                    indexWriter.addDocument(document);
                }
            } catch (Exception e) {
                e.printStackTrace();
                failedList.add(id);
            }
        }
        indexWriter.commit();
        indexWriter.close();
        failedList.size();
    }

    public void deleteIndex(String indexName, String id) throws IOException {
        IndexWriter indexWriter = getIndexWriter(indexName);
        indexWriter.deleteDocuments(new Term("id", id));
        indexWriter.commit();
        indexWriter.close();
    }

    public void deleteAll(String indexName) throws IOException {
        IndexWriter indexWriter = getIndexWriter(indexName);
        indexWriter.deleteAll();
        indexWriter.commit();
        indexWriter.close();
    }

    public IndexCount countIndex(String indexName) throws IOException {
        IndexWriter indexWriter = getIndexWriter(indexName);
        IndexReader indexReader = getIndexReader(indexWriter);

        // 总共的索引文档
        int maxDocs = indexReader.maxDoc();
        // 有效的索引文档
        int numDocs = indexReader.numDocs();
        // 被标记删除的索引文档
        int numDeletedDocs = indexReader.numDeletedDocs();
        indexWriter.close();
        return new IndexCount(maxDocs, numDocs, numDeletedDocs);
    }

    public void restore() {
    }

    /**
     * 彻底删除文档
     *
     * @param
     * @return
     * @date 2025-04-02 11:31:44
     */
    public void forceDelete(String indexName) throws IOException {
        IndexWriter indexWriter = getIndexWriter(indexName);
        indexWriter.forceMergeDeletes();
        indexWriter.close();
    }
}
