// Copyright (C) 2019 Marius Schellenberger

package db

import (
	"errors"
	"git.giftfish.de/ston1th/gowiki/pkg/core"
	"git.giftfish.de/ston1th/gowiki/pkg/render"
	"git.giftfish.de/ston1th/gowiki/pkg/store"
	"sort"
	"strings"
)

const pagePrefix = "page/"

var (
	errPageNotFound      = store.ErrKeyNotFound
	errPageExists        = errors.New("db: page already exists")
	errPermissionDenied  = errors.New("db: permission denied")
	errInvalidPermission = errors.New("db: invalid permission")
	errPrivateWikiPage   = errors.New("db: private wiki pages are not allowed")
	errDeleteIndexPage   = errors.New("db: index page can not be deleted")
)

func (db *DB) GetAllSections(username string, admin bool) (sections core.SectionPages) {
	m := make(map[string]core.UpdatedPages)
	db.store.ForEachPrefix(pagePrefix, func(k string, _ []byte) error {
		if k == core.IndexURI {
			return nil
		}
		p, err := db.getPage(k, username)
		if err == nil {
			s := k[:strings.Index(k, "/")]
			if _, ok := m[s]; !ok {
				m[s] = core.UpdatedPages{}
			}
			m[s] = append(m[s], *p)
		}
		return nil
	})
	if admin {
		secs, err := db.GetSections()
		if err == nil {
			for _, s := range secs {
				if _, ok := m[s.Name]; !ok {
					m[s.Name] = core.UpdatedPages{}
				}
			}
		}
	}
	for s, p := range m {
		sort.Sort(p)
		l := len(p)
		if l > 5 {
			l = 5
		}
		sections = append(sections, core.SectionPage{s, p[:l]})
	}
	sort.Sort(sections)
	return
}

func (db *DB) GetAllSectionPages(section, username string, admin bool) (pages core.Pages, err error) {
	err = db.SectionExists(section)
	if err != nil {
		return
	}
	db.store.ForEachPrefix(pagePrefix, func(k string, _ []byte) error {
		if !strings.HasPrefix(k, section+"/") {
			return nil
		}
		if k == core.IndexURI {
			return nil
		}
		p, err := db.getPage(k, username)
		if err == nil {
			pages = append(pages, *p)
		}
		return nil
	})
	if len(pages) == 0 && !admin {
		err = errPageNotFound
		return
	}
	sort.Sort(pages)
	return
}

func (db *DB) ValidatePage(st, username string) bool {
	p := db.cache.Get(st)
	if p == nil {
		p, err := db.getPage(st, username)
		if err != nil {
			return false
		}
		db.cache.Add(st, p)
		return true
	}
	return db.ReadPerm(st, username, p.Perm)
}

func (db *DB) GetPage(section, title, username string) (p *core.Page, err error) {
	st := render.StoreTitle(section, title)
	p = db.cache.Get(st)
	if p == nil {
		p, err = db.getPage(st, username)
		db.cache.Add(st, p)
		return
	}
	if !db.ReadPerm(section, username, p.Perm) {
		err = errPageNotFound
	}
	return
}

func (db *DB) getPage(title, username string) (p *core.Page, err error) {
	p = new(core.Page)
	err = db.store.Get(pagePrefix+title, p)
	if err != nil {
		return
	}
	if !db.ReadPerm(title, username, p.Perm) {
		err = errPageNotFound
	}
	return
}

func (db *DB) CreatePage(title, section, markdown, username string, p core.Permission) (page *core.Page, err error) {
	if p == core.Invalid {
		err = errInvalidPermission
		return
	}
	if section == core.WikiSection && p == core.Private {
		err = errPrivateWikiPage
		return
	}
	st := render.StoreTitle(section, render.Title(title))
	_, err = db.getPage(st, username)
	if err != store.ErrKeyNotFound {
		if err == nil {
			err = errPageExists
		}
		return
	}
	s, err := db.GetSection(section)
	if err != nil {
		return
	}
	if _, ok := core.Contains(username, s.Members); !ok {
		err = errPermissionDenied
		return
	}

	page = &core.Page{
		Title:      title,
		StoreTitle: st,
		Markdown:   markdown,
		Created:    created(username),
		Perm:       p,
	}
	search := render.Render(page)

	err = db.store.Set(pagePrefix+st, page)
	if err != nil {
		return
	}

	err = db.Index.Add(title, st, search)
	if err != nil {
		return
	}
	db.cache.Add(st, page)
	return
}

func (db *DB) UpdatePage(title, section, markdown, username string, p core.Permission) (err error) {
	if p == core.Invalid {
		err = errInvalidPermission
		return
	}
	if section == core.WikiSection && p == core.Private {
		err = errPrivateWikiPage
		return
	}
	st := render.StoreTitle(section, title)
	page, err := db.getPage(st, username)
	if err != nil {
		return
	}
	if !db.WritePerm(section, username, page.Perm) {
		return errPermissionDenied
	}

	page.Markdown = markdown
	page.Updated = created(username)
	page.Perm = p
	search := render.Render(page)

	err = db.store.Set(pagePrefix+st, page)
	if err != nil {
		return
	}

	err = db.Index.Add(page.Title, st, search)
	if err != nil {
		return
	}
	db.cache.Add(st, page)
	return
}

func (db *DB) deletePage(st string) (err error) {
	if err = db.store.Delete(pagePrefix + st); err != nil {
		return
	}
	db.cache.Delete(st)
	return db.Index.Delete(st)
}

func (db *DB) DeletePage(title, section, username string) (err error) {
	var page *core.Page
	if section == core.WikiSection && title == core.IndexPage {
		err = errDeleteIndexPage
		return
	}
	st := render.StoreTitle(section, title)
	if page, err = db.getPage(st, username); err != nil {
		return
	}
	if !db.WritePerm(section, username, page.Perm) {
		return errPermissionDenied
	}
	return db.deletePage(st)
}
