// Package store provides in-memory storage for mock data.
package store

import (
	"sync"

	dpmv1 "github.com/tidbcloud/cluster-service-ng/api/dataplane-manager/v1"
)

// MemoryStore provides thread-safe in-memory storage for mock resources.
type MemoryStore struct {
	mu sync.RWMutex

	// DataPlane resources
	dataplanes map[string]*dpmv1.DataPlane
	nodepools  map[string][]*dpmv1.NodePool // key: dataplane_id

	// Bucket resources
	buckets map[string]*dpmv1.Bucket // key: dataplane_id/bucket_id

	// LoadBalancer resources
	loadbalancers map[string]*dpmv1.LoadBalancer // key: dataplane_id/lb_id

	// PrivateLink resources
	privatelinkServices map[string]*dpmv1.PrivateLinkService // key: dataplane_id/service_id

	// NodePoolConfigs (static, pre-defined)
	nodePoolConfigs []*dpmv1.NodePoolConfig
}

// NewMemoryStore creates a new in-memory store with pre-seeded data.
func NewMemoryStore() *MemoryStore {
	s := &MemoryStore{
		dataplanes:          make(map[string]*dpmv1.DataPlane),
		nodepools:           make(map[string][]*dpmv1.NodePool),
		buckets:             make(map[string]*dpmv1.Bucket),
		loadbalancers:       make(map[string]*dpmv1.LoadBalancer),
		privatelinkServices: make(map[string]*dpmv1.PrivateLinkService),
		nodePoolConfigs:     defaultNodePoolConfigs(),
	}
	return s
}

// defaultNodePoolConfigs returns pre-defined node pool configurations.
func defaultNodePoolConfigs() []*dpmv1.NodePoolConfig {
	return []*dpmv1.NodePoolConfig{
		{
			InstanceType:          "m5.xlarge",
			CpuCore:               4,
			AvailableCpuMilliCore: 3500,
			MemoryGi:              8,
			AvailableMemoryMi:     7168,
			NodeType:              dpmv1.NodeType_Dedicated,
			WithLocalDisk:         false,
		},
	}
}

// Reset clears all stored data and re-initializes with defaults.
func (s *MemoryStore) Reset() {
	s.mu.Lock()
	defer s.mu.Unlock()

	s.dataplanes = make(map[string]*dpmv1.DataPlane)
	s.nodepools = make(map[string][]*dpmv1.NodePool)
	s.buckets = make(map[string]*dpmv1.Bucket)
	s.loadbalancers = make(map[string]*dpmv1.LoadBalancer)
	s.privatelinkServices = make(map[string]*dpmv1.PrivateLinkService)
	s.nodePoolConfigs = defaultNodePoolConfigs()
}

// Dump returns a snapshot of all stored data.
func (s *MemoryStore) Dump() map[string]interface{} {
	s.mu.RLock()
	defer s.mu.RUnlock()

	return map[string]interface{}{
		"dataplanes":          s.dataplanes,
		"nodepools":           s.nodepools,
		"buckets":             s.buckets,
		"loadbalancers":       s.loadbalancers,
		"privatelinkServices": s.privatelinkServices,
		"nodePoolConfigs":     s.nodePoolConfigs,
	}
}

// DataPlane operations

func (s *MemoryStore) GetDataPlane(id string) (*dpmv1.DataPlane, bool) {
	s.mu.RLock()
	defer s.mu.RUnlock()
	dp, ok := s.dataplanes[id]
	return dp, ok
}

func (s *MemoryStore) SetDataPlane(dp *dpmv1.DataPlane) {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.dataplanes[dp.Id] = dp
}

func (s *MemoryStore) ListDataPlanes() []*dpmv1.DataPlane {
	s.mu.RLock()
	defer s.mu.RUnlock()
	result := make([]*dpmv1.DataPlane, 0, len(s.dataplanes))
	for _, dp := range s.dataplanes {
		result = append(result, dp)
	}
	return result
}

func (s *MemoryStore) DeleteDataPlane(id string) {
	s.mu.Lock()
	defer s.mu.Unlock()
	delete(s.dataplanes, id)
	delete(s.nodepools, id)
}

