package rest

import (
	"crypto/tls"
	"encoding/base64"
	"io"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/rs/zerolog/log"
)

var sfProxyClient = &http.Client{
	Timeout: 30 * time.Second,
	Transport: &http.Transport{
		TLSClientConfig: &tls.Config{MinVersion: tls.VersionTLS12},
	},
}

func RegisterSfInstanceProxy(router *gin.Engine) {
	router.Any("/sf-instance/:encodedOrigin/*path", handleSfInstanceProxy)
}

func handleSfInstanceProxy(c *gin.Context) {
	encoded := c.Param("encodedOrigin")
	path := c.Param("path")

	originBytes, err := base64.RawURLEncoding.DecodeString(encoded)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid encoded origin"})
		return
	}
	origin := string(originBytes)

	parsed, err := url.Parse(origin)
	if err != nil || parsed.Scheme != "https" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "origin must be https"})
		return
	}

	if !isSalesforceHost(parsed.Host) {
		c.JSON(http.StatusForbidden, gin.H{"error": "only *.salesforce.com and *.force.com origins are allowed"})
		return
	}

	targetURL := strings.TrimRight(origin, "/") + path
	if c.Request.URL.RawQuery != "" {
		targetURL += "?" + c.Request.URL.RawQuery
	}

	proxyReq, err := http.NewRequestWithContext(c.Request.Context(), c.Request.Method, targetURL, c.Request.Body)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create proxy request"})
		return
	}

	for _, h := range []string{"Authorization", "Content-Type", "Accept", "Sforce-Query-Options"} {
		if v := c.GetHeader(h); v != "" {
			proxyReq.Header.Set(h, v)
		}
	}

	resp, err := sfProxyClient.Do(proxyReq)
	if err != nil {
		log.Warn().Err(err).Str("target", targetURL).Msg("sf-instance proxy request failed")
		c.JSON(http.StatusBadGateway, gin.H{"error": "upstream request failed"})
		return
	}
	defer resp.Body.Close()

	for _, h := range []string{"Content-Type", "Sforce-Limit-Info"} {
		if v := resp.Header.Get(h); v != "" {
			c.Header(h, v)
		}
	}

	c.Status(resp.StatusCode)
	if _, err := io.Copy(c.Writer, resp.Body); err != nil {
		log.Debug().Err(err).Str("target", targetURL).Msg("sf-instance proxy response copy interrupted")
	}
}

func isSalesforceHost(host string) bool {
	h := strings.ToLower(host)
	return strings.HasSuffix(h, ".salesforce.com") ||
		strings.HasSuffix(h, ".force.com") ||
		h == "salesforce.com" ||
		h == "force.com"
}
