package controller

import (
	"bytes"
	"fmt"
	"image"
	_ "image/gif" // 仅注册 GIF 解码器，不重编码（会丢失动画）
	"image/jpeg"
	"image/png"
	"io"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"metazone.cc/metalab/internal/common"

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

// UploadImage 上传帖子图片（需登录，multipart/form-data）
// JPEG/PNG 自动转 WebP quality 80 存储，保留原尺寸不 resize
func (ctrl *PostController) UploadImage(c *gin.Context) {
	uid, _, ok := common.GetGinUser(c)
	if !ok {
		common.Error(c, http.StatusUnauthorized, "请先登录")
		return
	}

	file, header, err := c.Request.FormFile("file")
	if err != nil {
		common.Error(c, http.StatusBadRequest, "请选择文件")
		return
	}
	defer file.Close()

	// 检查扩展名
	ext := strings.ToLower(filepath.Ext(header.Filename))
	if !allowedImageExt[ext] {
		common.Error(c, http.StatusBadRequest, "仅支持 jpg / jpeg / png / gif / webp 格式")
		return
	}

	// 限制 5MB
	maxSize := int64(5 << 20)
	if header.Size > maxSize {
		common.Error(c, http.StatusBadRequest, "图片大小不能超过 5MB")
		return
	}

	// 读取全部数据（后续 WebP 转换需要）
	data, err := io.ReadAll(file)
	if err != nil {
		common.Error(c, http.StatusInternalServerError, "读取文件失败")
		return
	}

	// 存储路径
	storageDir := "storage/uploads/posts"
	if err := os.MkdirAll(storageDir, 0755); err != nil {
		common.Error(c, http.StatusInternalServerError, "存储初始化失败")
		return
	}

	ts := time.Now().UnixMilli()
	var savePath, url string

	isJPEGPNG := ext == ".jpg" || ext == ".jpeg" || ext == ".png"
	isWebP := ext == ".webp"
	isGIF := ext == ".gif"

	// 强制解码验证文件是否为有效图片（防伪扩展名、图片投毒），验证失败直接拒绝
	// JPEG/PNG：解码后做 WebP 压缩（二分查找质量，目标 ≤ 原文件大小）
	if isJPEGPNG {
		img, _, err := image.Decode(bytes.NewReader(data))
		if err != nil {
			common.Error(c, http.StatusBadRequest, "图片格式无效，请上传有效的图片文件")
			return
		}
		webpData := encodeWebPBinary(img, len(data))
		if webpData != nil && len(webpData) < len(data) {
			filename := fmt.Sprintf("%d_%d.webp", uid, ts)
			savePath = filepath.Join(storageDir, filename)
			if err := os.WriteFile(savePath, webpData, 0644); err == nil {
				url = "/uploads/posts/" + filename
			}
		}
	}

	// GIF：仅解码验证，不转换
	if isGIF {
		if _, _, err := image.Decode(bytes.NewReader(data)); err != nil {
			common.Error(c, http.StatusBadRequest, "图片格式无效，请上传有效的图片文件")
			return
		}
	}

	// WebP：仅解码验证，不重复编码
	if isWebP {
		if _, err := webp.Decode(bytes.NewReader(data)); err != nil {
			common.Error(c, http.StatusBadRequest, "图片格式无效，请上传有效的图片文件")
			return
		}
	}

	// 回退存储：JPEG/PNG 重编码剥离元数据；WebP 已验证直接存；GIF 不重编（丢动画）
	if savePath == "" {
		dataOut := data

		if isJPEGPNG {
			decImg, _, decErr := image.Decode(bytes.NewReader(data))
			if decErr == nil {
				var buf bytes.Buffer
				var encErr error
				if ext == ".png" {
					encErr = png.Encode(&buf, decImg)
				} else {
					encErr = jpeg.Encode(&buf, decImg, &jpeg.Options{Quality: 92})
				}
				if encErr == nil && buf.Len() > 0 {
					dataOut = buf.Bytes()
				}
			}
		}

		filename := fmt.Sprintf("%d_%d%s", uid, ts, ext)
		savePath = filepath.Join(storageDir, filename)
		if err := os.WriteFile(savePath, dataOut, 0644); err != nil {
			common.Error(c, http.StatusInternalServerError, "保存图片失败")
			return
		}
		url = "/uploads/posts/" + filename
	}

	common.VditorUploadOk(c, map[string]string{header.Filename: url})
}
