package main

import (
	"net/http"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

func loggingMiddleware(next http.HandlerFunc) http.HandlerFunc {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if strings.HasPrefix(r.URL.Path, "/static/") || strings.HasPrefix(r.URL.Path, "/favicon") {
			next.ServeHTTP(w, r)
			return
		}

		path := r.URL.Path
		if r.URL.RawQuery != "" {
			path += "?" + r.URL.RawQuery
		}
		log.Debug().
			Str("ip", actualIP(r)).
			Str("path", path).
			Str("user-agent", r.Header.Get("User-Agent")).
			Str("referer", r.Header.Get("Referer")).
			Msg("request")

		next.ServeHTTP(w, r)
	})
}

var (
	queue              = [26]sync.Mutex{}
	concurrentRequests = [26]atomic.Uint32{}
)

func queueMiddleware(next http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if len(r.URL.Path) <= 30 || strings.HasPrefix(r.URL.Path, "/njump/static") {
			next.ServeHTTP(w, r)
			return
		}

		willQueue := false
		for _, prefix := range []string{"/njump", "/nevent1", "/image", "/naddr1", "/npub1", "/nprofile1", "/note1", "/embed"} {
			if strings.HasPrefix(r.URL.Path, prefix) {
				willQueue = true
				break
			}
		}

		if !willQueue {
			next.ServeHTTP(w, r)
			return
		}

		qidx := stupidHash(r.URL.Path) % len(concurrentRequests)
		// add 1
		count := concurrentRequests[qidx].Add(1)
		isFirst := count == 1
		if count > 2 {
			log.Debug().Str("path", r.URL.Path).Uint32("count", count).Int("qidx", qidx).Str("ip", actualIP(r)).
				Msg("too many concurrent requests")
		}

		// lock (or wait for the lock)
		queue[qidx].Lock()

		if isFirst {
			// we are the first requesting this, so we have the duty to reset it to zero later
			defer concurrentRequests[qidx].Store(0)
			defer queue[qidx].Unlock()
			// defer these calls because if there is a panic on ServeHTTP the server will catch it

			next.ServeHTTP(w, r)
			return
		}

		// if we are not the first to request this we will wait for the underlying page to be loaded
		// then we will be redirect to open it again, so hopefully we will hit the cloudflare cache this time
		path := r.URL.Path
		if r.URL.RawQuery != "" {
			path += "?" + r.URL.RawQuery
		}

		queue[qidx].Unlock()

		time.Sleep(time.Millisecond * 90)
		http.Redirect(w, r, path, http.StatusFound)
	}
}

// stupidHash doesn't care very much about collisions
func stupidHash(s string) int {
	return int(s[3] + s[7] + s[18] + s[29])
}
