// Filename: internal/handlers/proxy_handler.go
package handlers

import (
	"bytes"
	"compress/gzip"
	"context"
	"encoding/json"
	"fmt"
	"gemini-balancer/internal/channel"
	"gemini-balancer/internal/errors"
	"gemini-balancer/internal/middleware"
	"gemini-balancer/internal/models"
	"gemini-balancer/internal/service"
	"gemini-balancer/internal/settings"
	"gemini-balancer/internal/store"
	"io"
	"net"
	"net/http"
	"net/http/httptest"
	"net/http/httputil"
	"net/url"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"github.com/sirupsen/logrus"
	"gorm.io/datatypes"
)

type proxyErrorContextKey struct{}

type ProxyHandler struct {
	resourceService  *service.ResourceService
	store            store.Store
	settingsManager  *settings.SettingsManager
	groupManager     *service.GroupManager
	channel          channel.ChannelProxy
	logger           *logrus.Entry
	transparentProxy *httputil.ReverseProxy
}

func NewProxyHandler(
	resourceService *service.ResourceService,
	store store.Store,
	sm *settings.SettingsManager,
	gm *service.GroupManager,
	channel channel.ChannelProxy,
	logger *logrus.Logger,
) *ProxyHandler {
	ph := &ProxyHandler{
		resourceService:  resourceService,
		store:            store,
		settingsManager:  sm,
		groupManager:     gm,
		channel:          channel,
		logger:           logger.WithField("component", "ProxyHandler"),
		transparentProxy: &httputil.ReverseProxy{},
	}
	ph.transparentProxy.Transport = &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		DialContext: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 60 * time.Second,
		}).DialContext,
		MaxIdleConns:          100,
		IdleConnTimeout:       90 * time.Second,
		TLSHandshakeTimeout:   10 * time.Second,
		ExpectContinueTimeout: 1 * time.Second,
	}
	ph.transparentProxy.ErrorHandler = ph.transparentProxyErrorHandler
	ph.transparentProxy.BufferPool = &bufferPool{}
	return ph
}

func (h *ProxyHandler) HandleProxy(c *gin.Context) {
	if c.Request.Method == "GET" && (strings.HasSuffix(c.Request.URL.Path, "/models") || strings.HasSuffix(c.Request.URL.Path, "/models/")) {
		h.handleListModelsRequest(c)
		return
	}

	maxBodySize := int64(h.settingsManager.GetSettings().MaxRequestBodySizeMB * 1024 * 1024)
	requestBody, err := io.ReadAll(io.LimitReader(c.Request.Body, maxBodySize))
	if err != nil {
		errToJSON(c, uuid.New().String(), errors.NewAPIError(errors.ErrBadRequest, "Request body too large or failed to read"))
		return
	}

	modelName := h.channel.ExtractModel(c, requestBody)
	groupName := c.Param("group_name")
	isPreciseRouting := groupName != ""

	if !isPreciseRouting && modelName == "" {
		errToJSON(c, uuid.New().String(), errors.NewAPIError(errors.ErrBadRequest, "Model not specified in request"))
		return
	}

	initialResources, err := h.getResourcesForRequest(c, modelName, groupName, isPreciseRouting)
	if err != nil {
		if apiErr, ok := err.(*errors.APIError); ok {
			errToJSON(c, uuid.New().String(), apiErr)
		} else {
			errToJSON(c, uuid.New().String(), errors.NewAPIError(errors.ErrInternalServer, "Failed to get initial resources"))
		}
		return
	}

	finalOpConfig, err := h.groupManager.BuildOperationalConfig(initialResources.KeyGroup)
	if err != nil {
		h.logger.WithError(err).Error("Failed to build operational config.")
		errToJSON(c, uuid.New().String(), errors.NewAPIError(errors.ErrInternalServer, "Failed to build operational config"))
		return
	}

	initialResources.RequestConfig = h.buildFinalRequestConfig(h.settingsManager.GetSettings(), initialResources.RequestConfig)

	isOpenAICompatible := h.channel.IsOpenAICompatibleRequest(c)
	if isOpenAICompatible {
		h.serveTransparentProxy(c, requestBody, initialResources, finalOpConfig, modelName, groupName, isPreciseRouting)
		return
	}

	isStream := h.channel.IsStreamRequest(c, requestBody)
	useSmartGateway := finalOpConfig.EnableSmartGateway != nil && *finalOpConfig.EnableSmartGateway
	if useSmartGateway && isStream && h.settingsManager.GetSettings().EnableStreamingRetry {
		h.serveSmartStream(c, requestBody, initialResources, isPreciseRouting)
	} else {
		h.serveTransparentProxy(c, requestBody, initialResources, finalOpConfig, modelName, groupName, isPreciseRouting)
	}
}

