// WEB 用户接口处理器，负责认证、账户资料、积分查询、头像上传和兑换请求。
package handler

import (
	"errors"
	"io"
	"net/http"
	"strings"

	mediakey "juhe-factory/api/internal/media"
	"juhe-factory/api/internal/model"
	"juhe-factory/api/internal/service"
	"juhe-factory/api/internal/storage"

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

const webUserContextKey = "web_user"

type Web struct {
	service *service.Web
	cos     *storage.COS
}

func NewWeb(service *service.Web, cos *storage.COS) *Web { return &Web{service: service, cos: cos} }

func (h *Web) Login(c *gin.Context) {
	var body struct {
		Account  string `json:"account" binding:"required"`
		Password string `json:"password" binding:"required"`
	}
	if err := c.ShouldBindJSON(&body); err != nil {
		fail(c, http.StatusBadRequest, "invalid_request", "请输入账号和密码")
		return
	}
	pair, err := h.service.Login(body.Account, body.Password)
	if err != nil {
		fail(c, http.StatusUnauthorized, "invalid_credentials", err.Error())
		return
	}
	c.JSON(http.StatusOK, gin.H{"data": pair})
}
func (h *Web) Refresh(c *gin.Context) {
	var body struct {
		RefreshToken string `json:"refresh_token" binding:"required"`
	}
	if err := c.ShouldBindJSON(&body); err != nil {
		fail(c, http.StatusBadRequest, "invalid_request", "缺少刷新令牌")
		return
	}
	pair, err := h.service.Refresh(body.RefreshToken)
	if err != nil {
		fail(c, http.StatusUnauthorized, "invalid_refresh_token", err.Error())
		return
	}
	c.JSON(http.StatusOK, gin.H{"data": pair})
}
func (h *Web) Logout(c *gin.Context) {
	var body struct {
		RefreshToken string `json:"refresh_token"`
	}
	_ = c.ShouldBindJSON(&body)
	if err := h.service.Logout(body.RefreshToken); err != nil {
		fail(c, http.StatusInternalServerError, "logout_failed", "退出失败，请重试")
		return
	}
	c.Status(http.StatusNoContent)
}
func (h *Web) Middleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		raw := strings.TrimSpace(strings.TrimPrefix(c.GetHeader("Authorization"), "Bearer "))
		user, err := h.service.Authenticate(raw)
		if err != nil {
			fail(c, http.StatusUnauthorized, "unauthorized", err.Error())
			c.Abort()
			return
		}
		c.Set(webUserContextKey, user)
		c.Next()
	}
}

