package util

import (
	"crypto/rand"
	"encoding/hex"
	"github.com/bouncepaw/mycorrhiza/files"
	"log"
	"net/http"
	"regexp"
	"strings"

	"github.com/bouncepaw/mycomarkup/util"
	"github.com/bouncepaw/mycorrhiza/cfg"
)

// PrepareRq strips the trailing / in rq.URL.Path. In the future it might do more stuff for making all request structs uniform.
func PrepareRq(rq *http.Request) {
	rq.URL.Path = strings.TrimSuffix(rq.URL.Path, "/")
}

// ShorterPath is used by handlerList to display shorter path to the files. It
// simply strips the hyphae directory name.
func ShorterPath(path string) string {
	if strings.HasPrefix(path, files.HyphaeDir()) {
		tmp := strings.TrimPrefix(path, files.HyphaeDir())
		if tmp == "" {
			return ""
		}
		return tmp[1:]
	}
	return path
}

// HTTP404Page writes a 404 error in the status, needed when no content is found on the page.
func HTTP404Page(w http.ResponseWriter, page string) {
	w.Header().Set("Content-Type", "text/html;charset=utf-8")
	w.WriteHeader(http.StatusNotFound)
	_, _ = w.Write([]byte(page))
}

// HTTP200Page wraps some frequently used things for successful 200 responses.
func HTTP200Page(w http.ResponseWriter, page string) {
	w.Header().Set("Content-Type", "text/html;charset=utf-8")
	w.WriteHeader(http.StatusOK)
	_, _ = w.Write([]byte(page))
}

// RandomString generates a random string of the given length. It is cryptographically secure to some extent.
func RandomString(n int) (string, error) {
	bytes := make([]byte, n)
	if _, err := rand.Read(bytes); err != nil {
		return "", err
	}
	return hex.EncodeToString(bytes), nil
}

// BeautifulName makes the ugly name beautiful by replacing _ with spaces and using title case.
func BeautifulName(uglyName string) string {
	// Why not reuse
	return util.BeautifulName(uglyName)
}

// CanonicalName makes sure the `name` is canonical. A name is canonical if it is lowercase and all spaces are replaced with underscores.
func CanonicalName(name string) string {
	return util.CanonicalName(name)
}

// hyphaPattern is a pattern which all hypha names must match.
var hyphaPattern = regexp.MustCompile(`[^?!:#@><*|"'&%{}]+`)

var usernamePattern = regexp.MustCompile(`[^?!:#@><*|"'&%{}/]+`)

// IsCanonicalName checks if the `name` is canonical.
func IsCanonicalName(name string) bool {
	return hyphaPattern.MatchString(name)
}

// IsPossibleUsername is true if the given username is ok. Same as IsCanonicalName, but cannot have / in it and cannot be equal to "anon" or "wikimind"
func IsPossibleUsername(username string) bool {
	return username != "anon" && username != "wikimind" && usernameIsWhiteListed(username) && usernamePattern.MatchString(strings.TrimSpace(username))
}

func usernameIsWhiteListed(username string) bool {
	if !cfg.UseWhiteList {
		return true
	}
	for _, allowedUsername := range cfg.WhiteList {
		if allowedUsername == username {
			return true
		}
	}
	return false
}

// HyphaNameFromRq extracts hypha name from http request. You have to also pass the action which is embedded in the url or several actions. For url /hypha/hypha, the action would be "hypha".
func HyphaNameFromRq(rq *http.Request, actions ...string) string {
	p := rq.URL.Path
	for _, action := range actions {
		if strings.HasPrefix(p, "/"+action+"/") {
			return CanonicalName(strings.TrimPrefix(p, "/"+action+"/"))
		}
	}
	log.Println("HyphaNameFromRq: this request is invalid, fall back to home hypha")
	return cfg.HomeHypha
}

// FormData is a convenient struct for passing user input and errors to HTML
// forms and showing to the user.
type FormData struct {
	err    error
	fields map[string]string
}

func NewFormData() FormData {
	return FormData{
		err:    nil,
		fields: map[string]string{},
	}
}

func FormDataFromRequest(r *http.Request, keys []string) FormData {
	formData := NewFormData()
	for _, key := range keys {
		formData.Put(key, r.FormValue(key))
	}
	return formData
}

func (f FormData) HasError() bool {
	return f.err != nil
}

func (f FormData) Error() string {
	if f.err == nil {
		return ""
	}
	return f.err.Error()
}

func (f FormData) WithError(err error) FormData {
	f.err = err
	return f
}

func (f FormData) Get(key string) string {
	return f.fields[key]
}

func (f FormData) Put(key, value string) {
	f.fields[key] = value
}