package proxy

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"path"
	"regexp"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	gocache "github.com/patrickmn/go-cache"
	"github.com/prometheus/prometheus/pkg/labels"
	"github.com/prometheus/prometheus/promql/parser"
	"go.uber.org/zap"

	apierr "github.com/pingcap-inc/clinic/pkg/api/error"
	"github.com/pingcap-inc/clinic/pkg/auth/session"
	"github.com/pingcap-inc/clinic/pkg/log"
	"github.com/pingcap-inc/clinic/pkg/o11y"
	o11yurls "github.com/pingcap-inc/clinic/pkg/o11y/urls"
	rbac "github.com/pingcap-inc/clinic/pkg/rbac"
)

const (
	VarTiDBClusterVariable      = "var-tidb_cluster"
	VarOrgTypeVariable          = "var-org_type"
	O11yClusterInfoVariable     = "o11y_cluster_info"
	VarO11yClusterInfoVariable  = "var-o11y_cluster_info"
	ControlPlaneInfoVariable    = "control_plane_info"
	VarControlPlaneInfoVariable = "var-control_plane_info"
	VarProjectIDVariable        = "var-project_id"
)

const o11yClusterInfoCacheTTL = 5 * time.Minute

type O11yProxyHandler struct {
	rbac       rbac.Auth
	o11yEnable bool
	o11yCli    *o11y.Client
	env        string

	o11yClusterInfoCache *gocache.Cache
}

func NewO11yProxyHandler(rbac rbac.Auth, env string) *O11yProxyHandler {
	return &O11yProxyHandler{rbac: rbac, env: env}
}

func (handler *O11yProxyHandler) SetO11y(o11yCli *o11y.Client) {
	handler.o11yCli = o11yCli
	handler.o11yEnable = true
}

// ProxyControlPlaneMetrics resolves an explicit logical control-plane selector
// through the canonical O11Y resolver. Requests without a selector retain the
// legacy cluster-info behavior for compatibility.
func (handler *O11yProxyHandler) ProxyControlPlaneMetrics(c *gin.Context, routerPathFunc func(string) string) {
	ctx := c.Request.Context()
	controlPlaneInfo, source := controlPlaneInfoFromMetricsRequest(c)
	if controlPlaneInfo == "" {
		handler.GinHandle(c,
			func(c *gin.Context) string {
				return handler.GetO11yClusterInfo(c)
			},
			nil,
			routerPathFunc,
		)
		return
	}

	if session.GetUser(c) == nil {
		_ = c.Error(apierr.ErrNotFound.New("the user is not found"))
		return
	}
	if !handler.o11yEnable || handler.o11yCli == nil {
		_ = c.Error(apierr.ErrInternal.New("o11y is not enabled"))
		return
	}

	log.Info(ctx, "resolving control plane metrics target",
		zap.String("operation", "resolve_control_plane_metrics_target"),
		zap.String("selector_source", source))
	target, err := o11yurls.NewClusterURLsResolver(handler.o11yCli).ResolveLogicalControlPlaneMetricsTarget(ctx, controlPlaneInfo)
	if err != nil {
		switch {
		case errors.Is(err, o11yurls.ErrInvalidControlPlaneInfo):
			log.Warn(ctx, "invalid control plane metrics selector",
				zap.String("operation", "resolve_control_plane_metrics_target"),
				zap.String("selector_source", source))
			_ = c.Error(apierr.ErrBadRequest.New("control_plane_info is invalid"))
		case errors.Is(err, o11y.ErrNotFound):
			log.Warn(ctx, "control plane metrics target not found",
				zap.String("operation", "resolve_control_plane_metrics_target"),
				zap.String("selector_source", source))
			_ = c.Error(apierr.ErrNotFound.New("control plane metrics target is not found"))
		default:
			log.Error(ctx, "failed to resolve control plane metrics target",
				zap.String("operation", "resolve_control_plane_metrics_target"),
				zap.String("selector_source", source),
				zap.Error(err))
			_ = c.Error(apierr.ErrInternal.New("failed to resolve control plane metrics target"))
		}
		return
	}

	log.Info(ctx, "resolved control plane metrics target",
		zap.String("operation", "resolve_control_plane_metrics_target"),
		zap.String("selector_source", source),
		zap.String("o11y_cluster_id", target.O11yCluster.ID))
	handler.proxyCluster(c, target.O11yCluster, routerPathFunc)
}

