package handlers

import (
	"context"
	"errors"
	"log"
	"net/http"
	"regexp"
	"sync"
	"time"

	"go.mongodb.org/mongo-driver/v2/bson"
	"go.mongodb.org/mongo-driver/v2/mongo"

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

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

const (
	testVerifyCode = "8888"           // 测试验证码（未接入短信时使用）
	codeExpire     = 5 * time.Minute  // 验证码有效期
)

var (
	codeStore   = make(map[string]codeEntry)
	codeStoreMu sync.RWMutex
)

type codeEntry struct {
	Code     string
	ExpireAt time.Time
}

// SendCodeInput 发送验证码请求
type SendCodeInput struct {
	Mobile string `json:"mobile" binding:"required"`
}

// RegisterInput 手机注册请求
type RegisterInput struct {
	Mobile   string `json:"mobile" binding:"required"`
	Code     string `json:"code" binding:"required"`
	Password string `json:"password" binding:"required"`
	Username string `json:"username"` // 可选，默认用手机号
	Email    string `json:"email"`    // 可选
}

// SendCode 发送验证码（测试阶段：任意手机号输入 8888 即可）
func SendCode(c *gin.Context) {
	var input SendCodeInput
	if err := c.ShouldBindJSON(&input); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "请输入手机号"})
		return
	}

	if !isValidMobile(input.Mobile) {
		c.JSON(http.StatusBadRequest, gin.H{"error": "手机号格式不正确"})
		return
	}

	// 未接入短信平台，使用测试验证码 8888
	codeStoreMu.Lock()
	codeStore[input.Mobile] = codeEntry{
		Code:     testVerifyCode,
		ExpireAt: time.Now().Add(codeExpire),
	}
	codeStoreMu.Unlock()

	c.JSON(http.StatusOK, gin.H{
		"message": "验证码已发送（测试环境请输入 8888）",
		"expire":  int(codeExpire.Seconds()),
	})
}

// Register 手机号注册
func Register(c *gin.Context) {
	var input RegisterInput
	if err := c.ShouldBindJSON(&input); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "请填写手机号、验证码和密码"})
		return
	}

	if !isValidMobile(input.Mobile) {
		c.JSON(http.StatusBadRequest, gin.H{"error": "手机号格式不正确"})
		return
	}

	if len(input.Password) < 6 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "密码至少6位"})
		return
	}

	if input.Email != "" && !isValidEmail(input.Email) {
		c.JSON(http.StatusBadRequest, gin.H{"error": "邮箱格式不正确"})
		return
	}

	// 校验验证码
	codeStoreMu.RLock()
	entry, ok := codeStore[input.Mobile]
	codeStoreMu.RUnlock()

	if !ok || entry.Code != input.Code {
		c.JSON(http.StatusBadRequest, gin.H{"error": "验证码错误"})
		return
	}
	if time.Now().After(entry.ExpireAt) {
		codeStoreMu.Lock()
		delete(codeStore, input.Mobile)
		codeStoreMu.Unlock()
		c.JSON(http.StatusBadRequest, gin.H{"error": "验证码已过期，请重新获取"})
		return
	}

	// 删除已用验证码
	codeStoreMu.Lock()
	delete(codeStore, input.Mobile)
	codeStoreMu.Unlock()

	username := input.Username
	if username == "" {
		username = input.Mobile
	}

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	coll := config.GetDB(config.DBName).Collection("users")

	// 检查手机号是否已注册
	var exist models.User
	err := coll.FindOne(ctx, bson.M{"mobile": input.Mobile}).Decode(&exist)
	if err == nil {
		c.JSON(http.StatusConflict, gin.H{"error": "该手机号已注册"})
		return
	}
	if !errors.Is(err, mongo.ErrNoDocuments) {
		log.Printf("[Register] FindOne mobile error: %v", err)
		resp := gin.H{"error": "注册失败，请稍后重试"}
		if gin.Mode() == gin.DebugMode {
			resp["debug"] = err.Error()
		}
		c.JSON(http.StatusInternalServerError, resp)
		return
	}
	err = coll.FindOne(ctx, bson.M{"username": username}).Decode(&exist)
	if err == nil {
		c.JSON(http.StatusConflict, gin.H{"error": "用户名已存在"})
		return
	}
	if !errors.Is(err, mongo.ErrNoDocuments) {
		log.Printf("[Register] FindOne username error: %v", err)
		resp := gin.H{"error": "注册失败，请稍后重试"}
		if gin.Mode() == gin.DebugMode {
			resp["debug"] = err.Error()
		}
		c.JSON(http.StatusInternalServerError, resp)
		return
	}

	// 若提供了邮箱，检查是否已注册
	if input.Email != "" {
		err = coll.FindOne(ctx, bson.M{"email": input.Email}).Decode(&exist)
		if err == nil {
			c.JSON(http.StatusConflict, gin.H{"error": "该邮箱已注册"})
			return
		}
		if !errors.Is(err, mongo.ErrNoDocuments) {
			log.Printf("[Register] FindOne email error: %v", err)
			resp := gin.H{"error": "注册失败，请稍后重试"}
			if gin.Mode() == gin.DebugMode {
				resp["debug"] = err.Error()
			}
			c.JSON(http.StatusInternalServerError, resp)
			return
		}
	}

	// 超级管理员仅一个：第一个注册用户为超级管理员，后续均为普通用户
	count, _ := coll.CountDocuments(ctx, bson.M{})
	roleID := models.RoleIDUser
	role := "user"
	if count == 0 {
		roleID = models.RoleIDSuperAdmin
		role = "admin"
	}

	doc := bson.M{
		"username": username,
		"mobile":   input.Mobile,
		"password": utils.HashPassword(input.Password),
		"role":     role,
		"role_id":  roleID,
	}
	if input.Email != "" {
		doc["email"] = input.Email
	}

	_, err = coll.InsertOne(ctx, doc)
	if err != nil {
		log.Printf("[Register] InsertOne error: %v", err)
		resp := gin.H{"error": "注册失败，请稍后重试"}
		if gin.Mode() == gin.DebugMode {
			resp["debug"] = err.Error()
		}
		c.JSON(http.StatusInternalServerError, resp)
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"message": "注册成功",
	})
}

