package service

import (
	"log/slog"
	"net/http"
	"sync"
	"time"

	"github.com/tidbcloud/config-center/internal/db"
	ccch "github.com/tidbcloud/config-center/pkg/http_client"
	pb "github.com/tidbcloud/config-center/proto/config_center"
)

type ConfigCenterService struct {
	DB           db.DB
	ServerRegion string // only set in regional mode

	mu              sync.RWMutex
	cachedClients   map[pb.ClusterType]map[string]*ccch.ConfigCenterHTTPClient
	cacheExpireTime time.Time
}

const regionCacheTTL = 5 * time.Second

// before migrate to TemplateService, use global variable
var ServerRegion string

// GetRegionalClients returns regional clients for the given cluster type.
// It caches the full client map from DB and refreshes every regionCacheTTL.
func (s *ConfigCenterService) GetRegionalClients(clusterType pb.ClusterType) map[string]*ccch.ConfigCenterHTTPClient {
	s.mu.RLock()
	if s.cachedClients != nil && time.Now().Before(s.cacheExpireTime) {
		clients := s.cachedClients[clusterType]
		s.mu.RUnlock()
		return clients
	}
	s.mu.RUnlock()

	s.mu.Lock()
	defer s.mu.Unlock()
	// Re-check after upgrading to the write lock: another goroutine may have refreshed.
	if s.cachedClients != nil && time.Now().Before(s.cacheExpireTime) {
		return s.cachedClients[clusterType]
	}

	routers, err := s.DB.ListRegionalServices()
	if err != nil {
		slog.Error("failed to list regional services from DB, using stale cache", "error", err)
		if s.cachedClients != nil {
			return s.cachedClients[clusterType]
		}
		return nil
	}

	clients := make(map[pb.ClusterType]map[string]*ccch.ConfigCenterHTTPClient)
	for clusterTypeID := range pb.ClusterType_name {
		clients[pb.ClusterType(clusterTypeID)] = make(map[string]*ccch.ConfigCenterHTTPClient)
	}
	for _, router := range routers {
		ct := pb.ClusterType(pb.ClusterType_value[router.ClusterType])
		clients[ct][router.LogicalRegion] = &ccch.ConfigCenterHTTPClient{
			HTTPClient: http.DefaultClient,
			BaseURL:    router.Endpoint,
		}
	}

	s.cachedClients = clients
	s.cacheExpireTime = time.Now().Add(regionCacheTTL)

	return s.cachedClients[clusterType]
}

// GetRegionalClient returns a single regional client for routing.
func (s *ConfigCenterService) GetRegionalClient(clusterType pb.ClusterType, region string) *ccch.ConfigCenterHTTPClient {
	clients := s.GetRegionalClients(clusterType)
	if clients == nil {
		return nil
	}
	return clients[region]
}

// InvalidateRegionalCache forces the next GetRegionalClients call to re-read from DB.
func (s *ConfigCenterService) InvalidateRegionalCache() {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.cacheExpireTime = time.Time{}
}
