package globalapi

import (
	stderrors "errors"
	"fmt"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/pingcap/log"
	"github.com/tidbcloud/observability/pkg/common/constants"
	"github.com/tidbcloud/observability/pkg/common/errors"
	"github.com/tidbcloud/observability/pkg/common/metrics"
	"github.com/tidbcloud/observability/pkg/common/structs"
	"github.com/tidbcloud/observability/pkg/globalservice"
	"github.com/tidbcloud/observability/pkg/regionalapi/apiinterceptor"
	"go.uber.org/zap"
)

// QueryRegionRoutes
// @Summary Query region routes for logical region routing
// @Description Query regional route information based on vendor, region, tenant ID, and business type. This API returns the DNS address and routing details for the specified logical region.
// @Tags globalapi
// @Accept json
// @Produce json
// @Param X-Logical-Region header string false "Logical region name. If supplied (non-empty), takes precedence over the (X-Org-Id, X-Biz-Type) pair. X-Region-Id is always required."
// @Param X-Region-Id header string true "Region ID in the format <vendor>-<region>, e.g. aws-us-west-2. Always required."
// @Param X-Org-Id header string false "Organization/Tenant ID. Required when X-Logical-Region is empty."
// @Param X-Biz-Type header string false "Business type (e.g., tidbcloud, serverless, byoc). Required when X-Logical-Region is empty."
// @Param X-Project-Id header string false "Project ID for finer-grained routing (e.g. serverless parent cluster f02-f06)."
// @Param X-Application-Id header string false "Application/Cluster ID (e.g. serverless parent cluster devtier-infra)."
// @Success 200 {object} message.Result{data=structs.RegionalRouter} "Regional router information including DNS address and logical region details"
// @Failure 400 {object} message.Result "Bad request - missing required parameters"
// @Failure 500 {object} message.Result "Internal server error"
// @Router /routes [get]
func QueryRegionRoutes(c *gin.Context) {
	// get resolve parameter from gin context
	obj, ok := c.Get(structs.GlobalServerRequestParametersKey)
	if !ok {
		log.Error("get all request parameters failed")
		apiinterceptor.Fail(c, errors.New(errors.GetRequestParamError, "get all request parameters failed"))
		return
	}
	reqParams := obj.(structs.RequestParameters)

	// When X-Logical-Region is set, X-Region-Id (vendor+region) is also required to scope the lookup.
	if _, hasLogical := reqParams.Headers[string(constants.OpenAPIHeaderLogicalRegion)]; hasLogical {
		if reqParams.Vendor == "" || reqParams.Region == "" {
			log.Error("X-Region-Id is required when X-Logical-Region is set")
			apiinterceptor.Fail(c, errors.New(errors.ParameterInvalid, "X-Region-Id header is required when X-Logical-Region is set"))
			return
		}
	}

	// Build routing context for logical region routing
	routingCtx := buildRoutingContext(reqParams, c.Request.URL.Path)

	// Resolve serverless child-cluster dimensions to parent-cluster dimensions
	// before route lookup. On failure, log the error and fall back to the
	// original routing context rather than rejecting the request.
	// mgmt call failures are propagated so the caller can retry.
	if shouldResolveServerlessChild(routingCtx) {
		resolved, resolveErr := resolveServerlessChild(c.Request.Context(), routingCtx)
		if resolveErr != nil {
			if stderrors.Is(resolveErr, ErrMgmtCallFailed) {
				log.Error("resolve serverless child mgmt call failed", zap.Error(resolveErr))
				metrics.HandleGlobalBizErrorMetrics(fmt.Sprintf("resolve serverless child mgmt call failed: %s", resolveErr.Error()))
				apiinterceptor.Fail(c, resolveErr)
				return
			}
			log.Warn("resolve serverless child failed, falling back to original routing context",
				zap.Error(resolveErr),
				zap.String("applicationID", routingCtx.ApplicationID),
				zap.String("bizType", routingCtx.BizType))
			metrics.HandleGlobalBizErrorMetrics(fmt.Sprintf("resolve serverless child failed, fallback to original context. error: %s", resolveErr.Error()))
		} else {
			routingCtx = resolved
		}
	}

	routeInfo, err := globalservice.GetGlobalManager().GetRegionalRouteInfo(c, routingCtx)
	if err != nil {
		log.Error("get regional route info failed", zap.Error(err))
		metrics.HandleGlobalBizErrorMetrics(fmt.Sprintf("get regional route info failed. error: %s", err.Error()))
		apiinterceptor.Fail(c, err)
		return
	}

	apiinterceptor.Succeed(c, http.StatusOK, routeInfo)
}
