package appapi

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

	userv1 "hyapp.local/api/proto/user/v1"
	"hyapp/pkg/giftlimits"
	"hyapp/services/gateway-service/internal/auth"
	"hyapp/services/gateway-service/internal/mediaimage"
	"hyapp/services/gateway-service/internal/transport/http/httpkit"
)

type userAvatarUploadData struct {
	AvatarUploadID string `json:"avatar_upload_id"`
	URL            string `json:"url"`
	SHA256         string `json:"sha256"`
	FrameCount     int32  `json:"frame_count"`
	Animated       bool   `json:"animated"`
	ContentType    string `json:"content_type"`
	Format         string `json:"format"`
	SizeBytes      int64  `json:"size_bytes"`
	Width          int32  `json:"width"`
	Height         int32  `json:"height"`
	DurationMS     int64  `json:"duration_ms"`
	ServerTimeMS   int64  `json:"server_time_ms"`
}

// uploadUserAvatar 是登录用户唯一可信头像上传入口。所有原始字节在 gateway 完成解析和权益授权后
// 直接写入 COS；Content-Type 与后缀都使用实际格式，禁止图片库转码或从客户端文件名推断。
func (h *Handler) uploadUserAvatar(writer http.ResponseWriter, request *http.Request) {
	if h.objectUploader == nil || h.objectURLVerifier == nil || h.userAvatarUploadClient == nil {
		httpkit.WriteError(writer, request, http.StatusServiceUnavailable, httpkit.CodeUpstreamError, "avatar upload is unavailable")
		return
	}
	request.Body = http.MaxBytesReader(writer, request.Body, avatarUploadMaxBytes+uploadMultipartOverheadLimit)
	if err := request.ParseMultipartForm(avatarUploadMaxBytes + uploadMultipartOverheadLimit); err != nil {
		httpkit.WriteError(writer, request, http.StatusRequestEntityTooLarge, "FILE_TOO_LARGE", "file exceeds upload limit")
		return
	}
	if request.MultipartForm != nil {
		defer request.MultipartForm.RemoveAll()
	}
	commandID := strings.TrimSpace(request.FormValue("command_id"))
	if !giftlimits.ValidCommandID(commandID) {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "command_id is invalid")
		return
	}
	file, header, err := request.FormFile("file")
	if err != nil {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "file is required")
		return
	}
	defer file.Close()
	content, err := io.ReadAll(io.LimitReader(file, avatarUploadMaxBytes+1))
	if err != nil || len(content) == 0 {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "file is invalid")
		return
	}
	if int64(len(content)) > avatarUploadMaxBytes {
		httpkit.WriteError(writer, request, http.StatusRequestEntityTooLarge, "FILE_TOO_LARGE", "file exceeds upload limit")
		return
	}
	if header.Size >= 0 && header.Size != int64(len(content)) {
		// multipart 大小、SHA 和 COS ContentLength 必须基于同一份原始字节，截断或额外拼接均拒绝。
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "file size does not match multipart metadata")
		return
	}
	// 文件名和 multipart Content-Type 都可能来自错误的相册缓存元数据；格式、后缀和
	// 对象 Content-Type 只采用原始字节解析结果，避免真实 WebP/GIF 被误报为 JPEG 后拒绝。
	info, _, err := mediaimage.Inspect(content)
	if err != nil {
		httpkit.WriteError(writer, request, http.StatusUnsupportedMediaType, "UNSUPPORTED_MEDIA_TYPE", err.Error())
		return
	}
	media := &userv1.UserAvatarMedia{
		ContentType: info.ContentType, Format: info.Format, SizeBytes: info.SizeBytes, Width: info.Width, Height: info.Height,
		Animated: info.Animated, FrameCount: info.FrameCount, DurationMs: info.DurationMS, Sha256: info.SHA256, Status: "active",
	}
	userID := auth.UserIDFromContext(request.Context())
	authorized, err := h.userAvatarUploadClient.AuthorizeUserAvatarUpload(request.Context(), &userv1.AuthorizeUserAvatarUploadRequest{
		Meta: httpkit.UserMeta(request, ""), UserId: userID, CommandId: commandID, Media: media,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	if authorized == nil || !authorized.GetAllowed() || !giftlimits.ValidCommandID(authorized.GetAvatarUploadId()) || strings.TrimSpace(authorized.GetObjectKey()) == "" {
		httpkit.WriteError(writer, request, http.StatusForbidden, httpkit.CodePermissionDenied, "avatar upload denied")
		return
	}
	objectKey := strings.TrimLeft(strings.TrimSpace(authorized.GetObjectKey()), "/")
	objectURL, err := h.objectUploader.PutObject(request.Context(), objectKey, bytes.NewReader(content), int64(len(content)), info.ContentType)
	if err != nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "object upload failed")
		return
	}
	if !h.objectURLVerifier.OwnsObjectURL(objectURL, objectKey) {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "object upload url verification failed")
		return
	}
	media.Url, media.ObjectKey = objectURL, objectKey
	completed, err := h.userAvatarUploadClient.CompleteUserAvatarUpload(request.Context(), &userv1.CompleteUserAvatarUploadRequest{
		Meta: httpkit.UserMeta(request, ""), UserId: userID,
		AvatarUploadId: authorized.GetAvatarUploadId(), Media: media,
	})
	if err != nil {
		httpkit.WriteRPCError(writer, request, err)
		return
	}
	if completed == nil || !completed.GetActive() || completed.GetAvatarUploadId() != authorized.GetAvatarUploadId() || completed.GetMedia() == nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "avatar upload completion failed")
		return
	}
	result := completed.GetMedia()
	httpkit.WriteOK(writer, request, userAvatarUploadData{
		AvatarUploadID: completed.GetAvatarUploadId(), URL: result.GetUrl(), SHA256: result.GetSha256(),
		FrameCount: result.GetFrameCount(), Animated: result.GetAnimated(), ContentType: result.GetContentType(),
		Format: result.GetFormat(), SizeBytes: result.GetSizeBytes(), Width: result.GetWidth(), Height: result.GetHeight(),
		DurationMS: result.GetDurationMs(), ServerTimeMS: completed.GetServerTimeMs(),
	})
}
