package api

import (
	"net/http"
	"strings"

	"github.com/go-chi/chi/v5"
)

type HostBucketRouter struct {
	routes        map[string]chi.Router
	bktParam      string
	defaultRouter chi.Router
}

func NewHostBucketRouter(bktParam string) HostBucketRouter {
	return HostBucketRouter{
		routes:   make(map[string]chi.Router),
		bktParam: bktParam,
	}
}

func (hr *HostBucketRouter) Default(router chi.Router) {
	hr.defaultRouter = router
}

func (hr HostBucketRouter) Map(host string, h chi.Router) {
	hr.routes[strings.ToLower(host)] = h
}

func (hr HostBucketRouter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	bucket, domain := getBucketDomain(getHost(r))
	router, ok := hr.routes[strings.ToLower(domain)]
	if !ok {
		router = hr.defaultRouter
		if router == nil {
			http.Error(w, http.StatusText(404), 404)
			return
		}
	}

	if rctx := chi.RouteContext(r.Context()); rctx != nil && bucket != "" {
		rctx.URLParams.Add(hr.bktParam, bucket)
	}

	router.ServeHTTP(w, r)
}

func getBucketDomain(host string) (bucket string, domain string) {
	parts := strings.Split(host, ".")
	if len(parts) > 1 {
		return parts[0], strings.Join(parts[1:], ".")
	}
	return "", host
}

// getHost tries its best to return the request host.
// According to section 14.23 of RFC 2616 the Host header
// can include the port number if the default value of 80 is not used.
func getHost(r *http.Request) string {
	host := r.Host
	if r.URL.IsAbs() {
		host = r.URL.Host
	}

	if i := strings.Index(host, ":"); i != -1 {
		host = host[:i]
	}

	return host
}