func (h *ProxyHandler) serveTransparentProxy(c *gin.Context, requestBody []byte, initialResources *service.RequestResources, finalOpConfig *models.KeyGroupSettings, modelName, groupName string, isPreciseRouting bool) {
	startTime := time.Now()
	correlationID := uuid.New().String()

	var finalRecorder *httptest.ResponseRecorder
	var lastUsedResources *service.RequestResources
	var finalProxyErr *errors.APIError
	var isSuccess bool
	var finalPromptTokens, finalCompletionTokens, actualRetries int

	defer h.publishFinalLogEvent(c, startTime, correlationID, modelName, lastUsedResources,
		finalRecorder, finalProxyErr, isSuccess, finalPromptTokens, finalCompletionTokens,
		actualRetries, isPreciseRouting)

	maxRetries := h.getMaxRetries(isPreciseRouting, finalOpConfig)
	totalAttempts := maxRetries + 1

	for attempt := 1; attempt <= totalAttempts; attempt++ {
		if c.Request.Context().Err() != nil {
			h.logger.WithField("id", correlationID).Info("Client disconnected, aborting retry loop.")
			if finalProxyErr == nil {
				finalProxyErr = errors.NewAPIError(errors.ErrBadRequest, "Client disconnected")
			}
			break
		}

		resources, err := h.getResourcesForAttempt(c, attempt, initialResources, modelName, groupName, isPreciseRouting, correlationID)
		if err != nil {
			if apiErr, ok := err.(*errors.APIError); ok {
				finalProxyErr = apiErr
			} else {
				finalProxyErr = errors.NewAPIError(errors.ErrInternalServer, "Failed to get resources for retry")
			}
			break
		}
		lastUsedResources = resources
		if attempt > 1 {
			actualRetries = attempt - 1
		}

		h.logger.WithField("id", correlationID).Infof("Attempt %d/%d with KeyID %d", attempt, totalAttempts, resources.APIKey.ID)

		recorder, attemptErr, attemptSuccess := h.executeProxyAttempt(
			c, correlationID, requestBody, resources, isPreciseRouting, groupName,
			&finalPromptTokens, &finalCompletionTokens,
		)

		finalRecorder, finalProxyErr, isSuccess = recorder, attemptErr, attemptSuccess
		h.resourceService.ReportRequestResult(resources, isSuccess, finalProxyErr)

		if isSuccess {
			break
		}
		if h.shouldStopRetrying(attempt, totalAttempts, finalProxyErr, correlationID) {
			break
		}
		h.publishRetryLogEvent(c, startTime, correlationID, modelName, resources, recorder, attemptErr, actualRetries, isPreciseRouting)
	}

	h.writeFinalResponse(c, correlationID, finalRecorder, finalProxyErr)
}

func (h *ProxyHandler) executeProxyAttempt(c *gin.Context, corrID string, body []byte, res *service.RequestResources, isPrecise bool, groupName string, pTokens, cTokens *int) (*httptest.ResponseRecorder, *errors.APIError, bool) {
	recorder := httptest.NewRecorder()
	var attemptErr *errors.APIError
	var isSuccess bool

	requestTimeout := time.Duration(h.settingsManager.GetSettings().RequestTimeoutSeconds) * time.Second
	ctx, cancel := context.WithTimeout(c.Request.Context(), requestTimeout)
	defer cancel()

	attemptReq := c.Request.Clone(ctx)
	attemptReq.Body = io.NopCloser(bytes.NewReader(body))
	attemptReq.ContentLength = int64(len(body))

	h.configureProxy(corrID, res, isPrecise, groupName, &attemptErr, &isSuccess, pTokens, cTokens)
	*attemptReq = *attemptReq.WithContext(context.WithValue(attemptReq.Context(), proxyErrorContextKey{}, &attemptErr))

	h.transparentProxy.ServeHTTP(recorder, attemptReq)

	return recorder, attemptErr, isSuccess
}

