package middleware

import (
	"net/http"
	"strings"

	"metazone.cc/metalab/internal/common"
	"metazone.cc/metalab/internal/config"
	"metazone.cc/metalab/internal/model"
	"metazone.cc/metalab/internal/session"

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

// autoCheckIner AuthMiddleware 对自动签到服务的最小依赖
type autoCheckIner interface {
	CheckIn(userID uint) (checkedIn bool, newExp int, levelUp bool, err error)
}

// AuthMiddleware 认证中间件（结构体模式，持有 SessionManager）
type AuthMiddleware struct {
	cfg            *config.Config
	sessionManager *session.Manager
	levelSvc       autoCheckIner
}

// NewAuthMiddleware 构造函数
func NewAuthMiddleware(cfg *config.Config, sm *session.Manager) *AuthMiddleware {
	return &AuthMiddleware{cfg: cfg, sessionManager: sm}
}

// WithLevelService 链式注入等级服务（用于自动签到）
func (am *AuthMiddleware) WithLevelService(svc autoCheckIner) *AuthMiddleware {
	am.levelSvc = svc
	return am
}

// Required 登录认证中间件：读 session cookie → 验证会话 → 注入用户信息
// 页面请求（非 /api/ 前缀）→ 验证失败清除 cookie 并重定向到登录页
// API 请求 → 验证失败返回 401 JSON
func (am *AuthMiddleware) Required() gin.HandlerFunc {
	return func(c *gin.Context) {
		sid, err := c.Cookie(common.SessionCookieName)
		if err != nil || sid == "" {
			am.abortUnauthorized(c)
			return
		}

		s, err := am.sessionManager.Validate(sid)
		if err != nil || s == nil {
			am.abortUnauthorized(c)
			return
		}

		injectSessionContext(c, s)
		am.tryAutoCheckIn(c, s)
		c.Next()
	}
}

// abortUnauthorized 统一处理未认证：API 返回 JSON，页面请求跳转登录
func (am *AuthMiddleware) abortUnauthorized(c *gin.Context) {
	common.ClearSessionCookie(c, am.cfg)
	if strings.HasPrefix(c.Request.URL.Path, "/api/") {
		c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
			"success": false, "message": "登录已过期，请重新登录",
		})
	} else {
		common.RedirectToLogin(c)
	}
}

// Optional 可选认证：已登录则注入用户信息，未登录也放行
func (am *AuthMiddleware) Optional() gin.HandlerFunc {
	return func(c *gin.Context) {
		sid, err := c.Cookie(common.SessionCookieName)
		if err != nil || sid == "" {
			c.Next()
			return
		}

		s, err := am.sessionManager.Validate(sid)
		if err != nil || s == nil {
			common.ClearSessionCookie(c, am.cfg)
			c.Next()
			return
		}

		injectSessionContext(c, s)
		am.tryAutoCheckIn(c, s)
		c.Next()
	}
}

// tryAutoCheckIn 尝试自动签到，并同步 session Exp 与 DB（静默失败，不影响主流程）
// CheckIn 即使已签到也会返回 DB 中最新的 Exp，这里始终与 session 比较，
// 确保 CompleteTask 等非签到路径的 Exp 变化也能同步到 session，避免 NAV 显示旧等级
// 被封禁用户跳过自动签到
func (am *AuthMiddleware) tryAutoCheckIn(c *gin.Context, s *session.Session) {
	if am.levelSvc == nil || s == nil {
		return
	}
	if s.Status == model.StatusBanned {
		return
	}
	_, newExp, _, _ := am.levelSvc.CheckIn(s.UserID)
	// 只允许 Exp 只增不减：DB 异常返回 0 或主从延迟读到旧值时不会回退
	if newExp > s.Exp {
		s.Exp = newExp
		_ = am.sessionManager.UpdateSession(s)
		c.Set("exp", newExp)
	}
}

// injectSessionContext 将会话中的用户信息注入 gin context
func injectSessionContext(c *gin.Context, s *session.Session) {
	if s == nil {
		return
	}
	c.Set("uid", s.UserID)
	c.Set("email", s.Email)
	c.Set("username", s.Username)
	c.Set("avatar", s.Avatar)
	c.Set("role", s.Role)
	c.Set("status", s.Status)
	c.Set("exp", s.Exp)
	c.Set("sid", s.ID)
}

// BannedWriteGuard 封禁用户写操作守卫
// 仅拦截 POST/PUT/DELETE/PATCH 请求，GET/HEAD/OPTIONS 放行
// 需在 Required() 之后调用，确保上下文中已有用户 status
func (am *AuthMiddleware) BannedWriteGuard() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 只检查写操作
		switch c.Request.Method {
		case "GET", "HEAD", "OPTIONS":
			c.Next()
			return
		}

		status, _ := c.Get("status")
		if status == model.StatusBanned {
			c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
				"success": false,
				"message": "账号已被封禁，无法执行此操作",
			})
			return
		}
		c.Next()
	}
}
