package appapi

import (
	"io"
	"net/http"
	"net/url"
	"path"
	"strconv"
	"strings"
	"time"

	"hyapp/services/gateway-service/internal/transport/http/httpkit"
)

const effectMediaProxyMaxBytes int64 = 30 << 20

var (
	effectMediaProxyHTTPClient = &http.Client{Timeout: 15 * time.Second}
	effectMediaProxyHosts      = map[string]struct{}{
		"media.haiyihy.com": {},
	}
	effectMediaProxyExtensions = map[string]string{
		".mp4":  "video/mp4",
		".pag":  "application/octet-stream",
		".svga": "application/octet-stream",
	}
)

// proxyEffectMedia streams trusted binary animation assets through gateway so
// browser-only SVGA/PAG runtimes can read them even when the media CDN has no CORS.
func (h *Handler) proxyEffectMedia(writer http.ResponseWriter, request *http.Request) {
	rawURL := strings.TrimSpace(request.URL.Query().Get("url"))
	sourceURL, contentType, ok := validateEffectMediaProxyURL(rawURL)
	if !ok {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}

	upstreamRequest, err := http.NewRequestWithContext(request.Context(), http.MethodGet, sourceURL.String(), nil)
	if err != nil {
		httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}
	upstreamRequest.Header.Set("Accept", "application/octet-stream,*/*")
	upstreamRequest.Header.Set("User-Agent", "hyapp-gateway-effect-media-proxy/1.0")
	// 透明 MP4 的浏览器播放器需要 seek 多个采样帧来拆分 RGB/Alpha 通道；透传 Range
	// 才能让 video 元素按需取片段，而不是每次检测都重新下载整段动效。
	if value := strings.TrimSpace(request.Header.Get("Range")); value != "" {
		upstreamRequest.Header.Set("Range", value)
	}

	upstream, err := effectMediaProxyHTTPClient.Do(upstreamRequest)
	if err != nil {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	defer upstream.Body.Close()

	if upstream.StatusCode < http.StatusOK || upstream.StatusCode >= http.StatusMultipleChoices {
		httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
		return
	}
	if upstream.ContentLength > effectMediaProxyMaxBytes {
		httpkit.WriteError(writer, request, http.StatusRequestEntityTooLarge, httpkit.CodeInvalidArgument, "invalid argument")
		return
	}
	if value := strings.TrimSpace(upstream.Header.Get("Content-Type")); value != "" {
		contentType = value
	}

	header := writer.Header()
	header.Set("Content-Type", contentType)
	header.Set("Cache-Control", "public, max-age=86400")
	header.Set("X-Content-Type-Options", "nosniff")
	if value := strings.TrimSpace(upstream.Header.Get("Accept-Ranges")); value != "" {
		header.Set("Accept-Ranges", value)
	}
	if value := strings.TrimSpace(upstream.Header.Get("Content-Range")); value != "" {
		header.Set("Content-Range", value)
	}
	if upstream.ContentLength >= 0 {
		header.Set("Content-Length", strconv.FormatInt(upstream.ContentLength, 10))
	}

	writer.WriteHeader(upstream.StatusCode)
	_, _ = io.Copy(writer, io.LimitReader(upstream.Body, effectMediaProxyMaxBytes+1))
}

func validateEffectMediaProxyURL(rawURL string) (*url.URL, string, bool) {
	sourceURL, err := url.Parse(rawURL)
	if err != nil || sourceURL == nil {
		return nil, "", false
	}
	if sourceURL.Scheme != "https" {
		return nil, "", false
	}
	host := strings.ToLower(sourceURL.Hostname())
	if _, ok := effectMediaProxyHosts[host]; !ok {
		return nil, "", false
	}
	contentType, ok := effectMediaProxyExtensions[strings.ToLower(path.Ext(sourceURL.Path))]
	if !ok {
		return nil, "", false
	}
	return sourceURL, contentType, true
}
