package handlers

import (
	"net/http"
	"strings"

	"github.com/gin-gonic/gin"
	"golang.org/x/crypto/bcrypt"

	"sproutgate-backend/internal/models"
)

func (h *Handler) ListUsers(c *gin.Context) {
	users, err := h.store.ListUsers()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to load users"})
		return
	}
	publicUsers := make([]models.UserPublic, 0, len(users))
	for _, u := range users {
		publicUsers = append(publicUsers, u.OwnerPublic())
	}
	c.JSON(http.StatusOK, gin.H{"total": len(publicUsers), "users": publicUsers})
}

func (h *Handler) GetPublicUser(c *gin.Context) {
	account := strings.TrimSpace(c.Param("account"))
	if account == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "account is required"})
		return
	}
	users, err := h.store.ListUsers()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to load users"})
		return
	}
	for _, user := range users {
		if strings.EqualFold(strings.TrimSpace(user.Account), account) {
			if user.Banned {
				c.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
				return
			}
			c.JSON(http.StatusOK, gin.H{"user": user.PublicProfile()})
			return
		}
	}
	c.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
}

func (h *Handler) CreateUser(c *gin.Context) {
	var req createUserRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"})
		return
	}
	req.Account = strings.TrimSpace(req.Account)
	if req.Account == "" || strings.TrimSpace(req.Password) == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "account and password are required"})
		return
	}
	hash, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to hash password"})
		return
	}
	wu, err := normalizePublicWebsiteURL(req.WebsiteURL)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	record := models.UserRecord{
		Account:         req.Account,
		PasswordHash:    string(hash),
		Username:        req.Username,
		Email:           req.Email,
		Level:           req.Level,
		SproutCoins:     req.SproutCoins,
		SecondaryEmails: req.SecondaryEmails,
		Phone:           req.Phone,
		AvatarURL:       req.AvatarURL,
		WebsiteURL:      wu,
		Bio:             req.Bio,
		CreatedAt:       models.NowISO(),
		UpdatedAt:       models.NowISO(),
	}
	if err := h.store.CreateUser(record); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	c.JSON(http.StatusCreated, gin.H{"user": record.OwnerPublic()})
}

func (h *Handler) UpdateUser(c *gin.Context) {
	account := strings.TrimSpace(c.Param("account"))
	if account == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "account is required"})
		return
	}
	var req updateUserRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request"})
		return
	}
	user, found, err := h.store.GetUser(account)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to load user"})
		return
	}
	if !found {
		c.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
		return
	}
	if req.Password != nil && strings.TrimSpace(*req.Password) != "" {
		hash, err := bcrypt.GenerateFromPassword([]byte(*req.Password), bcrypt.DefaultCost)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to hash password"})
			return
		}
		user.PasswordHash = string(hash)
	}
	if req.Username != nil {
		user.Username = *req.Username
	}
	if req.Email != nil {
		user.Email = *req.Email
	}
	if req.Level != nil {
		user.Level = *req.Level
	}
	if req.SproutCoins != nil {
		user.SproutCoins = *req.SproutCoins
	}
	if req.SecondaryEmails != nil {
		user.SecondaryEmails = *req.SecondaryEmails
	}
	if req.Phone != nil {
		user.Phone = *req.Phone
	}
	if req.AvatarURL != nil {
		user.AvatarURL = *req.AvatarURL
	}
	if req.WebsiteURL != nil {
		wu, err := normalizePublicWebsiteURL(*req.WebsiteURL)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}
		user.WebsiteURL = wu
	}
	if req.Bio != nil {
		user.Bio = *req.Bio
	}
	if req.Banned != nil {
		user.Banned = *req.Banned
		if !user.Banned {
			user.BanReason = ""
			user.BannedAt = ""
		} else if strings.TrimSpace(user.BannedAt) == "" {
			user.BannedAt = models.NowISO()
		}
	}
	if req.BanReason != nil {
		r := strings.TrimSpace(*req.BanReason)
		if len(r) > maxBanReasonLen {
			c.JSON(http.StatusBadRequest, gin.H{"error": "ban reason is too long"})
			return
		}
		if r != "" && !user.Banned {
			c.JSON(http.StatusBadRequest, gin.H{"error": "cannot set ban reason while user is not banned"})
			return
		}
		user.BanReason = r
	}
	if err := h.store.SaveUser(user); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to save user"})
		return
	}
	c.JSON(http.StatusOK, gin.H{"user": user.OwnerPublic()})
}

func (h *Handler) DeleteUser(c *gin.Context) {
	account := strings.TrimSpace(c.Param("account"))
	if account == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "account is required"})
		return
	}
	if err := h.store.DeleteUser(account); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to delete user"})
		return
	}
	c.JSON(http.StatusOK, gin.H{"deleted": true})
}

func (h *Handler) AdminMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		token := adminTokenFromRequest(c)
		if token == "" || token != h.store.AdminToken() {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid admin token"})
			c.Abort()
			return
		}
		c.Next()
	}
}