func (h *ProxyHandler) configureProxy(corrID string, res *service.RequestResources, isPrecise bool, groupName string, attemptErr **errors.APIError, isSuccess *bool, pTokens, cTokens *int) {
	h.transparentProxy.Director = func(r *http.Request) {
		targetURL, _ := url.Parse(res.UpstreamEndpoint.URL)
		r.URL.Scheme, r.URL.Host, r.Host = targetURL.Scheme, targetURL.Host, targetURL.Host

		var pureClientPath string
		if isPrecise {
			pureClientPath = strings.TrimPrefix(r.URL.Path, "/proxy/"+groupName)
		} else {
			pureClientPath = r.URL.Path
		}
		r.URL.Path = h.channel.RewritePath(targetURL.Path, pureClientPath)

		r.Header.Del("Authorization")
		h.channel.ModifyRequest(r, res.APIKey)
		r.Header.Set("X-Correlation-ID", corrID)
	}

	transport := h.transparentProxy.Transport.(*http.Transport)
	if res.ProxyConfig != nil {
		proxyURLStr := fmt.Sprintf("%s://%s", res.ProxyConfig.Protocol, res.ProxyConfig.Address)
		if proxyURL, err := url.Parse(proxyURLStr); err == nil {
			transport.Proxy = http.ProxyURL(proxyURL)
		} else {
			transport.Proxy = http.ProxyFromEnvironment
		}
	} else {
		transport.Proxy = http.ProxyFromEnvironment
	}

	h.transparentProxy.ModifyResponse = h.createModifyResponseFunc(attemptErr, isSuccess, pTokens, cTokens)
}

func (h *ProxyHandler) createModifyResponseFunc(attemptErr **errors.APIError, isSuccess *bool, pTokens, cTokens *int) func(*http.Response) error {
	return func(resp *http.Response) error {
		var reader io.ReadCloser = resp.Body
		if resp.Header.Get("Content-Encoding") == "gzip" {
			gzReader, err := gzip.NewReader(resp.Body)
			if err != nil {
				h.logger.WithError(err).Error("Failed to create gzip reader")
			} else {
				reader = gzReader
				resp.Header.Del("Content-Encoding")
			}
		}
		defer reader.Close()

		bodyBytes, err := io.ReadAll(reader)
		if err != nil {
			*attemptErr = errors.NewAPIErrorWithUpstream(http.StatusBadGateway, "UPSTREAM_GATEWAY_ERROR", nil)
			resp.Body = io.NopCloser(bytes.NewReader([]byte{}))
			return nil
		}

		if resp.StatusCode < 400 {
			*isSuccess = true
			*pTokens, *cTokens = extractUsage(bodyBytes)
		} else {
			*attemptErr = errors.NewAPIErrorWithUpstream(resp.StatusCode, fmt.Sprintf("UPSTREAM_%d", resp.StatusCode), bodyBytes)
		}
		resp.Body = io.NopCloser(bytes.NewReader(bodyBytes))
		return nil
	}
}

func (h *ProxyHandler) transparentProxyErrorHandler(rw http.ResponseWriter, r *http.Request, err error) {
	corrID := r.Header.Get("X-Correlation-ID")
	log := h.logger.WithField("id", corrID)
	log.Errorf("Transparent proxy encountered an error: %v", err)

	errPtr, ok := r.Context().Value(proxyErrorContextKey{}).(**errors.APIError)
	if !ok || errPtr == nil {
		log.Error("FATAL: proxyErrorContextKey not found in context for error handler.")
		defaultErr := errors.NewAPIError(errors.ErrBadGateway, "An unexpected proxy error occurred")
		writeErrorToResponse(rw, defaultErr)
		return
	}

	if *errPtr == nil {
		if errors.IsClientNetworkError(err) {
			*errPtr = errors.NewAPIError(errors.ErrBadRequest, "Client connection closed")
		} else {
			*errPtr = errors.NewAPIError(errors.ErrBadGateway, err.Error())
		}
	}
	writeErrorToResponse(rw, *errPtr)
}

