// Copyright (C) 2019 Marius Schellenberger

package index

import (
	"errors"
	"git.giftfish.de/ston1th/docstore/pkg/core"
	"github.com/blevesearch/bleve"
	"github.com/blevesearch/bleve/mapping"
	"html/template"
	"os"
	"strings"
	"time"
)

const maxSearchResult = 101

const docType = "document"

type document struct {
	Text    string    `json:"text"`
	Keyword []string  `json:"keyword"`
	Created time.Time `json:"created"`
}

func (d *document) Type() string {
	return docType
}

type Index struct {
	i bleve.Index
}

func NewIndex(path string) (i *Index, err error) {
	var bi bleve.Index
	if _, err = os.Stat(path); os.IsNotExist(err) {
		var m *mapping.IndexMappingImpl
		m, err = createMapping()
		//m := bleve.NewIndexMapping()
		bi, err = bleve.New(path, m)
		if err != nil {
			err = errors.New("index: " + err.Error())
			return
		}
	} else {
		bi, err = bleve.Open(path)
		if err != nil {
			err = errors.New("index: " + err.Error())
			return
		}
	}
	i = &Index{i: bi}
	return
}

func (i *Index) Close() error {
	return i.i.Close()
}

func (i *Index) Add(text string) (id string, err error) {
	id = newID()
	for {
		doc, _ := i.i.Document(id)
		if doc == nil {
			break
		}
		id = newID()
	}
	k := classify(text)
	err = i.i.Index(id, &document{text, k, time.Now()})
	return
}

func (i *Index) Delete(id string) error {
	return i.i.Delete(id)
}

func (i *Index) Search(search string) (results core.Results, err error) {
	req := bleve.NewSearchRequest(bleve.NewQueryStringQuery(search))
	req.Highlight = bleve.NewHighlightWithStyle("html")
	req.Size = maxSearchResult
	res, err := i.i.Search(req)
	if err != nil {
		return
	}
	for _, hit := range res.Hits {
		if hit == nil {
			continue
		}
		r := core.Result{ID: hit.ID}
		d, err := i.i.Document(hit.ID)
		if d == nil || err != nil {
			continue
		}
		for fragField, frags := range hit.Fragments {
			if fragField == "keyword" {
				var k string
				for _, f := range frags {
					k += f
				}
				r.Keyword = strings.ReplaceAll(strings.ReplaceAll(k, "<mark>", ""), "</mark>", "")
			}
			if fragField == "text" || fragField == "created" {
				for _, f := range frags {
					r.HTML += template.HTML(f)
				}
				break
			}
		}
		results = append(results, r)
	}
	return
}