func controlPlaneInfoFromMetricsRequest(c *gin.Context) (string, string) {
	if controlPlaneInfo := c.GetHeader(ControlPlaneInfoVariable); controlPlaneInfo != "" {
		return controlPlaneInfo, "header"
	}
	if promQL := GetO11yPromQL(c); promQL != "" {
		if controlPlaneInfo := GetPromQLData(promQL, ControlPlaneInfoVariable); controlPlaneInfo != "" {
			return controlPlaneInfo, "promql"
		}
	}
	return "", ""
}

func (handler *O11yProxyHandler) GinHandle(
	c *gin.Context,
	getO11yClusterInfo func(c *gin.Context) string,
	getControlPlaneInfo func(c *gin.Context) string,
	routerPathFunc func(string) string,
	getClusterID ...func(c *gin.Context) string,
) {
	user := session.GetUser(c)
	if user == nil {
		_ = c.Error(apierr.ErrNotFound.New("the user is not found"))
		return
	}

	if !handler.o11yEnable {
		// return no data with success
		c.Header("Content-Type", "application/json")
		if strings.HasSuffix(c.FullPath(), "query_range") {
			c.String(http.StatusOK, `{"status":"success","data":{"resultType":"matrix","result":[]}}`)
		} else {
			c.String(http.StatusOK, `{"status":"success","data":{"resultType":"vector","result":[]}}`)
		}
		return
	}

	clusterID := ""
	if len(getClusterID) > 0 && getClusterID[0] != nil {
		clusterID = normalizeClusterID(getClusterID[0](c))
		c.Request = c.Request.WithContext(contextWithClusterID(c.Request.Context(), clusterID))
	}

	var controlPlaneInfo string
	if getControlPlaneInfo != nil {
		controlPlaneInfo = getControlPlaneInfo(c)
	}
	if controlPlaneInfo != "" {
		o11yCluster, ok := handler.GetO11yClusterFromCache(c.Request.Context(), controlPlaneInfo)
		if !ok {
			_ = c.Error(apierr.ErrNotFound.New("o11y cluster info is not found"))
			return
		}
		handler.proxyCluster(c, o11yCluster, routerPathFunc)
		return
	}

	o11yClusterInfo := getO11yClusterInfo(c)
	log.Info(c.Request.Context(), "GinHandle1",
		zap.String("cluster_id", clusterID),
		zap.String("o11yClusterInfo", o11yClusterInfo))
	if o11yClusterInfo == "" {
		// return no data with success
		c.Header("Content-Type", "application/json")
		if strings.HasSuffix(c.FullPath(), "query_range") {
			c.String(http.StatusOK, `{"status":"success","data":{"resultType":"matrix","result":[]}}`)
		} else {
			c.String(http.StatusOK, `{"status":"success","data":{"resultType":"vector","result":[]}}`)
		}
		return
	}
	// o11yClusterInfo: project_id/vendor/region/o11y_cluster_id
	o11yClusterInfo, err := url.QueryUnescape(o11yClusterInfo)
	log.Info(c.Request.Context(), "GinHandle2",
		zap.String("cluster_id", clusterID),
		zap.String("o11yClusterInfo", o11yClusterInfo))
	if err != nil {
		_ = c.Error(apierr.ErrBadRequest.New("cluster info is invalid"))
	}
	info := strings.Split(o11yClusterInfo, "/")
	if len(info) != 4 {
		log.Warn(c.Request.Context(), "GinHandle3", zap.String("o11yClusterInfo", o11yClusterInfo))
		_ = c.Error(apierr.ErrBadRequest.New("cluster info is invalid"))
		return
	}
	// if getO11yInternalReadURI != nil {
	// 	if uri := getO11yInternalReadURI(c); uri != "" {
	// 		handler.proxyTargetURI(c, uri, info[3], routerPathFunc)
	// 		return
	// 	}
	// }
	// info[0] is project id
	handler.proxy(c, info[1], info[2], info[3], controlPlaneInfo, routerPathFunc)
}

