package routes

import (
	"net/http"
	"net/url"
	"regexp"
	"time"

	"github.com/gorilla/mux"
	"github.com/patrickmn/go-cache"
	"sour.is/x/toolbox/httpsrv"
	"sour.is/x/toolbox/uuid"
)

func init() {
	s := NewShortManager(365 * 24 * time.Hour)

	httpsrv.HttpRegister("short", httpsrv.HttpRoutes{
		{Name: "getShort", Method: "GET", Pattern: "/s/{id}", HandlerFunc: s.getShort},
		{Name: "putShort", Method: "PUT", Pattern: "/s/{id}", HandlerFunc: s.putShort},
	})
}

type shortManager struct {
	defaultExpire time.Duration
	db            *cache.Cache
}

type shortURL struct {
	ID     string
	URL    string
	Secret string
}

func NewShortManager(defaultExpire time.Duration) *shortManager {
	return &shortManager{
		defaultExpire: defaultExpire,
		db:            cache.New(defaultExpire, defaultExpire/10),
	}
}

func (s *shortManager) GetURL(id string) *shortURL {
	if u, ok := s.db.Get(id); ok {
		if url, ok := u.(*shortURL); ok {
			return url
		}
	}

	return nil
}
func (s *shortManager) PutURL(id string, url *shortURL) {
	s.db.SetDefault(id, url)
}

func (s *shortManager) getShort(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)

	id := vars["id"]
	url := s.GetURL(id)

	if url == nil {
		httpsrv.WriteError(w, 404, "not found")
		return
	}

	w.Header().Set("Location", url.URL)
	w.WriteHeader(http.StatusFound)
}

func (s *shortManager) putShort(w http.ResponseWriter, r *http.Request) {
	defer r.Body.Close()

	vars := mux.Vars(r)

	id := vars["id"]
	secret := r.FormValue("secret")
	u, err := url.Parse(r.FormValue("url"))
	if err != nil {
		httpsrv.WriteError(w, 400, "bad url")
		return
	}

	short := s.GetURL(id)

	if short == nil {
		short = newshort(id, secret, u.String())

		s.PutURL(short.ID, short)
		httpsrv.WriteObject(w, 200, short)

		return
	}

	if secret == "" {
		httpsrv.WriteError(w, 401, "no auth")
		return
	}

	if secret != short.Secret {
		httpsrv.WriteError(w, 403, "forbidden")
		return
	}

	short.URL = u.String()

	s.PutURL(short.ID, short)
	httpsrv.WriteObject(w, 200, short)
}

func newshort(id, secret, u string) *shortURL {
	m, err := regexp.MatchString("[a-z-]{1,64}", id)
	if id == "" || !m || err != nil {
		id = uuid.V4()
	}
	m, err = regexp.MatchString("[a-z-]{1,64}", secret)
	if secret == "" || !m || err != nil {
		secret = uuid.V4()
	}
	return &shortURL{ID: id, Secret: secret, URL: u}
}
