use log::info;
use std::sync::Arc;

use tantivy::schema::{Field, IndexRecordOption, Schema, TextFieldIndexing, TextOptions};
use tantivy::Index;

use crate::analyzer::create_analyzer;
use crate::error::Result;

use super::IndexWriterWrapperImpl;

fn build_text_schema(field_name: &str, tokenizer_name: &str) -> (Schema, Field) {
    let mut schema_builder = Schema::builder();
    // positions is required for matching phase.
    let indexing = TextFieldIndexing::default()
        .set_tokenizer(tokenizer_name)
        .set_fieldnorms(false)
        .set_index_option(IndexRecordOption::WithFreqsAndPositions);
    let option = TextOptions::default().set_indexing_options(indexing);
    let field = schema_builder.add_text_field(field_name, option);
    schema_builder.enable_user_specified_doc_id();
    (schema_builder.build(), field)
}

impl IndexWriterWrapperImpl {
    pub(crate) fn create_text_writer(
        field_name: &str,
        path: &str,
        analyzer_name: &str,
        analyzer_params: &str,
        analyzer_extra_info: &str,
        num_threads: usize,
        overall_memory_budget_in_bytes: usize,
        in_ram: bool,
        enable_background_merge: bool,
    ) -> Result<IndexWriterWrapperImpl> {
        info!(
            "create text index writer, field_name: {}, tantivy_index_version 7, enable_background_merge: {}",
            field_name, enable_background_merge
        );

        let analyzer = create_analyzer(analyzer_params, analyzer_extra_info)?;

        let (schema, field) = build_text_schema(field_name, analyzer_name);
        let index = if in_ram {
            Index::create_in_ram(schema)
        } else {
            Index::create_in_dir(path, schema)?
        };
        index.tokenizers().register(analyzer_name, analyzer);
        let index_writer =
            index.writer_with_num_threads(num_threads, overall_memory_budget_in_bytes)?;
        if !enable_background_merge {
            // Sealed text index builds end with an explicit merge-all in
            // finish(); disable background policy merges for them. Growing
            // segments keep the default policy, otherwise the segment count
            // grows unbounded across periodic commits.
            index_writer.set_merge_policy(Box::new(tantivy::merge_policy::NoMergePolicy));
        }

        Ok(IndexWriterWrapperImpl {
            field,
            index_writer,
            index: Arc::new(index),
            id_field: None,
            enable_user_specified_doc_id: true,
            enable_background_merge,
        })
    }
}