func (handler *O11yProxyHandler) proxyCluster(c *gin.Context, cluster *o11y.ClusterInfo, routerPathFunc func(string) string) {
	if cluster == nil {
		_ = c.Error(apierr.ErrNotFound.New("o11y cluster info is not found"))
		return
	}
	if cluster.InternalReadUri == "" {
		_ = c.Error(apierr.ErrInternal.New("o11y cluster internal read uri is empty"))
		return
	}
	internalReadURI, err := url.Parse(cluster.InternalReadUri)
	if err != nil {
		_ = c.Error(apierr.ErrInternal.Wrap(err, "parse o11y internal read uri failed"))
		return
	}
	if internalReadURI.Host == "" {
		_ = c.Error(apierr.ErrInternal.New("o11y cluster internal read uri host is empty"))
		return
	}

	scheme := internalReadURI.Scheme
	if scheme == "" {
		scheme = "https"
	}
	targetHost := internalReadURI.Host
	log.Info(c.Request.Context(), "proxy o11y by internalReadUri",
		zap.String("cluster_id", clusterIDFromContext(c.Request.Context())),
		zap.String("targetHost", targetHost),
		zap.String("o11yClusterID", cluster.ID),
		zap.String("internalReadUri", cluster.InternalReadUri))

	requestModifyFunc := buildModifyFuncByInternalReadURI(targetHost, internalReadURI.Path, internalReadURI.RawPath, routerPathFunc)
	respModifyFunc := func(resp *http.Response) error { resp.Header.Set("X-Metrics-From", "o11y"); return nil }
	proxyModifyResponse(c.Request.Context(), c, scheme, targetHost, requestModifyFunc, respModifyFunc)
}

func buildModifyFuncByInternalReadURI(
	dataServiceHost, internalReadPath, internalReadRawPath string,
	routerPath func(string) string,
) func(r *http.Request) *http.Request {
	return func(r *http.Request) *http.Request {
		stripMetricsScopeParam(r)
		r.URL.Path = joinInternalReadPath(internalReadPath, routerPath(r.URL.Path))
		if internalReadRawPath != "" {
			r.URL.RawPath = joinInternalReadPath(internalReadRawPath, routerPath(r.URL.RawPath))
		} else {
			r.URL.RawPath = ""
		}
		log.Info(r.Context(), "forward to o11y",
			zap.String("host", dataServiceHost),
			zap.String("path", r.URL.Path),
			zap.String("url", r.URL.String()))
		r.Host = dataServiceHost
		return r
	}
}

func joinInternalReadPath(internalReadPath, originPath string) string {
	base := "/" + strings.Trim(internalReadPath, "/")
	suffix := strings.TrimPrefix(originPath, "/")
	suffix = strings.TrimPrefix(suffix, "api/v1")
	suffix = strings.TrimPrefix(suffix, "/")
	if suffix == "" {
		return base
	}
	return path.Join(base, suffix)
}

func (handler *O11yProxyHandler) proxy(c *gin.Context,
	vendor,
	region,
	o11yClusterID,
	controlPlaneInfo string,
	routerPathFunc func(string2 string) string,
) {
	// control panel use prod o11y server
	o11yPathV2 := false
	if controlPlaneInfo != "" {
		_, _, _, seedRegion, err := parseControlPlaneInfoV2(controlPlaneInfo)
		if err != nil {
			log.Error(c.Request.Context(), "failed to parse control plane info v2", zap.Error(err))
		}
		log.Info(c.Request.Context(), "proxy1",
			zap.String("cluster_id", clusterIDFromContext(c.Request.Context())),
			zap.String("vendor", vendor),
			zap.String("seedRegion", seedRegion))
		if vendor == "aws" {
			if strings.HasSuffix(seedRegion, "-ng") {
				// use -premium o11y region
				if region == "ap-south-1" {
					// ap-south-1 keeps the raw region without any premium/ng suffix.
				} else if region == "ap-southeast-2" {
					region += "-ng"
				} else {
					region += "-premium"
				}
				o11yPathV2 = true
			} else {
				if region == "us-west-2" && handler.env != "prod" {
					region += "-staging"
				}
			}
		}
	}
	schema := "https"
	targetHost := handler.o11yCli.GenDataServiceProxyHost(region, vendor)
	if vendor == "aws" && region == "us-east-1" && handler.env == "dev" && (o11yClusterID == "e8eae62d-c9e0-4520-afcb-15e5180354d6" || o11yClusterID == "019cc673-ecca-7928-9ce4-5fd1b8113784") {
		schema = "https"
		targetHost = "www.ds.us-east-1-serverless.aws.observability.tidbcloud.com"
	}
	log.Info(c.Request.Context(), "proxy2",
		zap.String("cluster_id", clusterIDFromContext(c.Request.Context())),
		zap.String("targetHost", targetHost),
		zap.String("o11yClusterID", o11yClusterID))
	requestModifyFunc := buildModifyFuncByO11yID(handler.o11yCli, targetHost, o11yClusterID, routerPathFunc, o11yPathV2)
	respModifyFunc := func(resp *http.Response) error { resp.Header.Set("X-Metrics-From", "o11y"); return nil }
	proxyModifyResponse(c.Request.Context(), c, schema, targetHost, requestModifyFunc, respModifyFunc)
}

