package listing

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"errors"
	"io"
	"net"
	"net/http"
	"strconv"
	"strings"
	"time"

	"hfb_sys/backend/pkg/response"

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

func (h *Handler) ImportExternalUpload(c *gin.Context) {
	c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, maxExternalUploadBodyBytes)
	raw, err := io.ReadAll(c.Request.Body)
	if err != nil {
		response.BadRequest(c, "上传内容过大或读取失败")
		return
	}
	if err := h.verifyExternalUpload(c, raw); err != nil {
		writeExternalUploadAuthError(c, err)
		return
	}
	var req ExternalUploadRequest
	if err := json.Unmarshal(raw, &req); err != nil {
		response.BadRequest(c, "上传 JSON 格式不正确")
		return
	}
	result, err := h.service.ImportExternalUpload(c.Request.Context(), req, ExternalUploadMeta{
		IP:         c.ClientIP(),
		UserAgent:  c.GetHeader("User-Agent"),
		RawPayload: raw,
	})
	if err != nil {
		writeListingError(c, err)
		return
	}
	response.Created(c, result)
}

var (
	errExternalUploadForbiddenIP = errors.New("external upload forbidden ip")
	errExternalUploadTimestamp   = errors.New("external upload timestamp invalid")
	errExternalUploadSignature   = errors.New("external upload signature invalid")
)

const externalUploadMaxClockSkew = 5 * time.Minute

func (h *Handler) verifyExternalUpload(c *gin.Context, raw []byte) error {
	if !externalUploadIPAllowed(c.ClientIP(), h.externalUploadAllowedIPs) {
		return errExternalUploadForbiddenIP
	}
	secret := strings.TrimSpace(h.externalUploadSecret)
	if secret == "" {
		return nil
	}
	timestamp := strings.TrimSpace(c.GetHeader("X-HFB-Timestamp"))
	signature := strings.TrimSpace(c.GetHeader("X-HFB-Signature"))
	if timestamp == "" && signature == "" {
		return nil
	}
	if timestamp == "" || signature == "" {
		return errExternalUploadSignature
	}
	ts, err := strconv.ParseInt(timestamp, 10, 64)
	if err != nil {
		return errExternalUploadTimestamp
	}
	requestTime := time.Unix(ts, 0)
	if time.Since(requestTime) > externalUploadMaxClockSkew || time.Until(requestTime) > externalUploadMaxClockSkew {
		return errExternalUploadTimestamp
	}
	mac := hmac.New(sha256.New, []byte(secret))
	mac.Write([]byte(timestamp))
	mac.Write([]byte("."))
	mac.Write(raw)
	expected := hex.EncodeToString(mac.Sum(nil))
	if !hmac.Equal([]byte(strings.ToLower(signature)), []byte(expected)) {
		return errExternalUploadSignature
	}
	return nil
}

func externalUploadIPAllowed(clientIP string, allowed []string) bool {
	if len(allowed) == 0 {
		return true
	}
	parsedIP := net.ParseIP(clientIP)
	for _, raw := range allowed {
		item := strings.TrimSpace(raw)
		if item == "" {
			continue
		}
		if parsedIP != nil {
			if _, network, err := net.ParseCIDR(item); err == nil && network.Contains(parsedIP) {
				return true
			}
		}
		if item == clientIP {
			return true
		}
	}
	return false
}

func writeExternalUploadAuthError(c *gin.Context, err error) {
	switch err {
	case errExternalUploadForbiddenIP:
		response.Error(c, http.StatusForbidden, "forbidden", "来源 IP 不允许访问")
	case errExternalUploadTimestamp:
		response.Error(c, http.StatusUnauthorized, "invalid_timestamp", "请求时间戳无效")
	default:
		response.Error(c, http.StatusUnauthorized, "invalid_signature", "请求签名无效")
	}
}

func (h *Handler) DefaultUploadScreenshot(c *gin.Context) {
	c.Header("Content-Type", "image/svg+xml; charset=utf-8")
	c.Header("Cache-Control", "public, max-age=86400")
	c.String(http.StatusOK, `<svg xmlns="http://www.w3.org/2000/svg" width="960" height="540" viewBox="0 0 960 540"><rect width="960" height="540" fill="#f3f4f6"/><rect x="72" y="72" width="816" height="396" rx="24" fill="#ffffff" stroke="#d1d5db" stroke-width="4"/><text x="480" y="248" text-anchor="middle" font-family="Arial, sans-serif" font-size="42" font-weight="700" fill="#374151">账号资料默认图</text><text x="480" y="310" text-anchor="middle" font-family="Arial, sans-serif" font-size="28" fill="#6b7280">开放接口上传，待后台补充审核</text></svg>`)
}