// ResetPasswordInput 密码找回请求
type ResetPasswordInput struct {
	Mobile      string `json:"mobile" binding:"required"`
	Code        string `json:"code" binding:"required"`
	NewPassword string `json:"new_password" binding:"required"`
}

// ResetPassword 密码找回（手机号+验证码）
func ResetPassword(c *gin.Context) {
	var input ResetPasswordInput
	if err := c.ShouldBindJSON(&input); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "请填写手机号、验证码和新密码"})
		return
	}

	if !isValidMobile(input.Mobile) {
		c.JSON(http.StatusBadRequest, gin.H{"error": "手机号格式不正确"})
		return
	}

	if len(input.NewPassword) < 6 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "新密码至少6位"})
		return
	}

	// 校验验证码
	codeStoreMu.RLock()
	entry, ok := codeStore[input.Mobile]
	codeStoreMu.RUnlock()

	if !ok || entry.Code != input.Code {
		c.JSON(http.StatusBadRequest, gin.H{"error": "验证码错误"})
		return
	}
	if time.Now().After(entry.ExpireAt) {
		codeStoreMu.Lock()
		delete(codeStore, input.Mobile)
		codeStoreMu.Unlock()
		c.JSON(http.StatusBadRequest, gin.H{"error": "验证码已过期，请重新获取"})
		return
	}

	codeStoreMu.Lock()
	delete(codeStore, input.Mobile)
	codeStoreMu.Unlock()

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	coll := config.GetDB(config.DBName).Collection("users")
	var user models.User
	err := coll.FindOne(ctx, bson.M{"mobile": input.Mobile}).Decode(&user)
	if err != nil {
		if errors.Is(err, mongo.ErrNoDocuments) {
			c.JSON(http.StatusNotFound, gin.H{"error": "该手机号未注册"})
			return
		}
		log.Printf("[ResetPassword] FindOne error: %v", err)
		resp := gin.H{"error": "操作失败"}
		if gin.Mode() == gin.DebugMode {
			resp["debug"] = err.Error()
		}
		c.JSON(http.StatusInternalServerError, resp)
		return
	}

	_, err = coll.UpdateOne(ctx, bson.M{"_id": user.ID}, bson.M{"$set": bson.M{"password": utils.HashPassword(input.NewPassword)}})
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "重置失败"})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "密码已重置，请登录"})
}

func isValidMobile(mobile string) bool {
	// 简单校验：11位数字，1开头
	matched, _ := regexp.MatchString(`^1\d{10}$`, mobile)
	return matched
}

func isValidEmail(email string) bool {
	// 简单邮箱格式校验
	matched, _ := regexp.MatchString(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`, email)
	return matched
}