func (h *ProxyHandler) getResourcesForAttempt(c *gin.Context, attempt int, initialResources *service.RequestResources, modelName, groupName string, isPreciseRouting bool, correlationID string) (*service.RequestResources, error) {
	if attempt == 1 {
		return initialResources, nil
	}
	h.logger.WithField("id", correlationID).Infof("Retrying... getting new resources for attempt %d.", attempt)
	resources, err := h.getResourcesForRequest(c, modelName, groupName, isPreciseRouting)
	if err != nil {
		return nil, err
	}
	finalRequestConfig := h.buildFinalRequestConfig(h.settingsManager.GetSettings(), resources.RequestConfig)
	resources.RequestConfig = finalRequestConfig
	return resources, nil
}

func (h *ProxyHandler) shouldStopRetrying(attempt, totalAttempts int, err *errors.APIError, correlationID string) bool {
	if attempt >= totalAttempts {
		return true
	}
	if err != nil && errors.IsUnretryableRequestError(err.Message) {
		h.logger.WithField("id", correlationID).Warnf("Attempt failed with unretryable request error. Aborting retries. Message: %s", err.Message)
		return true
	}
	return false
}

func (h *ProxyHandler) writeFinalResponse(c *gin.Context, corrID string, rec *httptest.ResponseRecorder, apiErr *errors.APIError) {
	if rec != nil {
		for k, v := range rec.Header() {
			c.Writer.Header()[k] = v
		}
		c.Writer.WriteHeader(rec.Code)
		c.Writer.Write(rec.Body.Bytes())
	} else if apiErr != nil {
		errToJSON(c, corrID, apiErr)
	} else {
		errToJSON(c, corrID, errors.NewAPIError(errors.ErrInternalServer, "An unknown error occurred"))
	}
}

func (h *ProxyHandler) publishFinalLogEvent(c *gin.Context, startTime time.Time, corrID, modelName string, res *service.RequestResources, rec *httptest.ResponseRecorder, finalErr *errors.APIError, isSuccess bool, pTokens, cTokens, retries int, isPrecise bool) {
	if res == nil {
		h.logger.WithField("id", corrID).Warn("No resources were used, skipping final log event.")
		return
	}
	event := h.createLogEvent(c, startTime, corrID, modelName, res, models.LogTypeFinal, isPrecise)
	event.RequestLog.LatencyMs = int(time.Since(startTime).Milliseconds())
	event.RequestLog.IsSuccess = isSuccess
	event.RequestLog.Retries = retries
	if isSuccess {
		event.RequestLog.PromptTokens, event.RequestLog.CompletionTokens = pTokens, cTokens
	}
	if rec != nil {
		event.RequestLog.StatusCode = rec.Code
	}
	if !isSuccess {
		errToLog := finalErr
		if errToLog == nil && rec != nil {
			errToLog = errors.NewAPIErrorWithUpstream(rec.Code, fmt.Sprintf("UPSTREAM_%d", rec.Code), rec.Body.Bytes())
		}
		if errToLog != nil {
			if errToLog.Code == "" && errToLog.HTTPStatus >= 400 {
				errToLog.Code = fmt.Sprintf("UPSTREAM_%d", errToLog.HTTPStatus)
			}
			event.Error = errToLog
			event.RequestLog.ErrorCode, event.RequestLog.ErrorMessage = errToLog.Code, errToLog.Message
			event.RequestLog.Status = errToLog.Status
		}
	}
	eventData, err := json.Marshal(event)
	if err != nil {
		h.logger.WithField("id", corrID).WithError(err).Error("Failed to marshal log event")
		return
	}
	if err := h.store.Publish(context.Background(), models.TopicRequestFinished, eventData); err != nil {
		h.logger.WithField("id", corrID).WithError(err).Error("Failed to publish log event")
	}
}

