package main

import (
	"crypto/rand"
	"crypto/subtle"
	"encoding/hex"
	"net/http"
	"strings"
	"time"

	"github.com/labstack/echo/v4"
)

func (a *App) isAdminRequest(c echo.Context) bool {
	header := strings.TrimSpace(c.Request().Header.Get(echo.HeaderAuthorization))
	if header == "" || !strings.HasPrefix(strings.ToLower(header), "bearer ") {
		return false
	}
	token := strings.TrimSpace(header[7:])
	if token == "" {
		return false
	}
	return a.sessions.ValidateToken(token)
}

func (a *App) adminOnly(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) error {
		a.sessions.PurgeExpired()

		header := strings.TrimSpace(c.Request().Header.Get(echo.HeaderAuthorization))
		if header == "" || !strings.HasPrefix(strings.ToLower(header), "bearer ") {
			return writeError(c, http.StatusUnauthorized, "missing admin token")
		}
		token := strings.TrimSpace(header[7:])
		if token == "" || !a.sessions.ValidateToken(token) {
			return writeError(c, http.StatusUnauthorized, "invalid or expired admin token")
		}
		return next(c)
	}
}

func (a *App) verifyAdmin(username, password string) bool {
	u := strings.TrimSpace(username)
	p := strings.TrimSpace(password)
	if u == "" || p == "" {
		return false
	}
	userMatch := subtle.ConstantTimeCompare([]byte(u), []byte(a.cfg.AdminUser)) == 1
	passMatch := subtle.ConstantTimeCompare([]byte(p), []byte(a.cfg.AdminPass)) == 1
	return userMatch && passMatch
}

func (s *SessionStore) CreateToken() (string, time.Time, error) {
	raw := make([]byte, 32)
	if _, err := rand.Read(raw); err != nil {
		return "", time.Time{}, err
	}
	token := hex.EncodeToString(raw)
	expires := time.Now().UTC().Add(s.duration)

	s.mu.Lock()
	s.tokens[token] = expires
	s.mu.Unlock()

	return token, expires, nil
}

func (s *SessionStore) ValidateToken(token string) bool {
	s.mu.RLock()
	expires, ok := s.tokens[token]
	s.mu.RUnlock()
	if !ok {
		return false
	}
	return time.Now().UTC().Before(expires)
}

func (s *SessionStore) DeleteToken(token string) {
	s.mu.Lock()
	delete(s.tokens, token)
	s.mu.Unlock()
}

func (s *SessionStore) PurgeExpired() {
	now := time.Now().UTC()
	s.mu.Lock()
	for token, expiry := range s.tokens {
		if now.After(expiry) {
			delete(s.tokens, token)
		}
	}
	s.mu.Unlock()
}
