package bleve

import (
	"fmt"
	"log"
	"os"
	"tercul/internal/domain"

	blevelib "github.com/blevesearch/bleve/v2"
	"gorm.io/gorm"
)

type BleveClient struct {
	index blevelib.Index
}

// NewBleveClient initializes or opens a Bleve index at the given path.
func NewBleveClient(indexPath string) (*BleveClient, error) {
	var index blevelib.Index
	var err error

	if _, err = os.Stat(indexPath); os.IsNotExist(err) {
		// Create a new index if it doesn't exist
		indexMapping := blevelib.NewIndexMapping()
		index, err = blevelib.New(indexPath, indexMapping)
		if err != nil {
			return nil, err
		}
		log.Println("Created a new Bleve index at:", indexPath)
	} else {
		// Open an existing index
		index, err = blevelib.Open(indexPath)
		if err != nil {
			return nil, err
		}
		log.Println("Opened an existing Bleve index at:", indexPath)
	}

	return &BleveClient{index: index}, nil
}

// AddTranslation indexes a single translation into the Bleve index.
func (bc *BleveClient) AddTranslation(translation domain.Translation) error {
	// Create a structured document containing all relevant translation fields
	document := map[string]interface{}{
		"id":                translation.ID,
		"title":             translation.Title,
		"content":           translation.Content,
		"description":       translation.Description,
		"language":          translation.Language,
		"status":            translation.Status,
		"translatable_id":   translation.TranslatableID,
		"translatable_type": translation.TranslatableType,
		"translator_id": func() uint {
			if translation.TranslatorID != nil {
				return *translation.TranslatorID
			}
			return 0
		}(),
	}

	// Use the translation's ID as the unique document ID
	docID := fmt.Sprintf("%d", translation.ID)
	return bc.index.Index(docID, document)
}

// AddTranslations indexes all translations from the database into the Bleve index.
func (bc *BleveClient) AddTranslations(db *gorm.DB) error {
	const batchSize = 50000
	offset := 0

	for {
		// Fetch translations in batches
		var translations []domain.Translation
		err := db.Offset(offset).Limit(batchSize).Find(&translations).Error
		if err != nil {
			log.Printf("Error fetching translations from the database: %v", err)
			return err
		}

		// Break if no more translations to process
		if len(translations) == 0 {
			break
		}

		// Create a Bleve batch for better indexing performance
		batch := bc.index.NewBatch()
		for _, translation := range translations {
			// Create a structured document for each translation
			document := map[string]interface{}{
				"id":                translation.ID,
				"title":             translation.Title,
				"content":           translation.Content,
				"description":       translation.Description,
				"language":          translation.Language,
				"status":            translation.Status,
				"translatable_id":   translation.TranslatableID,
				"translatable_type": translation.TranslatableType,
				"translator_id": func() uint {
					if translation.TranslatorID != nil {
						return *translation.TranslatorID
					}
					return 0
				}(),
			}

			docID := fmt.Sprintf("%d", translation.ID)
			err = batch.Index(docID, document)
			if err != nil {
				log.Printf("Error indexing translation ID %s: %v", docID, err)
			}
		}

		// Commit the batch to the index
		err = bc.index.Batch(batch)
		if err != nil {
			log.Printf("Error committing batch to the Bleve index: %v", err)
			return err
		}

		log.Printf("Indexed %d translations into Bleve.", len(translations))
		offset += batchSize
	}

	log.Println("All translations have been indexed into Bleve.")
	return nil
}

// Search performs a search with multiple filters and a full-text query.
func (bc *BleveClient) Search(queryString string, filters map[string]string, size int) (*blevelib.SearchResult, error) {
	// Create the main query for full-text search
	mainQuery := blevelib.NewMatchQuery(queryString)
	mainQuery.SetFuzziness(2)

	// Create a boolean query
	booleanQuery := blevelib.NewBooleanQuery()

	// Add the main query to the "must" clause
	booleanQuery.AddMust(mainQuery)

	// Add filter queries to the "must" clause
	for field, value := range filters {
		termQuery := blevelib.NewTermQuery(value)
		termQuery.SetField(field)
		booleanQuery.AddMust(termQuery)
	}

	// Build the search request
	searchRequest := blevelib.NewSearchRequest(booleanQuery)
	searchRequest.Size = size

	// Execute the search
	results, err := bc.index.Search(searchRequest)
	if err != nil {
		log.Printf("Search failed: %v", err)
		return nil, err
	}

	return results, nil
}

// Close closes the Bleve index.
func (bc *BleveClient) Close() error {
	return bc.index.Close()
}
