package api

import (
	"errors"
	"net/http"

	"github.com/gin-gonic/gin"

	"github.com/hhs/camtalk/internal/auth"
	apperr "github.com/hhs/camtalk/internal/errors"
	"github.com/hhs/camtalk/internal/ratelimit"
	"github.com/hhs/camtalk/internal/trace"
)

// AuthHandler 提供认证相关的 REST 端点。
type AuthHandler struct {
	authService auth.Service
	tokenMgr    *auth.TokenManager
}

// NewAuthHandler 创建 AuthHandler。
func NewAuthHandler(authService auth.Service, tokenMgr *auth.TokenManager) *AuthHandler {
	return &AuthHandler{
		authService: authService,
		tokenMgr:    tokenMgr,
	}
}

// RegisterRoutes 注册认证相关路由到给定的路由组。
func (h *AuthHandler) RegisterRoutes(rg *gin.RouterGroup, limiter ratelimit.Limiter) {
	authGroup := rg.Group("/auth")
	{
		// 注册和登录端点添加限流中间件（按 IP 限流）
		if limiter != nil {
			authGroup.POST("/register",
				ratelimit.Middleware(limiter, func(c *gin.Context) string {
					return c.ClientIP() + ":register"
				}),
				h.Register)
			authGroup.POST("/login",
				ratelimit.Middleware(limiter, func(c *gin.Context) string {
					return c.ClientIP() + ":login"
				}),
				h.Login)
		} else {
			authGroup.POST("/register", h.Register)
			authGroup.POST("/login", h.Login)
		}
		// refresh 和 logout 不限流
		authGroup.POST("/refresh", h.Refresh)
		authGroup.POST("/logout", auth.AuthMiddleware(h.tokenMgr), h.Logout)
	}
}

// Register POST /api/auth/register — 用户注册。
func (h *AuthHandler) Register(c *gin.Context) {
	log := trace.FromContext(c.Request.Context())
	clientIP := c.ClientIP()

	var req auth.RegisterRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"code":    apperr.CodeInvalidInput,
			"message": "invalid request body",
		})
		return
	}

	if msg := validateCredentials(req.Username, req.Password); msg != "" {
		c.JSON(http.StatusBadRequest, gin.H{
			"code":    apperr.CodeInvalidInput,
			"message": msg,
		})
		return
	}

	resp, err := h.authService.Register(c.Request.Context(), req)
	if err != nil {
		log.Warnw("register failed",
			"username", req.Username,
			"client_ip", clientIP,
			"error", err)
		handleAuthError(c, err)
		return
	}

	log.Infow("register success",
		"username", req.Username,
		"client_ip", clientIP)
	c.JSON(http.StatusCreated, resp)
}

// Login POST /api/auth/login — 用户登录。
func (h *AuthHandler) Login(c *gin.Context) {
	log := trace.FromContext(c.Request.Context())
	clientIP := c.ClientIP()

	var req auth.LoginRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"code":    apperr.CodeInvalidInput,
			"message": "invalid request body",
		})
		return
	}

	if msg := validateCredentials(req.Username, req.Password); msg != "" {
		c.JSON(http.StatusBadRequest, gin.H{
			"code":    apperr.CodeInvalidInput,
			"message": msg,
		})
		return
	}

	resp, err := h.authService.Login(c.Request.Context(), req)
	if err != nil {
		log.Warnw("login failed",
			"username", req.Username,
			"client_ip", clientIP,
			"error", err)
		handleAuthError(c, err)
		return
	}

	log.Infow("login success",
		"username", req.Username,
		"client_ip", clientIP)
	c.JSON(http.StatusOK, resp)
}

// Refresh POST /api/auth/refresh — 刷新令牌。
func (h *AuthHandler) Refresh(c *gin.Context) {
	log := trace.FromContext(c.Request.Context())

	var req auth.RefreshRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"code":    apperr.CodeInvalidInput,
			"message": "invalid request body",
		})
		return
	}

	if req.RefreshToken == "" {
		c.JSON(http.StatusBadRequest, gin.H{
			"code":    apperr.CodeInvalidInput,
			"message": "refresh_token is required",
		})
		return
	}

	resp, err := h.authService.Refresh(c.Request.Context(), req)
	if err != nil {
		log.Warnw("token refresh failed",
			"client_ip", c.ClientIP(),
			"error", err)
		handleAuthError(c, err)
		return
	}

	log.Infow("token refresh success",
		"client_ip", c.ClientIP())
	c.JSON(http.StatusOK, resp)
}

// Logout POST /api/auth/logout — 登出（需要认证）。
func (h *AuthHandler) Logout(c *gin.Context) {
	log := trace.FromContext(c.Request.Context())
	userID := c.GetString(auth.ContextKeyUserID)

	var req struct {
		RefreshToken string `json:"refresh_token"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"code":    apperr.CodeInvalidInput,
			"message": "invalid request body",
		})
		return
	}

	if req.RefreshToken == "" {
		c.JSON(http.StatusBadRequest, gin.H{
			"code":    apperr.CodeInvalidInput,
			"message": "refresh_token is required",
		})
		return
	}

	if err := h.authService.Logout(c.Request.Context(), userID, req.RefreshToken); err != nil {
		log.Errorw("logout failed",
			"user_id", userID,
			"error", err)
		c.JSON(http.StatusInternalServerError, gin.H{
			"code":    apperr.CodeInternalError,
			"message": "failed to logout",
		})
		return
	}

	log.Infow("logout success",
		"user_id", userID)
	c.JSON(http.StatusOK, gin.H{
		"message": "logged out successfully",
	})
}

// validateCredentials 校验用户名和密码格式。
// 返回空字符串表示校验通过，否则返回错误描述。
func validateCredentials(username, password string) string {
	if len(username) < 3 || len(username) > 64 {
		return "username must be 3-64 characters"
	}
	if len(password) < 8 || len(password) > 72 {
		return "password must be 8-72 characters"
	}
	return ""
}

// handleAuthError 将 auth 层错误映射为 HTTP 响应。
func handleAuthError(c *gin.Context, err error) {
	switch {
	case errors.Is(err, auth.ErrUsernameTaken):
		c.JSON(http.StatusConflict, gin.H{
			"code":    apperr.CodeUsernameTaken,
			"message": "username already taken",
		})
	case errors.Is(err, auth.ErrInvalidCredentials):
		c.JSON(http.StatusUnauthorized, gin.H{
			"code":    apperr.CodeInvalidCredentials,
			"message": "invalid username or password",
		})
	case errors.Is(err, auth.ErrRefreshTokenUsed):
		c.JSON(http.StatusUnauthorized, gin.H{
			"code":    apperr.CodeInvalidToken,
			"message": "refresh token has been used or expired",
		})
	default:
		c.JSON(http.StatusInternalServerError, gin.H{
			"code":    apperr.CodeInternalError,
			"message": "internal server error",
		})
	}
}