func (h *ProxyHandler) publishRetryLogEvent(c *gin.Context, startTime time.Time, corrID, modelName string, res *service.RequestResources, rec *httptest.ResponseRecorder, attemptErr *errors.APIError, retries int, isPrecise bool) {
	retryEvent := h.createLogEvent(c, startTime, corrID, modelName, res, models.LogTypeRetry, isPrecise)
	retryEvent.RequestLog.LatencyMs = int(time.Since(startTime).Milliseconds())
	retryEvent.RequestLog.IsSuccess = false
	retryEvent.RequestLog.StatusCode = rec.Code
	retryEvent.RequestLog.Retries = retries
	if attemptErr != nil {
		retryEvent.Error = attemptErr
		retryEvent.RequestLog.ErrorCode, retryEvent.RequestLog.ErrorMessage = attemptErr.Code, attemptErr.Message
		retryEvent.RequestLog.Status = attemptErr.Status
	}
	eventData, err := json.Marshal(retryEvent)
	if err != nil {
		h.logger.WithField("id", corrID).WithError(err).Error("Failed to marshal retry log event")
		return
	}
	if err := h.store.Publish(context.Background(), models.TopicRequestFinished, eventData); err != nil {
		h.logger.WithField("id", corrID).WithError(err).Error("Failed to publish retry log event")
	}
}

func (h *ProxyHandler) buildFinalRequestConfig(globalSettings *models.SystemSettings, groupConfig *models.RequestConfig) *models.RequestConfig {
	finalConfig := &models.RequestConfig{
		CustomHeaders:         make(datatypes.JSONMap),
		EnableStreamOptimizer: globalSettings.EnableStreamOptimizer,
		StreamMinDelay:        globalSettings.StreamMinDelay,
		StreamMaxDelay:        globalSettings.StreamMaxDelay,
		StreamShortTextThresh: globalSettings.StreamShortTextThresh,
		StreamLongTextThresh:  globalSettings.StreamLongTextThresh,
		StreamChunkSize:       globalSettings.StreamChunkSize,
		EnableFakeStream:      globalSettings.EnableFakeStream,
		FakeStreamInterval:    globalSettings.FakeStreamInterval,
	}
	for k, v := range globalSettings.CustomHeaders {
		finalConfig.CustomHeaders[k] = v
	}
	if groupConfig == nil {
		return finalConfig
	}
	groupConfigJSON, err := json.Marshal(groupConfig)
	if err != nil {
		h.logger.WithError(err).Error("Failed to marshal group request config for merging.")
		return finalConfig
	}
	if err := json.Unmarshal(groupConfigJSON, finalConfig); err != nil {
		h.logger.WithError(err).Error("Failed to unmarshal group request config for merging.")
	}
	return finalConfig
}

func writeErrorToResponse(rw http.ResponseWriter, apiErr *errors.APIError) {
	if writer, ok := rw.(interface{ Written() bool }); ok && writer.Written() {
		return
	}
	rw.Header().Set("Content-Type", "application/json; charset=utf-8")
	rw.WriteHeader(apiErr.HTTPStatus)
	json.NewEncoder(rw).Encode(gin.H{"error": apiErr})
}

