package managerserver

import (
	"context"
	"encoding/json"
	"io"
	"net/http"
	"sort"

	"github.com/go-logr/logr"
	pkginfra "github.com/tidbcloud/aws-shared-provider/pkg/infra"
	"github.com/tidbcloud/aws-shared-provider/pkg/tidbmanager"
	"github.com/tidbcloud/aws-shared-provider/pkg/utils"
	ep "github.com/tidbcloud/aws-shared-provider/pkg/utils/endpoint"
)

const (
	activeTiDBPort       = 4000
	activeTiDBStatusPort = 10080
)

type activeTiDBAddress struct {
	KeyspaceName string `json:"keyspace_name"`
	Hostname     string `json:"hostname"`
	Port         int    `json:"port"`
	Host         string `json:"host"`
	StatusPort   int    `json:"status_port"`
}

// `getActiveTiDB` ONLY returns the active tidb pods of essential (RCU-backed)
// clusters.
func (s *server) getActiveTiDB(w http.ResponseWriter, r *http.Request) {
	logger := log.WithName("tidb/get_active_tidb")
	logger.Info("req param", "req", r.URL)

	keep := s.essentialTokenFilter(r.Context(), logger)
	activeAddresses := collectActiveTiDBAddresses(s.tManagers, keep)

	jsonData, err := json.Marshal(activeAddresses)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.Header().Set("Content-Type", "application/json")
	_, _ = io.WriteString(w, string(jsonData))
}

// essentialTokenFilter returns a predicate reporting whether a token belongs to
// an essential cluster. Meta lookups hit the infra informer cache and are
// memoized per call; tokens whose meta cannot be resolved are treated as
// non-essential and skipped.
func (s *server) essentialTokenFilter(ctx context.Context, logger logr.Logger) func(token string) bool {
	regionalInfra := pkginfra.NewRegionalInfraWithClient(s.infraClusterCli, s.cfg.KubeClusterName)
	cache := map[string]bool{}

	return func(token string) bool {
		trimmed := utils.TrimToken(token)
		if essential, ok := cache[trimmed]; ok {
			return essential
		}

		meta, err := pkginfra.GetClusterOrBranchMeta(ctx, regionalInfra, s.infraApi, trimmed, s.cfg.KubeClusterName)
		if err != nil {
			logger.V(2).Info("skip token, fail to get meta", "token", token, "err", err)
			cache[trimmed] = false
			return false
		}

		essential := meta.IsEssential()
		cache[trimmed] = essential
		return essential
	}
}

// collectActiveTiDBAddresses walks the active assign cache of every target manager
// and returns the deduplicated, host-sorted addresses whose token passes keep.
func collectActiveTiDBAddresses(
	targetManagers map[string]tidbmanager.TidbManager,
	keep func(token string) bool,
) []activeTiDBAddress {
	addressMap := map[string]activeTiDBAddress{}

	for _, tManager := range targetManagers {
		if tManager == nil {
			continue
		}

		tManager.WalkActiveTokenFromAssignCache(func(token string, endpoints []string, _ int) bool {
			if !keep(token) {
				return true
			}
			for _, endpoint := range endpoints {
				podName, podIP := ep.ParsePodFromEndpoint(endpoint)
				if podIP == "" {
					continue
				}
				addressMap[podIP] = activeTiDBAddress{
					KeyspaceName: utils.TrimToken(token),
					Hostname:     podName,
					Port:         activeTiDBPort,
					Host:         podIP,
					StatusPort:   activeTiDBStatusPort,
				}
			}
			return true
		})
	}

	hosts := make([]string, 0, len(addressMap))
	for host := range addressMap {
		hosts = append(hosts, host)
	}
	sort.Strings(hosts)

	activeAddresses := make([]activeTiDBAddress, 0, len(hosts))
	for _, host := range hosts {
		activeAddresses = append(activeAddresses, addressMap[host])
	}
	return activeAddresses
}
