package middleware

import (
	"context"
	"net/http"
	"strings"

	"hfb_sys/backend/internal/modules/auth"
	"hfb_sys/backend/pkg/response"

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

const (
	ContextUserID             = "user_id"
	ContextPhone              = "phone"
	ContextAdminID            = "admin_id"
	ContextUsername           = "username"
	ContextPasswordMustChange = "password_must_change"
	AdminAccessCookieName     = "hfb_admin_access"
)

type AdminTokenContext struct {
	Username           string
	PasswordMustChange bool
}

type AdminTokenValidatorFunc func(ctx context.Context, adminID uint64, tokenVersion int64) (AdminTokenContext, error)

func extractBearerToken(c *gin.Context) string {
	header := c.GetHeader("Authorization")
	tokenText := strings.TrimSpace(strings.TrimPrefix(header, "Bearer "))
	if tokenText != "" && tokenText != header {
		return tokenText
	}
	return ""
}

func extractToken(c *gin.Context) string {
	if tokenText := extractBearerToken(c); tokenText != "" {
		return tokenText
	}
	return c.Query("token")
}

func Auth(jwtManager *auth.JWTManager) gin.HandlerFunc {
	return func(c *gin.Context) {
		tokenText := extractToken(c)
		if tokenText == "" {
			response.Unauthorized(c, "缺少访问令牌")
			c.Abort()
			return
		}

		claims, err := jwtManager.ParseSubject(tokenText, "access", "user")
		if err != nil {
			response.Unauthorized(c, "访问令牌无效或已过期")
			c.Abort()
			return
		}

		c.Set(ContextUserID, claims.UserID)
		c.Set(ContextPhone, claims.Phone)
		c.Next()
	}
}

func AdminAuth(jwtManager *auth.JWTManager, validate AdminTokenValidatorFunc) gin.HandlerFunc {
	return func(c *gin.Context) {
		tokenText := extractBearerToken(c)
		if tokenText == "" {
			if cookieToken, err := c.Cookie(AdminAccessCookieName); err == nil {
				tokenText = strings.TrimSpace(cookieToken)
			}
		}
		if tokenText == "" {
			response.Unauthorized(c, "缺少后台访问令牌")
			c.Abort()
			return
		}

		claims, err := jwtManager.ParseSubject(tokenText, "access", "admin")
		if err != nil {
			response.Unauthorized(c, "后台访问令牌无效或已过期")
			c.Abort()
			return
		}
		username := claims.Phone
		passwordMustChange := false
		if validate != nil {
			tokenContext, err := validate(c.Request.Context(), claims.UserID, claims.TokenVersion)
			if err != nil {
				response.Unauthorized(c, "后台访问令牌无效或已过期")
				c.Abort()
				return
			}
			username = tokenContext.Username
			passwordMustChange = tokenContext.PasswordMustChange
		}

		c.Set(ContextAdminID, claims.UserID)
		c.Set(ContextUsername, username)
		c.Set(ContextPasswordMustChange, passwordMustChange)
		c.Next()
	}
}

func RequireAdminPasswordChanged() gin.HandlerFunc {
	allowed := map[string]bool{
		"/api/admin/me":                      true,
		"/api/admin/auth/logout":             true,
		"/api/admin/admin-users/me/password": true,
	}
	return func(c *gin.Context) {
		value, ok := c.Get(ContextPasswordMustChange)
		if !ok {
			c.Next()
			return
		}
		mustChange, ok := value.(bool)
		if !ok || !mustChange || allowed[c.FullPath()] {
			c.Next()
			return
		}
		response.Error(c, http.StatusForbidden, "password_must_change", "请先修改初始密码")
		c.Abort()
	}
}
