package server

import (
	"crypto/sha256"
	"crypto/subtle"
	"encoding/hex"
	"net/http"
	"strings"

	"github.com/gin-gonic/gin"
)

// authCookieName 保存已通过授权校验的凭证。值为授权码的 SHA-256 十六进制摘要，
// 不落明文；HttpOnly 使前端 JS 无法读取，降低泄露面。
const authCookieName = "blackbean_auth"

// authCookieValue 计算授权码的稳定凭证值（SHA-256 摘要）。
func authCookieValue(code string) string {
	sum := sha256.Sum256([]byte(code))
	return hex.EncodeToString(sum[:])
}

// authRequired 是访问授权中间件：cfg.AuthCode 为空（未启用授权）时直接放行；
// 否则要求请求携带与授权码匹配的 Cookie，不匹配则 401 并中止后续处理。
func (s *Server) authRequired(c *gin.Context) {
	if s.cfg.AuthCode == "" {
		c.Next()
		return
	}
	if cookie, err := c.Cookie(authCookieName); err == nil && cookie == authCookieValue(s.cfg.AuthCode) {
		c.Next()
		return
	}
	fail(c, http.StatusUnauthorized, "需要授权码才能访问，请先在首页输入授权码")
	c.Abort()
}

// authStatus 返回授权状态：required=是否启用了授权，authorized=当前请求是否已通过授权。
// 该接口始终放行（不挂授权中间件），前端据此决定是否展示授权界面。
func (s *Server) authStatus(c *gin.Context) {
	required := s.cfg.AuthCode != ""
	authorized := false
	if required {
		if cookie, err := c.Cookie(authCookieName); err == nil {
			authorized = cookie == authCookieValue(s.cfg.AuthCode)
		}
	}
	ok(c, gin.H{"required": required, "authorized": authorized})
}

// authLogin 校验授权码并签发 Cookie。
// 该接口始终放行（不挂授权中间件）；未启用授权时返回 400。
func (s *Server) authLogin(c *gin.Context) {
	if s.cfg.AuthCode == "" {
		fail(c, http.StatusBadRequest, "本实例未启用访问授权")
		return
	}
	var req struct {
		Code string `json:"code"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		fail(c, http.StatusBadRequest, "请求体格式错误")
		return
	}
	// 常量时间比较，避免通过响应时差探测授权码
	want := []byte(s.cfg.AuthCode)
	got := []byte(strings.TrimSpace(req.Code))
	if len(want) != len(got) || subtle.ConstantTimeCompare(want, got) != 1 {
		fail(c, http.StatusUnauthorized, "授权码错误")
		return
	}
	http.SetCookie(c.Writer, &http.Cookie{
		Name:     authCookieName,
		Value:    authCookieValue(s.cfg.AuthCode),
		Path:     "/",
		HttpOnly: true,
		SameSite: http.SameSiteLaxMode,
	})
	ok(c, gin.H{"authorized": true})
}