// NodePool operations

func (s *MemoryStore) GetNodePools(dataplaneID string) []*dpmv1.NodePool {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return s.nodepools[dataplaneID]
}

func (s *MemoryStore) AddNodePool(dataplaneID string, np *dpmv1.NodePool) {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.nodepools[dataplaneID] = append(s.nodepools[dataplaneID], np)
}

func (s *MemoryStore) DeleteNodePool(dataplaneID, nodePoolID string) {
	s.mu.Lock()
	defer s.mu.Unlock()
	if nodePoolID == "" {
		return
	}
	pools := s.nodepools[dataplaneID]
	filtered := make([]*dpmv1.NodePool, 0, len(pools))
	for _, p := range pools {
		if p.GetName() == nodePoolID {
			continue
		}
		filtered = append(filtered, p)
	}
	s.nodepools[dataplaneID] = filtered
}

func (s *MemoryStore) GetNodePoolConfigs() []*dpmv1.NodePoolConfig {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return s.nodePoolConfigs
}

// Bucket operations

func (s *MemoryStore) GetBucket(dataplaneID, bucketID string) (*dpmv1.Bucket, bool) {
	s.mu.RLock()
	defer s.mu.RUnlock()
	key := dataplaneID + "/" + bucketID
	b, ok := s.buckets[key]
	return b, ok
}

func (s *MemoryStore) SetBucket(dataplaneID string, bucket *dpmv1.Bucket) {
	s.mu.Lock()
	defer s.mu.Unlock()
	key := dataplaneID + "/" + bucket.Id
	s.buckets[key] = bucket
}

func (s *MemoryStore) ListBuckets(dataplaneID string) []*dpmv1.Bucket {
	s.mu.RLock()
	defer s.mu.RUnlock()
	result := make([]*dpmv1.Bucket, 0)
	prefix := dataplaneID + "/"
	for key, b := range s.buckets {
		if len(key) > len(prefix) && key[:len(prefix)] == prefix {
			result = append(result, b)
		}
	}
	return result
}

func (s *MemoryStore) DeleteBucket(dataplaneID, bucketID string) {
	s.mu.Lock()
	defer s.mu.Unlock()
	key := dataplaneID + "/" + bucketID
	delete(s.buckets, key)
}

// LoadBalancer operations

func (s *MemoryStore) GetLoadBalancer(dataplaneID, lbID string) (*dpmv1.LoadBalancer, bool) {
	s.mu.RLock()
	defer s.mu.RUnlock()
	key := dataplaneID + "/" + lbID
	lb, ok := s.loadbalancers[key]
	return lb, ok
}

func (s *MemoryStore) SetLoadBalancer(dataplaneID string, lb *dpmv1.LoadBalancer) {
	s.mu.Lock()
	defer s.mu.Unlock()
	key := dataplaneID + "/" + lb.Id
	s.loadbalancers[key] = lb
}

func (s *MemoryStore) DeleteLoadBalancer(dataplaneID, lbID string) {
	s.mu.Lock()
	defer s.mu.Unlock()
	key := dataplaneID + "/" + lbID
	delete(s.loadbalancers, key)
}

// PrivateLinkService operations

func (s *MemoryStore) GetPrivateLinkService(dataplaneID, serviceID string) (*dpmv1.PrivateLinkService, bool) {
	s.mu.RLock()
	defer s.mu.RUnlock()
	key := dataplaneID + "/" + serviceID
	svc, ok := s.privatelinkServices[key]
	return svc, ok
}

func (s *MemoryStore) SetPrivateLinkService(dataplaneID string, svc *dpmv1.PrivateLinkService) {
	s.mu.Lock()
	defer s.mu.Unlock()
	key := dataplaneID + "/" + svc.Id
	s.privatelinkServices[key] = svc
}

func (s *MemoryStore) DeletePrivateLinkService(dataplaneID, serviceID string) {
	s.mu.Lock()
	defer s.mu.Unlock()
	key := dataplaneID + "/" + serviceID
	delete(s.privatelinkServices, key)
}