// Account 返回当前用户账户资料、积分额度和头像上传限制。
func (h *Web) Account(c *gin.Context) {
	data, err := h.service.Account(currentWebUser(c).ID)
	if err != nil {
		fail(c, http.StatusInternalServerError, "account_failed", "账户信息加载失败")
		return
	}
	if h.cos != nil {
		data["avatar_max_size_bytes"] = h.cos.MaxImageBytes()
	}
	c.JSON(http.StatusOK, gin.H{"data": data})
}
func (h *Web) UpdateProfile(c *gin.Context) {
	var body struct {
		Username string `json:"username" binding:"required"`
	}
	if err := c.ShouldBindJSON(&body); err != nil {
		fail(c, http.StatusBadRequest, "invalid_request", "请输入用户名")
		return
	}
	data, err := h.service.UpdateProfile(currentWebUser(c).ID, body.Username)
	if err != nil {
		if errors.Is(err, service.ErrInvalidUsername) || errors.Is(err, service.ErrUsernameExists) {
			fail(c, http.StatusBadRequest, "profile_invalid", err.Error())
			return
		}
		fail(c, http.StatusInternalServerError, "profile_update_failed", "资料更新失败，请稍后重试")
		return
	}
	c.JSON(http.StatusOK, gin.H{"data": data})
}
func (h *Web) ChangePassword(c *gin.Context) {
	var body struct {
		OriginalPassword string `json:"original_password" binding:"required"`
		NewPassword      string `json:"new_password" binding:"required"`
	}
	if err := c.ShouldBindJSON(&body); err != nil {
		fail(c, http.StatusBadRequest, "invalid_request", "请完整填写密码")
		return
	}
	if err := h.service.ChangePassword(currentWebUser(c).ID, body.OriginalPassword, body.NewPassword); err != nil {
		if errors.Is(err, service.ErrOriginalPassword) || strings.Contains(err.Error(), "密码长度") {
			fail(c, http.StatusBadRequest, "password_invalid", err.Error())
			return
		}
		fail(c, http.StatusInternalServerError, "password_update_failed", "密码修改失败，请稍后重试")
		return
	}
	c.Status(http.StatusNoContent)
}
func (h *Web) UploadAvatar(c *gin.Context) {
	if h.cos == nil {
		fail(c, http.StatusServiceUnavailable, "cos_not_configured", "对象存储未配置")
		return
	}
	file, header, err := c.Request.FormFile("file")
	if err != nil {
		fail(c, http.StatusBadRequest, "invalid_request", "请选择头像图片")
		return
	}
	defer file.Close()
	contentType := strings.TrimSpace(strings.Split(header.Header.Get("Content-Type"), ";")[0])
	extensions := map[string]string{"image/jpeg": ".jpg", "image/png": ".png", "image/webp": ".webp"}
	extension, allowed := extensions[contentType]
	if !allowed || header.Size <= 0 || header.Size > h.cos.MaxImageBytes() {
		fail(c, http.StatusBadRequest, "avatar_invalid", "头像仅支持 JPEG、PNG 或 WebP，且不得超过图片大小限制")
		return
	}
	headerBytes := make([]byte, 512)
	read, readErr := file.Read(headerBytes)
	if readErr != nil && !errors.Is(readErr, io.EOF) {
		fail(c, http.StatusBadRequest, "avatar_invalid", "头像图片内容无法解析")
		return
	}
	if detected := http.DetectContentType(headerBytes[:read]); detected != contentType {
		fail(c, http.StatusBadRequest, "avatar_invalid", "头像图片内容无法解析")
		return
	}
	if _, err := file.Seek(0, io.SeekStart); err != nil {
		fail(c, http.StatusInternalServerError, "avatar_upload_failed", "头像上传失败")
		return
	}
	userID := currentWebUser(c).ID
	oldKey, err := h.service.AvatarKey(userID)
	if err != nil {
		fail(c, http.StatusInternalServerError, "avatar_upload_failed", "头像上传失败")
		return
	}
	key := mediakey.UserAvatar(userID, uuid.New(), extension)
	url, err := h.cos.Put(c.Request.Context(), key, contentType, file, header.Size)
	if err != nil {
		fail(c, http.StatusInternalServerError, "avatar_upload_failed", "头像上传失败")
		return
	}
	if err := h.service.UpdateAvatar(userID, url, key); err != nil {
		_ = h.cos.Delete(c.Request.Context(), key)
		fail(c, http.StatusInternalServerError, "avatar_upload_failed", "头像上传失败")
		return
	}
	if oldKey != "" {
		_ = h.cos.Delete(c.Request.Context(), oldKey)
	}
	c.JSON(http.StatusOK, gin.H{"data": gin.H{"avatar_url": url}})
}
func (h *Web) Usage30Days(c *gin.Context) {
	items, err := h.service.Usage30Days(currentWebUser(c).ID)
	if err != nil {
		fail(c, http.StatusInternalServerError, "usage_failed", "消耗数据加载失败")
		return
	}
	c.JSON(http.StatusOK, gin.H{"data": items})
}

// ConsumptionRecords 按请求页码返回当前用户的积分变动流水。
func (h *Web) ConsumptionRecords(c *gin.Context) {
	page, pageSize := parsePage(c)
	result, err := h.service.ConsumptionRecords(currentWebUser(c).ID, page, pageSize)
	if err != nil {
		fail(c, http.StatusInternalServerError, "consumption_records_failed", "消耗记录加载失败")
		return
	}
	c.JSON(http.StatusOK, gin.H{"data": result})
}
func (h *Web) Redeem(c *gin.Context) {
	var body struct {
		Code string `json:"code" binding:"required"`
	}
	if err := c.ShouldBindJSON(&body); err != nil {
		fail(c, http.StatusBadRequest, "invalid_request", "请输入兑换码")
		return
	}
	result, err := h.service.Redeem(currentWebUser(c).ID, body.Code)
	if err != nil {
		fail(c, http.StatusBadRequest, "redeem_failed", err.Error())
		return
	}
	c.JSON(http.StatusOK, gin.H{"data": result})
}
func currentWebUser(c *gin.Context) *model.WebUser {
	value, _ := c.Get(webUserContextKey)
	user, _ := value.(*model.WebUser)
	return user
}
