package handlers

import (
	"crypto/rand"
	"encoding/base64"
	"net/http"
	"sync"
	"time"
)

const csrfCookieName = "warpbox_csrf"

type rateLimiter struct {
	mu      sync.Mutex
	records map[string]rateRecord
}

type rateRecord struct {
	StartedAt time.Time
	Count     int
}

func newRateLimiter() *rateLimiter {
	return &rateLimiter{records: make(map[string]rateRecord)}
}

func (l *rateLimiter) Allow(key string, limit int, window time.Duration, now time.Time) bool {
	if limit <= 0 || window <= 0 {
		return true
	}
	l.mu.Lock()
	defer l.mu.Unlock()
	record := l.records[key]
	if record.StartedAt.IsZero() || now.Sub(record.StartedAt) >= window {
		l.records[key] = rateRecord{StartedAt: now, Count: 1}
		return true
	}
	record.Count++
	l.records[key] = record
	return record.Count <= limit
}

func (a *App) csrfToken(w http.ResponseWriter, r *http.Request) string {
	if cookie, err := r.Cookie(csrfCookieName); err == nil && cookie.Value != "" {
		return cookie.Value
	}
	token := randomToken(32)
	http.SetCookie(w, &http.Cookie{
		Name:     csrfCookieName,
		Value:    token,
		Path:     "/",
		HttpOnly: true,
		SameSite: http.SameSiteLaxMode,
		Secure:   r.TLS != nil,
		Expires:  time.Now().Add(12 * time.Hour),
	})
	return token
}

func (a *App) validateCSRF(w http.ResponseWriter, r *http.Request) bool {
	if r.Method == http.MethodGet || r.Method == http.MethodHead || r.Method == http.MethodOptions {
		return true
	}
	cookie, err := r.Cookie(csrfCookieName)
	if err != nil || cookie.Value == "" || r.FormValue("csrf_token") != cookie.Value {
		http.Error(w, "invalid form token", http.StatusForbidden)
		return false
	}
	return true
}

func randomToken(byteCount int) string {
	data := make([]byte, byteCount)
	if _, err := rand.Read(data); err != nil {
		return base64.RawURLEncoding.EncodeToString([]byte(time.Now().String()))
	}
	return base64.RawURLEncoding.EncodeToString(data)
}
