package handlers

import (
	"context"
	"errors"
	"net/http"
	"os"
	"strings"
	"time"
	"unicode/utf8"

	"yh_web/server/config"
	"yh_web/server/models"
	"yh_web/server/utils"

	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v5"
	"go.mongodb.org/mongo-driver/v2/bson"
	"go.mongodb.org/mongo-driver/v2/mongo"
)

const siteJWTExpire = 30 * 24 * time.Hour

// SiteClaims 前台 JWT（仅弹幕等轻量场景，勿与后台 Claims 混用）
type SiteClaims struct {
	UserID   string `json:"user_id"`
	Username string `json:"username"`
	jwt.RegisteredClaims
}

func siteJWTSigningKey() []byte {
	s := strings.TrimSpace(os.Getenv("SITE_JWT_SECRET"))
	if s == "" {
		s = "yh_web_site_dm_jwt_change_in_production"
	}
	return []byte(s)
}

// ParseSiteClaims 解析前台 JWT，失败返回 nil,false
func ParseSiteClaims(tokenStr string) (*SiteClaims, bool) {
	tokenStr = strings.TrimSpace(tokenStr)
	if len(tokenStr) > 7 && strings.EqualFold(tokenStr[:7], "bearer ") {
		tokenStr = strings.TrimSpace(tokenStr[7:])
	}
	if tokenStr == "" {
		return nil, false
	}
	var claims SiteClaims
	t, err := jwt.ParseWithClaims(tokenStr, &claims, func(t *jwt.Token) (interface{}, error) {
		return siteJWTSigningKey(), nil
	})
	if err != nil || !t.Valid {
		return nil, false
	}
	return &claims, true
}

// SiteDanmakuTokenValid 弹幕发送权限：有效的前台 JWT
func SiteDanmakuTokenValid(tokenStr string) bool {
	_, ok := ParseSiteClaims(tokenStr)
	return ok
}

// MaskSiteUsernameForDanmaku 弹幕展示半匿名：1 字为「a***」，2 字及以上为前两字 + ***（如 aa***、ab***）
func MaskSiteUsernameForDanmaku(username string) string {
	username = strings.TrimSpace(username)
	if username == "" {
		return "***"
	}
	runes := []rune(username)
	if len(runes) == 1 {
		return string(runes[0]) + "***"
	}
	return string(runes[:2]) + "***"
}

type siteRegisterInput struct {
	Username string `json:"username" binding:"required"`
	Password string `json:"password" binding:"required"`
}

type siteLoginInput struct {
	Username string `json:"username" binding:"required"`
	Password string `json:"password" binding:"required"`
}

func siteUsersColl() *mongo.Collection {
	return config.GetDB(config.DBName).Collection("site_users")
}

// WebSiteRegister POST /api/web/site/register — 仅用于前台直播弹幕账号
func WebSiteRegister(c *gin.Context) {
	if config.MongoClient == nil {
		c.JSON(http.StatusServiceUnavailable, gin.H{"error": "服务暂不可用"})
		return
	}
	var input siteRegisterInput
	if err := c.ShouldBindJSON(&input); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "参数无效"})
		return
	}
	u := strings.TrimSpace(input.Username)
	if utf8.RuneCountInString(u) < 2 || utf8.RuneCountInString(u) > 32 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "用户名为 2～32 个字符"})
		return
	}
	if len(input.Password) < 6 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "密码至少 6 位"})
		return
	}
	ctx, cancel := context.WithTimeout(c.Request.Context(), 10*time.Second)
	defer cancel()
	coll := siteUsersColl()
	var existing models.SiteUser
	err := coll.FindOne(ctx, bson.M{"username": u}).Decode(&existing)
	if err == nil {
		c.JSON(http.StatusConflict, gin.H{"error": "该用户名已被注册"})
		return
	}
	if !errors.Is(err, mongo.ErrNoDocuments) {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "注册失败"})
		return
	}
	doc := models.SiteUser{
		Username:     u,
		PasswordHash: utils.HashPassword(input.Password),
		CreatedAt:    time.Now().UTC(),
	}
	res, err := coll.InsertOne(ctx, doc)
	if err != nil {
		if mongo.IsDuplicateKeyError(err) {
			c.JSON(http.StatusConflict, gin.H{"error": "该用户名已被注册"})
			return
		}
		c.JSON(http.StatusInternalServerError, gin.H{"error": "注册失败"})
		return
	}
	id, _ := res.InsertedID.(bson.ObjectID)
	token, err := issueSiteToken(id.Hex(), u)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "签发登录凭证失败"})
		return
	}
	c.JSON(http.StatusOK, gin.H{"token": token, "username": u})
}

// WebSiteLogin POST /api/web/site/login
func WebSiteLogin(c *gin.Context) {
	if config.MongoClient == nil {
		c.JSON(http.StatusServiceUnavailable, gin.H{"error": "服务暂不可用"})
		return
	}
	var input siteLoginInput
	if err := c.ShouldBindJSON(&input); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "参数无效"})
		return
	}
	u := strings.TrimSpace(input.Username)
	ctx, cancel := context.WithTimeout(c.Request.Context(), 10*time.Second)
	defer cancel()
	var user models.SiteUser
	err := siteUsersColl().FindOne(ctx, bson.M{"username": u}).Decode(&user)
	if err != nil {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "用户名或密码错误"})
		return
	}
	if utils.HashPassword(input.Password) != user.PasswordHash {
		c.JSON(http.StatusUnauthorized, gin.H{"error": "用户名或密码错误"})
		return
	}
	token, err := issueSiteToken(user.ID.Hex(), user.Username)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "签发登录凭证失败"})
		return
	}
	c.JSON(http.StatusOK, gin.H{"token": token, "username": user.Username})
}

func issueSiteToken(userID, username string) (string, error) {
	claims := SiteClaims{
		UserID:   userID,
		Username: username,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(siteJWTExpire)),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			Subject:   userID,
		},
	}
	t := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return t.SignedString(siteJWTSigningKey())
}