func (h *ProxyHandler) serveSmartStream(c *gin.Context, requestBody []byte, resources *service.RequestResources, isPreciseRouting bool) {
	startTime := time.Now()
	correlationID := uuid.New().String()
	log := h.logger.WithField("id", correlationID)
	log.Info("Smart Gateway activated for streaming request.")
	var originalRequest models.GeminiRequest
	if err := json.Unmarshal(requestBody, &originalRequest); err != nil {
		errToJSON(c, correlationID, errors.NewAPIError(errors.ErrInvalidJSON, "Invalid request format for Smart Gateway"))
		return
	}
	systemSettings := h.settingsManager.GetSettings()
	modelName := h.channel.ExtractModel(c, requestBody)
	requestFinishedEvent := h.createLogEvent(c, startTime, correlationID, modelName, resources, models.LogTypeFinal, isPreciseRouting)
	defer func() {
		requestFinishedEvent.LatencyMs = int(time.Since(startTime).Milliseconds())
		if c.Writer.Status() > 0 {
			requestFinishedEvent.StatusCode = c.Writer.Status()
		}
		eventData, err := json.Marshal(requestFinishedEvent)
		if err != nil {
			h.logger.WithField("id", correlationID).WithError(err).Error("Failed to marshal final log event for smart stream")
			return
		}
		if err := h.store.Publish(context.Background(), models.TopicRequestFinished, eventData); err != nil {
			h.logger.WithField("id", correlationID).WithError(err).Error("Failed to publish final log event for smart stream")
		}
	}()
	params := channel.SmartRequestParams{
		CorrelationID:        correlationID,
		APIKey:               resources.APIKey,
		UpstreamURL:          resources.UpstreamEndpoint.URL,
		RequestBody:          requestBody,
		OriginalRequest:      originalRequest,
		EventLogger:          requestFinishedEvent,
		MaxRetries:           systemSettings.MaxStreamingRetries,
		RetryDelay:           time.Duration(systemSettings.StreamingRetryDelayMs) * time.Millisecond,
		LogTruncationLimit:   systemSettings.LogTruncationLimit,
		StreamingRetryPrompt: systemSettings.StreamingRetryPrompt,
	}
	h.channel.ProcessSmartStreamRequest(c, params)
}

func (h *ProxyHandler) createLogEvent(c *gin.Context, startTime time.Time, corrID, modelName string, res *service.RequestResources, logType models.LogType, isPreciseRouting bool) *models.RequestFinishedEvent {
	event := &models.RequestFinishedEvent{
		RequestLog: models.RequestLog{
			RequestTime:   startTime,
			ModelName:     modelName,
			RequestPath:   c.Request.URL.Path,
			UserAgent:     c.Request.UserAgent(),
			CorrelationID: corrID,
			LogType:       logType,
			Metadata:      make(datatypes.JSONMap),
		},
		CorrelationID:    corrID,
		IsPreciseRouting: isPreciseRouting,
	}
	if _, exists := c.Get(middleware.RedactedBodyKey); exists {
		event.RequestLog.Metadata["request_body_present"] = true
	}
	if redactedAuth, exists := c.Get(middleware.RedactedAuthHeaderKey); exists {
		event.RequestLog.Metadata["authorization_header"] = redactedAuth.(string)
	}
	if authTokenValue, exists := c.Get("authToken"); exists {
		if authToken, ok := authTokenValue.(*models.AuthToken); ok {
			event.RequestLog.AuthTokenID = &authToken.ID
		}
	}
	if res != nil {
		if res.APIKey != nil {
			event.RequestLog.KeyID = &res.APIKey.ID
		}
		if res.KeyGroup != nil {
			event.RequestLog.GroupID = &res.KeyGroup.ID
		}
		if res.UpstreamEndpoint != nil {
			event.RequestLog.UpstreamID = &res.UpstreamEndpoint.ID
			event.UpstreamURL = &res.UpstreamEndpoint.URL
		}
		if res.ProxyConfig != nil {
			event.RequestLog.ProxyID = &res.ProxyConfig.ID
		}
	}
	return event
}

func (h *ProxyHandler) getResourcesForRequest(c *gin.Context, modelName string, groupName string, isPreciseRouting bool) (*service.RequestResources, error) {
	authTokenValue, exists := c.Get("authToken")
	if !exists {
		return nil, errors.NewAPIError(errors.ErrUnauthorized, "Auth token not found in context")
	}
	authToken, ok := authTokenValue.(*models.AuthToken)
	if !ok {
		return nil, errors.NewAPIError(errors.ErrInternalServer, "Invalid auth token type in context")
	}
	if isPreciseRouting {
		return h.resourceService.GetResourceFromGroup(c.Request.Context(), authToken, groupName)
	}
	return h.resourceService.GetResourceFromBasePool(c.Request.Context(), authToken, modelName)
}