// func (handler *O11yProxyHandler) proxyTargetURI(c *gin.Context,
// 	targetURI string,
// 	o11yClusterID string,
// 	routerPathFunc func(string2 string) string,
// ) {
// 	u, err := url.Parse(targetURI)
// 	if err != nil {
// 		log.Error("parse targetURI failed", zap.String("targetURI", targetURI), zap.Error(err))
// 	}
// 	if strings.Contains(u.Path, "internal/v2/metrics") {
// 		u.Path = handler.o11yCli.GenDataServiceProxyPathV2(o11yClusterID, routerPathFunc(u.Path))
// 		u.RawPath = handler.o11yCli.GenDataServiceProxyPathV2(o11yClusterID, routerPathFunc(u.RawPath))
// 	} else {
// 		u.Path = handler.o11yCli.GenDataServiceProxyPath(o11yClusterID, routerPathFunc(u.Path))
// 		u.RawPath = handler.o11yCli.GenDataServiceProxyPath(o11yClusterID, routerPathFunc(u.RawPath))
// 	}
// 	log.Info("proxyTargetHost1", zap.String("targetURI", targetURI))
// 	requestModifyFunc := func(r *http.Request) *http.Request {
// 		r.Host = u.Host // N.B. o11y load balancer router on Host Header, we should replace request.Host with the target host before proxying.
// 		r.URL = u
// 		log.Info("forward to o11y",
// 			zap.String("path", r.URL.Path),
// 			zap.String("url", r.URL.String()))
// 		return r
// 	}
// 	respModifyFunc := func(resp *http.Response) error { resp.Header.Set("X-Metrics-From", "o11y"); return nil }
// 	proxyModifyResponse(c, "http", u.Host, requestModifyFunc, respModifyFunc)
// }

func (handler *O11yProxyHandler) SetClusterInfoCache(cache *gocache.Cache) {
	handler.o11yClusterInfoCache = cache

	// init cache
	if handler.o11yCli != nil {
		handler.InitClusterInfoCache(context.Background())
	}
}

func (handler *O11yProxyHandler) setO11yClusterInfoToCache(clusters []o11y.ClusterInfo) {
	for _, cls := range clusters {
		controlPlaneInfo := fmt.Sprintf("%s/%s-%s/%s", cls.ProjectID, cls.Vendor, cls.Region, cls.ApplicationID)

		if controlPlaneInfo == "" {
			log.PlainError("cache o11y cluster info failed", zap.Any("cluster", cls))
			continue
		}
		// cached
		if _, ok := handler.o11yClusterInfoCache.Get(controlPlaneInfo); ok {
			continue
		}
		handler.o11yClusterInfoCache.Set(controlPlaneInfo, cls, o11yClusterInfoCacheTTL)
	}
}

func (handler *O11yProxyHandler) GetO11yClusterFromCache(ctx context.Context, controlPlaneInfo string) (*o11y.ClusterInfo, bool) {
	if controlPlaneInfo == "" {
		return nil, false
	}

	item, found := handler.o11yClusterInfoCache.Get(controlPlaneInfo)
	if !found {
		handler.InitClusterInfoCache(ctx)
		item, found = handler.o11yClusterInfoCache.Get(controlPlaneInfo)
	}

	if !found || item == nil {
		return nil, false
	}
	switch data := item.(type) {
	case o11y.ClusterInfo:
		return &data, true
	case *o11y.ClusterInfo:
		return data, data != nil
	default:
		return nil, false
	}
}

func (handler *O11yProxyHandler) GetO11yClusterInfoFromCache(ctx context.Context, controlPlaneInfo string) (string, bool) {
	if controlPlaneInfo == "" {
		return "", false
	}

	item, found := handler.o11yClusterInfoCache.Get(controlPlaneInfo)
	if found {
		if data, ok := item.(string); ok {
			return data, true
		}
	}
	cluster, ok := handler.GetO11yClusterFromCache(ctx, controlPlaneInfo)
	if !ok {
		return "", false
	}
	return fmt.Sprintf("%s/%s/%s/%s", cluster.ProjectID, cluster.Vendor, cluster.Region, cluster.ID), true
}

