// Copyright (C) 2019 Marius Schellenberger

package index

import (
	"errors"
	"git.giftfish.de/ston1th/godrop/v2"
	"git.giftfish.de/ston1th/gowiki/pkg/core"
	"git.giftfish.de/ston1th/gowiki/pkg/render"
	"github.com/blevesearch/bleve"
	"github.com/blevesearch/bleve/mapping"
	"html/template"
	"os"
)

const (
	maxSearchResult = 101
	docType         = "page"
	titleField      = "title"
	storeTitleField = "store_title"
	searchField     = "search"
)

type indexPage struct {
	Title      string `json:"title"`
	StoreTitle string `json:"store_title"`
	Search     string `json:"search"`
}

func (*indexPage) Type() string {
	return docType
}

type Index struct {
	i bleve.Index
}

func New(path string) (i *Index, err error) {
	var bi bleve.Index
	err = godrop.Unveil(path, "rwc")
	if err != nil {
		return
	}
	if _, err = os.Stat(path); os.IsNotExist(err) {
		err = os.Mkdir(path, 0700)
		if err != nil {
			err = errors.New("index: " + err.Error())
			return
		}
		err = godrop.Unveil(path, "rwc")
		if err != nil {
			return
		}
		var m *mapping.IndexMappingImpl
		m, err = createMapping()
		if err != nil {
			err = errors.New("mapping: " + err.Error())
			return
		}
		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(title, storeTitle, search string) error {
	doc, _ := i.i.Document(storeTitle)
	if doc != nil {
		err := i.Delete(storeTitle)
		if err != nil {
			return err
		}
	}
	return i.i.Index(storeTitle, &indexPage{title, storeTitle, search})
}

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

func (i *Index) Search(search string) (results []core.Result, 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{StoreTitle: hit.ID}
		r.Section, r.Title = render.SplitStoreTitle(hit.ID)
		r.Title = render.Untitle(r.Title)
		d, err := i.i.Document(hit.ID)
		if d == nil || err != nil {
			continue
		}
		for fragField, frags := range hit.Fragments {
			if fragField == titleField || fragField == searchField {
				for _, f := range frags {
					r.HTML += template.HTML(f)
				}
				break
			}
		}
		results = append(results, r)
	}
	return
}