func errToJSON(c *gin.Context, corrID string, apiErr *errors.APIError) {
	if c.IsAborted() {
		return
	}
	c.JSON(apiErr.HTTPStatus, gin.H{
		"error":          apiErr,
		"correlation_id": corrID,
	})
}

type bufferPool struct{}

func (b *bufferPool) Get() []byte  { return make([]byte, 32*1024) }
func (b *bufferPool) Put(_ []byte) {}

func extractUsage(body []byte) (promptTokens int, completionTokens int) {
	var data struct {
		UsageMetadata struct {
			PromptTokenCount     int `json:"promptTokenCount"`
			CandidatesTokenCount int `json:"candidatesTokenCount"`
		} `json:"usageMetadata"`
	}
	if err := json.Unmarshal(body, &data); err == nil {
		return data.UsageMetadata.PromptTokenCount, data.UsageMetadata.CandidatesTokenCount
	}
	return 0, 0
}

func (h *ProxyHandler) getMaxRetries(isPreciseRouting bool, finalOpConfig *models.KeyGroupSettings) int {
	if isPreciseRouting && finalOpConfig.MaxRetries != nil {
		return *finalOpConfig.MaxRetries
	}
	return h.settingsManager.GetSettings().MaxRetries
}

func (h *ProxyHandler) handleListModelsRequest(c *gin.Context) {
	authTokenValue, exists := c.Get("authToken")
	if !exists {
		errToJSON(c, uuid.New().String(), errors.NewAPIError(errors.ErrUnauthorized, "Auth token not found in context"))
		return
	}
	authToken, ok := authTokenValue.(*models.AuthToken)
	if !ok {
		errToJSON(c, uuid.New().String(), errors.NewAPIError(errors.ErrInternalServer, "Invalid auth token type in context"))
		return
	}
	modelNames := h.resourceService.GetAllowedModelsForToken(authToken)
	if strings.Contains(c.Request.URL.Path, "/v1beta/") {
		h.respondWithGeminiFormat(c, modelNames)
	} else {
		h.respondWithOpenAIFormat(c, modelNames)
	}
}

func (h *ProxyHandler) respondWithOpenAIFormat(c *gin.Context, modelNames []string) {
	type ModelEntry struct {
		ID      string `json:"id"`
		Object  string `json:"object"`
		Created int64  `json:"created"`
		OwnedBy string `json:"owned_by"`
	}
	type ModelListResponse struct {
		Object string       `json:"object"`
		Data   []ModelEntry `json:"data"`
	}
	data := make([]ModelEntry, len(modelNames))
	for i, name := range modelNames {
		data[i] = ModelEntry{
			ID:      name,
			Object:  "model",
			Created: time.Now().Unix(),
			OwnedBy: "gemini-balancer",
		}
	}
	response := ModelListResponse{
		Object: "list",
		Data:   data,
	}
	c.JSON(http.StatusOK, response)
}

func (h *ProxyHandler) respondWithGeminiFormat(c *gin.Context, modelNames []string) {
	type GeminiModelEntry struct {
		Name                       string   `json:"name"`
		Version                    string   `json:"version"`
		DisplayName                string   `json:"displayName"`
		Description                string   `json:"description"`
		SupportedGenerationMethods []string `json:"supportedGenerationMethods"`
		InputTokenLimit            int      `json:"inputTokenLimit"`
		OutputTokenLimit           int      `json:"outputTokenLimit"`
	}
	type GeminiModelListResponse struct {
		Models []GeminiModelEntry `json:"models"`
	}
	models := make([]GeminiModelEntry, len(modelNames))
	for i, name := range modelNames {
		models[i] = GeminiModelEntry{
			Name:                       fmt.Sprintf("models/%s", name),
			Version:                    "1.0.0",
			DisplayName:                name,
			Description:                "Served by Gemini Balancer",
			SupportedGenerationMethods: []string{"generateContent", "streamGenerateContent"},
			InputTokenLimit:            8192,
			OutputTokenLimit:           2048,
		}
	}
	response := GeminiModelListResponse{Models: models}
	c.JSON(http.StatusOK, response)
}