func (handler *O11yProxyHandler) InitClusterInfoCache(ctx context.Context) {
	clusters, err := handler.ListGlobalClusterInfo(ctx)
	if err != nil {
		log.PlainError("list o11y global clusters failed", zap.Error(err))
		return
	}

	handler.setO11yClusterInfoToCache(clusters)
}

func (handler *O11yProxyHandler) ListGlobalClusterInfo(ctx context.Context) ([]o11y.ClusterInfo, error) {
	if handler.o11yCli == nil {
		return nil, nil
	}

	clusters := []o11y.ClusterInfo{}

	if cls, err := handler.ListDevTierClusterInfo(ctx); err != nil {
		return clusters, err
	} else {
		clusters = append(clusters, cls...)
	}

	if cls, err := handler.ListBaseClusterInfo(ctx); err != nil {
		return clusters, err
	} else {
		clusters = append(clusters, cls...)
	}

	if cls, err := handler.ListSeedClusterInfo(ctx); err != nil {
		return clusters, err
	} else {
		clusters = append(clusters, cls...)
	}

	if cls, err := handler.ListNextGenSeedClusterInfo(ctx); err != nil {
		return clusters, err
	} else {
		clusters = append(clusters, cls...)
	}
	return clusters, nil
}

func (handler *O11yProxyHandler) ListDevTierClusterInfo(ctx context.Context) ([]o11y.ClusterInfo, error) {
	if handler.o11yCli == nil {
		return nil, nil
	}
	data, err := handler.o11yCli.ListGlobalClusters(ctx, &o11y.ListGlobalClusterParam{
		BizType:       handler.o11yCli.DevTierBizTypeWithEnv(),
		ApplicationID: o11y.GlobalDevtierID,
	})
	if err != nil {
		log.PlainError("list global devtier cluster via o11y failed", zap.Error(err))
		return nil, err
	}
	return data.Clusters, nil
}

// ListBaseClusterInfo
func (handler *O11yProxyHandler) ListBaseClusterInfo(ctx context.Context) ([]o11y.ClusterInfo, error) {
	if handler.o11yCli == nil {
		return nil, nil
	}
	data, err := handler.o11yCli.ListGlobalClusters(ctx, &o11y.ListGlobalClusterParam{
		BizType:       handler.o11yCli.BaseShootBizTypeWithEnv(),
		ProjectID:     o11y.BaseID,
		ApplicationID: o11y.BaseID,
		TenantID:      handler.o11yCli.BaseShootTenantWithEnv(),
	})
	if err != nil {
		log.PlainError("get base cluster via o11y failed", zap.Error(err))
		return nil, err
	}
	return data.Clusters, nil
}

// ListSeedClusterInfo
func (handler *O11yProxyHandler) ListSeedClusterInfo(ctx context.Context) ([]o11y.ClusterInfo, error) {
	if handler.o11yCli == nil {
		return nil, nil
	}
	data, err := handler.o11yCli.ListGlobalClusters(ctx, &o11y.ListGlobalClusterParam{
		BizType:   handler.o11yCli.BaseShootBizTypeWithEnv(),
		ProjectID: o11y.SeedID,
		// ApplicationID: o11y.SeedID,
		TenantID: handler.o11yCli.BaseShootTenantWithEnv(),
	})
	if err != nil {
		log.PlainError("list seed cluster via o11y failed", zap.Error(err))
		return nil, err
	}
	return data.Clusters, nil
}

// ListNextGenSeedClusterInfo
func (handler *O11yProxyHandler) ListNextGenSeedClusterInfo(ctx context.Context) ([]o11y.ClusterInfo, error) {
	if handler.o11yCli == nil {
		return nil, nil
	}
	data, err := handler.o11yCli.ListGlobalClusters(ctx, &o11y.ListGlobalClusterParam{
		BizType:   handler.o11yCli.NextGenBizTypeWithEnv(),
		ProjectID: o11y.SeedID,
		// ApplicationID: o11y.SeedID,
		TenantID: handler.o11yCli.BaseShootTenantWithEnv(),
	})
	if err != nil {
		log.PlainError("list seed cluster via o11y failed", zap.Error(err))
		return nil, err
	}
	return data.Clusters, nil
}

