package query

import (
	"context"
	"fmt"
	"regexp"
	"strings"
)

// uuid-like o11y cluster names (e.g. 019b23f6-ffc9-731d-be49-eb8f9e651855).
var o11yClusterIDRe = regexp.MustCompile(`(?i)^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`)

// resolveSlowlogsClusterID maps user-facing TiDB Cloud cluster id(s) (applicationID)
// in scope.cluster to o11y cluster UUID(s) used under
// deltalake/{org}/{o11yID}/slowlogs — same conversion as slowquery DuckDB via MetaCache.
//
// Supports:
//   - single string: applicationID → UUID; UUID left as-is
//   - string array (IN): each entry resolved independently (typical after
//     POST /olap/scope/list field=cluster, which returns o11y UUIDs)
//
// When MetaCache is nil (unit tests / no kube), values are left unchanged.
func (s *Service) resolveSlowlogsClusterID(ctx context.Context, hdr clusterHeader, scope Scope) error {
	if s == nil || s.meta == nil || scope == nil {
		return nil
	}
	v, key, ok := scope.lookup("cluster")
	if !ok {
		return nil
	}

	tenant := strings.TrimSpace(hdr.TenantID)
	if org, ok := scope.GetString("org"); ok && strings.TrimSpace(org) != "" {
		tenant = strings.TrimSpace(org)
	}

	switch {
	case v.eq != nil:
		raw := strings.TrimSpace(*v.eq)
		if raw == "" || looksLikeO11yClusterID(raw) {
			return nil
		}
		o11yID, err := s.lookupO11yClusterID(ctx, hdr, tenant, raw)
		if err != nil {
			return err
		}
		scope[key] = ScopeEq(o11yID)
		return nil

	case len(v.in) > 0:
		out := make([]string, 0, len(v.in))
		for _, raw := range v.in {
			raw = strings.TrimSpace(raw)
			if raw == "" {
				continue
			}
			if looksLikeO11yClusterID(raw) {
				out = append(out, raw)
				continue
			}
			o11yID, err := s.lookupO11yClusterID(ctx, hdr, tenant, raw)
			if err != nil {
				return err
			}
			out = append(out, o11yID)
		}
		if len(out) == 0 {
			return fmt.Errorf("scope.cluster array must not be empty")
		}
		if len(out) == 1 {
			scope[key] = ScopeEq(out[0])
		} else {
			scope[key] = ScopeIn(out...)
		}
		return nil

	default:
		// ranges unsupported for cluster id resolution
		return nil
	}
}

func (s *Service) lookupO11yClusterID(ctx context.Context, hdr clusterHeader, tenant, applicationID string) (string, error) {
	if tenant == "" {
		return "", fmt.Errorf("scope.org or x-org-id is required to resolve cluster id %q to o11y cluster id", applicationID)
	}

	env := strings.TrimSpace(hdr.Env)
	if env == "" {
		env = "prod"
	}
	deploy := strings.ToLower(strings.TrimSpace(hdr.DeployType))
	biz := strings.ToLower(strings.TrimSpace(hdr.BizType))
	if deploy == "byoc" {
		biz = "byoc"
		deploy = "nextgen"
	}
	nextGen := deploy == "nextgen" || deploy == "premium"
	byoc := biz == "byoc"
	region := strings.TrimSpace(hdr.Region)
	if region == "" {
		region = s.cfg.Region
	}
	project := strings.TrimSpace(hdr.ProjectID)

	info, err := s.meta.GetMetaInfo(ctx, env, region, tenant, project, applicationID, nextGen, byoc)
	if err != nil {
		return "", fmt.Errorf("resolve cluster id %q to o11y cluster id: %w", applicationID, err)
	}
	if info == nil || strings.TrimSpace(info.O11yID) == "" {
		return "", fmt.Errorf("o11y cluster id not found for cluster id %q (tenant %s)", applicationID, tenant)
	}
	return strings.TrimSpace(info.O11yID), nil
}

func looksLikeO11yClusterID(s string) bool {
	return o11yClusterIDRe.MatchString(strings.TrimSpace(s))
}
