package proxy

import (
	"bufio"
	"io"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/rose_cat707/luminary/internal/gateway"
	"github.com/rose_cat707/luminary/internal/middleware"
	"github.com/rose_cat707/luminary/internal/model"
	"github.com/rose_cat707/luminary/internal/provider"
)

type Handler struct {
	gateway *gateway.Service
}

func New(gw *gateway.Service) *Handler {
	return &Handler{gateway: gw}
}

func (h *Handler) Register(r *gin.RouterGroup) {
	r.POST("/chat/completions", h.forward(provider.EndpointChatCompletion))
	r.POST("/completions", h.forward(provider.EndpointTextCompletion))
	r.POST("/responses", h.forward(provider.EndpointResponses))
	r.POST("/embeddings", h.forward(provider.EndpointEmbeddings))
	r.POST("/rerank", h.forward(provider.EndpointRerank))
}

func (h *Handler) ingressKey(c *gin.Context) (*model.IngressKey, bool) {
	v, ok := c.Get("ingress_key")
	if !ok {
		return nil, false
	}
	k, ok := v.(*model.IngressKey)
	return k, ok
}

func (h *Handler) forward(endpoint provider.EndpointType) gin.HandlerFunc {
	return func(c *gin.Context) {
		ingress, ok := h.ingressKey(c)
		if !ok {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
			return
		}
		body, err := io.ReadAll(c.Request.Body)
		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "invalid body"})
			return
		}
		if provider.IsStreamRequest(body) && provider.SupportsStreaming(endpoint) {
			h.handleStream(c, ingress, endpoint, body)
			return
		}
		clientIP := middleware.ClientIP(c)
		resp, err := h.gateway.ForwardEndpoint(c.Request.Context(), ingress, endpoint, body, clientIP)
		if err != nil {
			c.JSON(gatewayHTTPStatus(err), gin.H{"error": gin.H{"message": err.Error(), "type": "gateway_error"}})
			return
		}
		c.Data(resp.StatusCode, "application/json", resp.Body)
	}
}

func (h *Handler) handleStream(c *gin.Context, ingress *model.IngressKey, endpoint provider.EndpointType, body []byte) {
	c.Header("Content-Type", "text/event-stream")
	c.Header("Cache-Control", "no-cache")
	c.Header("Connection", "keep-alive")
	c.Status(http.StatusOK)

	flusher, ok := c.Writer.(http.Flusher)
	if !ok {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "streaming not supported"})
		return
	}
	w := bufio.NewWriter(c.Writer)
	clientIP := middleware.ClientIP(c)
	err := h.gateway.ForwardStream(c.Request.Context(), ingress, endpoint, body, clientIP, w, flusher)
	w.Flush()
	if err != nil {
		_, _ = c.Writer.Write([]byte("data: {\"error\":\"" + err.Error() + "\"}\n\n"))
		flusher.Flush()
	}
}