// GetO11yClusterInfo
// try to get o11y cluster info from different sources
// 1. get from header 'control_plane_info'
// 2. get from promql variable 'control_plane_info'
// 3. get from promql variable 'o11y_cluster_info'
// 4. get from header 'o11y_cluster_info'
func (handler *O11yProxyHandler) GetO11yClusterInfo(c *gin.Context) string {
	o11yPromQL := GetO11yPromQL(c)
	o11yClusterInfo := ""

	controlPlaneInfo := c.GetHeader(ControlPlaneInfoVariable)
	if controlPlaneInfo != "" {
		o11yClusterInfo, _ = handler.GetO11yClusterInfoFromCache(c.Request.Context(), controlPlaneInfo)
	}

	if o11yClusterInfo == "" && o11yPromQL != "" {
		controlPlaneInfo = GetPromQLData(o11yPromQL, ControlPlaneInfoVariable)
		if controlPlaneInfo != "" {
			o11yClusterInfo, _ = handler.GetO11yClusterInfoFromCache(c.Request.Context(), controlPlaneInfo)
		}
	}

	if o11yClusterInfo == "" && o11yPromQL != "" {
		o11yClusterInfo = GetPromQLData(o11yPromQL, O11yClusterInfoVariable)
	}

	if o11yClusterInfo == "" {
		o11yClusterInfo = c.GetHeader(O11yClusterInfoVariable)
	}

	return o11yClusterInfo
}

// GetO11yPromQL retrieves the Prometheus Query Language (PromQL) from the given gin.Context.
//  1. It first checks for the "match[]" query parameter, and if present, returns its value.
//  2. If "match[]" is not found, it then checks for the "query" query parameter and returns its value if found.
//  3. If the HTTP request method is POST, it reads the raw request body and searches for a query parameter with the key "query".
//     If found, it returns the value of the "query" parameter.
//  4. If none of the above conditions are met, it returns the value of the "match[]" parameter obtained from the POST form.
func GetO11yPromQL(c *gin.Context) string {
	// grafana >= 9.0.0
	match := c.Query("match[]")
	if match != "" {
		return match
	}

	// explor
	match = c.Query("query")
	if match != "" {
		return match
	}

	if c.Request.Method == http.MethodPost {
		bodyByte, _ := c.GetRawData()
		if bodyByte != nil {
			c.Request.Body = io.NopCloser(bytes.NewReader(bodyByte))
			for _, q := range strings.Split(string(bodyByte), "&") {
				if strings.HasPrefix(q, "query=") {
					match, _ = url.QueryUnescape(strings.TrimPrefix(q, "query="))
					return match
				}
			}
		}
	}

	// grafan < 9.0.0
	return c.PostForm("match[]")
}

func GetPromQLData(promql, variable string) string {
	if promql == "" {
		return ""
	}

	count := 3
	if variable == ControlPlaneInfoVariable {
		count = 1
	}

	matcher := &labels.Matcher{
		Type:  labels.MatchEqual,
		Name:  O11yClusterInfoVariable,
		Value: "",
	}

	// prometheus parse
	queryExpr, err := parser.ParseExpr(promql)
	if err != nil {
		return ""
	}
	queryMatch := parser.ExtractSelectors(queryExpr)
	for _, query := range queryMatch {
		for _, m := range query {
			if m.Name == variable {
				matcher = m
			}
		}
	}

	switch matcher.Type {
	case labels.MatchEqual:
		return matcher.Value
	case labels.MatchRegexp:
		ms := strings.Split(matcher.Value, "|")
		for _, m := range ms {
			if strings.Count(m, "/") == count {
				return m
			}
		}
	default:
		return ""
	}

	return ""
}

func GetO11yAppID(match string) string {
	if match == "" {
		return ""
	}

	// e.g. match[]: {application_id="devtier-infra"}
	appIDRegex := regexp.MustCompile(`application_id="(.*?)"`)
	matchs := appIDRegex.FindStringSubmatch(match)

	if len(matchs) >= 2 {
		return matchs[1]
	}

	return ""
}

func GetO11yRegion(match string) string {
	if match == "" {
		return ""
	}

	// e.g. match[]: {application_id="devtier-infra"}
	regionRegex := regexp.MustCompile(`region="(.*?)"`)
	matchs := regionRegex.FindStringSubmatch(match)

	if len(matchs) >= 2 {
		return matchs[1]
	}

	return ""
}
