//go:build with_manager

package manager

import (
	"context"
	"strconv"
	"sync"
	"time"

	"github.com/go-playground/validator/v10"
	"github.com/gofrs/uuid/v5"
	"github.com/sagernet/sing-box/adapter"
	boxService "github.com/sagernet/sing-box/adapter/service"
	C "github.com/sagernet/sing-box/constant"
	"github.com/sagernet/sing-box/log"
	"github.com/sagernet/sing-box/option"
	"github.com/sagernet/sing-box/service/manager/constant"
	"github.com/sagernet/sing-box/service/manager/repository/postgresql"
	"github.com/sagernet/sing-box/service/manager/repository/sqlite"
	E "github.com/sagernet/sing/common/exceptions"
	"github.com/shtorm-7/go-cache/v2"
)

func RegisterService(registry *boxService.Registry) {
	boxService.Register[option.ManagerServiceOptions](registry, C.TypeManager, NewService)
}

type Service struct {
	boxService.Adapter
	ctx        context.Context
	logger     log.ContextLogger
	repository constant.Repository
	nodes      map[string]constant.ConnectedNode

	limiterLocks map[int]map[string]*cache.Cache[string, struct{}]
	trafficUsage map[int]*TrafficUsage

	defaultValidator *validator.Validate

	mtx         sync.RWMutex
	connLockMtx sync.Mutex
	trafficMtx  sync.Mutex
}

func NewService(ctx context.Context, logger log.ContextLogger, tag string, options option.ManagerServiceOptions) (adapter.Service, error) {
	var repository constant.Repository
	var err error
	switch options.Database.Driver {
	case "postgresql":
		repository, err = postgresql.NewPostgreSQLRepository(ctx, options.Database.DSN)
		if err != nil {
			return nil, err
		}
	case "sqlite":
		repository, err = sqlite.NewSQLiteRepository(ctx, options.Database.DSN)
		if err != nil {
			return nil, err
		}
	default:
		return nil, E.New("unknown driver \"", options.Database.Driver, "\"")
	}
	defaultValidator := validator.New()
	defaultValidator.RegisterStructValidation(func(sl validator.StructLevel) {
		user := sl.Current().Interface().(constant.UserCreate)
		switch user.Type {
		case "vless":
			if user.UUID == "" {
				sl.ReportError(user.UUID, "uuid", "UUID", "required", "")
			}
		case "vmess":
			if user.UUID == "" {
				sl.ReportError(user.UUID, "uuid", "UUID", "required", "")
			}
			if user.AlterID == 0 {
				sl.ReportError(user.AlterID, "alter_id", "AlterID", "required", "")
			}
		case "trojan", "shadowsocks", "hysteria", "hysteria2":
			if user.Password == "" {
				sl.ReportError(user.Password, "password", "Password", "required", "")
			}
		case "tuic":
			if user.UUID == "" {
				sl.ReportError(user.UUID, "uuid", "UUID", "required", "")
			}
			if user.Password == "" {
				sl.ReportError(user.Password, "password", "Password", "required", "")
			}
		case "mtproxy":
			if user.Secret == "" {
				sl.ReportError(user.Secret, "secret", "Secret", "required", "")
			}
		}
	}, constant.UserCreate{})
	validateRateLimiterInterval := func(sl validator.StructLevel, interval string) {
		if interval == "" {
			return
		}
		if _, err := time.ParseDuration(interval); err != nil {
			sl.ReportError(interval, "interval", "Interval", "duration", "")
		}
	}
	defaultValidator.RegisterStructValidation(func(sl validator.StructLevel) {
		validateRateLimiterInterval(sl, sl.Current().Interface().(constant.RateLimiterCreate).Interval)
	}, constant.RateLimiterCreate{})
	defaultValidator.RegisterStructValidation(func(sl validator.StructLevel) {
		validateRateLimiterInterval(sl, sl.Current().Interface().(constant.RateLimiterUpdate).Interval)
	}, constant.RateLimiterUpdate{})
	service := &Service{
		Adapter:          boxService.NewAdapter(C.TypeManager, tag),
		ctx:              ctx,
		logger:           logger,
		repository:       repository,
		nodes:            make(map[string]constant.ConnectedNode, 0),
		limiterLocks:     make(map[int]map[string]*cache.Cache[string, struct{}]),
		trafficUsage:     make(map[int]*TrafficUsage),
		defaultValidator: defaultValidator,
	}
	limiters, err := service.repository.GetTrafficLimiters(map[string][]string{})
	if err != nil {
		return nil, err
	}
	for _, limiter := range limiters {
		service.trafficUsage[limiter.ID] = &TrafficUsage{
			used:  limiter.RawUsed,
			quota: limiter.RawQuota,
		}
	}
	return service, nil
}

func (s *Service) CreateSquad(node constant.SquadCreate) (constant.Squad, error) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	err := s.defaultValidator.Struct(node)
	if err != nil {
		return constant.Squad{}, err
	}
	createdSquad, err := s.repository.CreateSquad(node)
	if err != nil {
		return createdSquad, err
	}
	return createdSquad, nil
}

func (s *Service) GetSquads(filters map[string][]string) ([]constant.Squad, error) {
	return s.repository.GetSquads(filters)
}

func (s *Service) GetSquadsCount(filters map[string][]string) (int, error) {
	return s.repository.GetSquadsCount(filters)
}

func (s *Service) GetSquad(id int) (constant.Squad, error) {
	return s.repository.GetSquad(id)
}

func (s *Service) UpdateSquad(id int, squad constant.SquadUpdate) (constant.Squad, error) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	err := s.defaultValidator.Struct(squad)
	if err != nil {
		return constant.Squad{}, err
	}
	updatedSquad, err := s.repository.UpdateSquad(id, squad)
	if err != nil {
		return updatedSquad, err
	}
	return updatedSquad, nil
}

func (s *Service) DeleteSquad(id int) (constant.Squad, error) {
	s.mtx.Lock()
	s.trafficMtx.Lock()
	s.connLockMtx.Lock()
	defer s.mtx.Unlock()
	defer s.trafficMtx.Unlock()
	defer s.connLockMtx.Unlock()
	deleted, err := s.repository.DeleteSquad(id)
	if err != nil {
		return deleted.Squad, err
	}
	for _, uuid := range deleted.OrphanedNodeUUIDs {
		if connectedNode, ok := s.nodes[uuid]; ok {
			connectedNode.Close()
			delete(s.nodes, uuid)
		}
	}
	for _, lid := range deleted.OrphanedTrafficLimiterIDs {
		delete(s.trafficUsage, lid)
	}
	for _, lid := range deleted.OrphanedConnectionLimiterIDs {
		delete(s.limiterLocks, lid)
	}
	for _, uuid := range deleted.SurvivingNodeUUIDs {
		connectedNode, ok := s.nodes[uuid]
		if !ok {
			continue
		}
		node, err := s.repository.GetNode(uuid)
		if err != nil {
			s.closeAllNodes()
			return deleted.Squad, err
		}
		squadIDs := convertIntSliceToStringSlice(node.SquadIDs)
		users, err := s.repository.GetUsers(map[string][]string{"squad_id_in": squadIDs})
		if err != nil {
			s.closeAllNodes()
			return deleted.Squad, err
		}
		connectedNode.UpdateUsers(users)
		bandwidthLimiters, err := s.repository.GetBandwidthLimiters(map[string][]string{"squad_id_in": squadIDs})
		if err != nil {
			s.closeAllNodes()
			return deleted.Squad, err
		}
		connectedNode.UpdateBandwidthLimiters(bandwidthLimiters)
		trafficLimiters, err := s.repository.GetTrafficLimiters(map[string][]string{"squad_id_in": squadIDs})
		if err != nil {
			s.closeAllNodes()
			return deleted.Squad, err
		}
		connectedNode.UpdateTrafficLimiters(trafficLimiters)
		connectionLimiters, err := s.repository.GetConnectionLimiters(map[string][]string{"squad_id_in": squadIDs})
		if err != nil {
			s.closeAllNodes()
			return deleted.Squad, err
		}
		connectedNode.UpdateConnectionLimiters(connectionLimiters)
		rateLimiters, err := s.repository.GetRateLimiters(map[string][]string{"squad_id_in": squadIDs})
		if err != nil {
			s.closeAllNodes()
			return deleted.Squad, err
		}
		connectedNode.UpdateRateLimiters(rateLimiters)
	}
	return deleted.Squad, nil
}

func (s *Service) CreateNode(node constant.NodeCreate) (constant.Node, error) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	err := s.defaultValidator.Struct(node)
	if err != nil {
		return constant.Node{}, err
	}
	createdNode, err := s.repository.CreateNode(node)
	if err != nil {
		return createdNode, err
	}
	return createdNode, nil
}

func (s *Service) GetNodes(filters map[string][]string) ([]constant.Node, error) {
	return s.repository.GetNodes(filters)
}

func (s *Service) GetNodesCount(filters map[string][]string) (int, error) {
	return s.repository.GetNodesCount(filters)
}

func (s *Service) GetNode(uuid string) (constant.Node, error) {
	return s.repository.GetNode(uuid)
}

func (s *Service) GetNodeStatus(uuid string) (string, error) {
	s.mtx.RLock()
	defer s.mtx.RUnlock()
	node, ok := s.nodes[uuid]
	if !ok || !node.IsOnline() {
		return "offline", nil
	}
	return "online", nil
}

func (s *Service) UpdateNode(uuid string, node constant.NodeUpdate) (constant.Node, error) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	err := s.defaultValidator.Struct(node)
	if err != nil {
		return constant.Node{}, err
	}
	updatedNode, err := s.repository.UpdateNode(uuid, node)
	if err != nil {
		return updatedNode, err
	}
	return updatedNode, nil
}

func (s *Service) DeleteNode(uuid string) (constant.Node, error) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	deletedNode, err := s.repository.DeleteNode(uuid)
	if err != nil {
		return deletedNode, err
	}
	node, ok := s.nodes[uuid]
	if ok {
		node.Close()
		delete(s.nodes, uuid)
	}
	return deletedNode, nil
}

func (s *Service) CreateUser(user constant.UserCreate) (constant.User, error) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	err := s.defaultValidator.Struct(user)
	if err != nil {
		return constant.User{}, err
	}
	createdUser, err := s.repository.CreateUser(user)
	if err != nil {
		return createdUser, err
	}
	nodes, err := s.repository.GetNodes(map[string][]string{
		"squad_id_in": convertIntSliceToStringSlice(createdUser.SquadIDs),
	})
	if err != nil {
		s.closeAllNodes()
		return createdUser, err
	}
	for _, node := range nodes {
		if node, ok := s.nodes[node.UUID]; ok {
			node.UpdateUser(createdUser)
		}
	}
	return createdUser, nil
}

func (s *Service) GetUsers(filters map[string][]string) ([]constant.User, error) {
	return s.repository.GetUsers(filters)
}

func (s *Service) GetUsersCount(filters map[string][]string) (int, error) {
	return s.repository.GetUsersCount(filters)
}

func (s *Service) GetUser(id int) (constant.User, error) {
	return s.repository.GetUser(id)
}

func (s *Service) UpdateUser(id int, user constant.UserUpdate) (constant.User, error) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	updatedUser, err := s.repository.UpdateUser(id, user)
	if err != nil {
		return updatedUser, err
	}
	nodes, err := s.repository.GetNodes(map[string][]string{
		"squad_id_in": convertIntSliceToStringSlice(updatedUser.SquadIDs),
	})
	if err != nil {
		s.closeAllNodes()
		return updatedUser, err
	}
	for _, node := range nodes {
		if node, ok := s.nodes[node.UUID]; ok {
			node.UpdateUser(updatedUser)
		}
	}
	return updatedUser, nil
}

func (s *Service) DeleteUser(id int) (constant.User, error) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	deletedUser, err := s.repository.DeleteUser(id)
	if err != nil {
		return deletedUser, err
	}
	nodes, err := s.repository.GetNodes(map[string][]string{
		"squad_id_in": convertIntSliceToStringSlice(deletedUser.SquadIDs),
	})
	if err != nil {
		s.closeAllNodes()
		return deletedUser, err
	}
	for _, node := range nodes {
		if node, ok := s.nodes[node.UUID]; ok {
			node.DeleteUser(deletedUser)
		}
	}
	return deletedUser, nil
}

func (s *Service) CreateBandwidthLimiter(limiter constant.BandwidthLimiterCreate) (constant.BandwidthLimiter, error) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	err := s.defaultValidator.Struct(limiter)
	if err != nil {
		return constant.BandwidthLimiter{}, err
	}
	createdLimiter, err := s.repository.CreateBandwidthLimiter(limiter)
	if err != nil {
		return createdLimiter, err
	}
	nodes, err := s.repository.GetNodes(map[string][]string{
		"squad_id_in": convertIntSliceToStringSlice(createdLimiter.SquadIDs),
	})
	if err != nil {
		s.closeAllNodes()
		return createdLimiter, err
	}
	for _, node := range nodes {
		if node, ok := s.nodes[node.UUID]; ok {
			node.UpdateBandwidthLimiter(createdLimiter)
		}
	}
	return createdLimiter, nil
}

func (s *Service) GetBandwidthLimiters(filters map[string][]string) ([]constant.BandwidthLimiter, error) {
	return s.repository.GetBandwidthLimiters(filters)
}

func (s *Service) GetBandwidthLimitersCount(filters map[string][]string) (int, error) {
	return s.repository.GetBandwidthLimitersCount(filters)
}

func (s *Service) GetBandwidthLimiter(id int) (constant.BandwidthLimiter, error) {
	return s.repository.GetBandwidthLimiter(id)
}

func (s *Service) UpdateBandwidthLimiter(id int, limiter constant.BandwidthLimiterUpdate) (constant.BandwidthLimiter, error) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	err := s.defaultValidator.Struct(limiter)
	if err != nil {
		return constant.BandwidthLimiter{}, err
	}
	updatedLimiter, err := s.repository.UpdateBandwidthLimiter(id, limiter)
	if err != nil {
		return updatedLimiter, err
	}
	nodes, err := s.repository.GetNodes(map[string][]string{
		"squad_id_in": convertIntSliceToStringSlice(updatedLimiter.SquadIDs),
	})
	if err != nil {
		s.closeAllNodes()
		return updatedLimiter, err
	}
	for _, node := range nodes {
		if node, ok := s.nodes[node.UUID]; ok {
			node.UpdateBandwidthLimiter(updatedLimiter)
		}
	}
	return updatedLimiter, nil
}

func (s *Service) DeleteBandwidthLimiter(id int) (constant.BandwidthLimiter, error) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	deletedLimiter, err := s.repository.DeleteBandwidthLimiter(id)
	if err != nil {
		return deletedLimiter, err
	}
	nodes, err := s.repository.GetNodes(map[string][]string{
		"squad_id_in": convertIntSliceToStringSlice(deletedLimiter.SquadIDs),
	})
	if err != nil {
		s.closeAllNodes()
		return deletedLimiter, err
	}
	for _, node := range nodes {
		if node, ok := s.nodes[node.UUID]; ok {
			node.DeleteBandwidthLimiter(deletedLimiter)
		}
	}
	return deletedLimiter, nil
}

func (s *Service) CreateTrafficLimiter(limiter constant.TrafficLimiterCreate) (constant.TrafficLimiter, error) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	err := s.defaultValidator.Struct(limiter)
	if err != nil {
		return constant.TrafficLimiter{}, err
	}
	createdLimiter, err := s.repository.CreateTrafficLimiter(limiter)
	if err != nil {
		return createdLimiter, err
	}
	s.trafficMtx.Lock()
	s.trafficUsage[createdLimiter.ID] = &TrafficUsage{
		used:  createdLimiter.RawUsed,
		quota: createdLimiter.RawQuota,
	}
	s.trafficMtx.Unlock()
	nodes, err := s.repository.GetNodes(map[string][]string{
		"squad_id_in": convertIntSliceToStringSlice(createdLimiter.SquadIDs),
	})
	if err != nil {
		s.closeAllNodes()
		return createdLimiter, err
	}
	for _, node := range nodes {
		if node, ok := s.nodes[node.UUID]; ok {
			node.UpdateTrafficLimiter(createdLimiter)
		}
	}
	return createdLimiter, nil
}

func (s *Service) GetTrafficLimiters(filters map[string][]string) ([]constant.TrafficLimiter, error) {
	return s.repository.GetTrafficLimiters(filters)
}

func (s *Service) GetTrafficLimitersCount(filters map[string][]string) (int, error) {
	return s.repository.GetTrafficLimitersCount(filters)
}

func (s *Service) GetTrafficLimiter(id int) (constant.TrafficLimiter, error) {
	return s.repository.GetTrafficLimiter(id)
}

func (s *Service) UpdateTrafficLimiter(id int, limiter constant.TrafficLimiterUpdate) (constant.TrafficLimiter, error) {
	s.mtx.Lock()
	s.trafficMtx.Lock()
	defer s.mtx.Unlock()
	defer s.trafficMtx.Unlock()
	err := s.defaultValidator.Struct(limiter)
	if err != nil {
		return constant.TrafficLimiter{}, err
	}
	updatedLimiter, err := s.repository.UpdateTrafficLimiter(id, limiter)
	if err != nil {
		return updatedLimiter, err
	}
	s.trafficUsage[updatedLimiter.ID] = &TrafficUsage{
		used:  updatedLimiter.RawUsed,
		quota: updatedLimiter.RawQuota,
	}
	nodes, err := s.repository.GetNodes(map[string][]string{
		"squad_id_in": convertIntSliceToStringSlice(updatedLimiter.SquadIDs),
	})
	if err != nil {
		s.closeAllNodes()
		return updatedLimiter, err
	}
	for _, node := range nodes {
		if node, ok := s.nodes[node.UUID]; ok {
			node.UpdateTrafficLimiter(updatedLimiter)
		}
	}
	return updatedLimiter, nil
}

func (s *Service) UpdateTrafficLimiterUsed(id int, used uint64) (constant.TrafficLimiter, error) {
	s.mtx.Lock()
	s.trafficMtx.Lock()
	defer s.mtx.Unlock()
	defer s.trafficMtx.Unlock()
	updatedLimiter, err := s.repository.UpdateTrafficLimiterUsed(id, used)
	if err != nil {
		return updatedLimiter, err
	}
	s.trafficUsage[updatedLimiter.ID] = &TrafficUsage{
		used:  updatedLimiter.RawUsed,
		quota: updatedLimiter.RawQuota,
	}
	nodes, err := s.repository.GetNodes(map[string][]string{
		"squad_id_in": convertIntSliceToStringSlice(updatedLimiter.SquadIDs),
	})
	if err != nil {
		s.closeAllNodes()
		return updatedLimiter, err
	}
	for _, node := range nodes {
		if node, ok := s.nodes[node.UUID]; ok {
			node.UpdateTrafficLimiter(updatedLimiter)
		}
	}
	return updatedLimiter, nil
}

func (s *Service) DeleteTrafficLimiter(id int) (constant.TrafficLimiter, error) {
	s.mtx.Lock()
	s.trafficMtx.Lock()
	defer s.mtx.Unlock()
	defer s.trafficMtx.Unlock()
	deletedLimiter, err := s.repository.DeleteTrafficLimiter(id)
	if err != nil {
		return deletedLimiter, err
	}
	delete(s.trafficUsage, deletedLimiter.ID)
	nodes, err := s.repository.GetNodes(map[string][]string{
		"squad_id_in": convertIntSliceToStringSlice(deletedLimiter.SquadIDs),
	})
	if err != nil {
		s.closeAllNodes()
		return deletedLimiter, err
	}
	for _, node := range nodes {
		if node, ok := s.nodes[node.UUID]; ok {
			node.DeleteTrafficLimiter(deletedLimiter)
		}
	}
	return deletedLimiter, nil
}

func (s *Service) CreateConnectionLimiter(limiter constant.ConnectionLimiterCreate) (constant.ConnectionLimiter, error) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	err := s.defaultValidator.Struct(limiter)
	if err != nil {
		return constant.ConnectionLimiter{}, err
	}
	createdLimiter, err := s.repository.CreateConnectionLimiter(limiter)
	if err != nil {
		return createdLimiter, err
	}
	nodes, err := s.repository.GetNodes(map[string][]string{
		"squad_id_in": convertIntSliceToStringSlice(createdLimiter.SquadIDs),
	})
	if err != nil {
		s.closeAllNodes()
		return createdLimiter, err
	}
	for _, node := range nodes {
		if node, ok := s.nodes[node.UUID]; ok {
			node.UpdateConnectionLimiter(createdLimiter)
		}
	}
	return createdLimiter, nil
}

func (s *Service) GetConnectionLimiters(filters map[string][]string) ([]constant.ConnectionLimiter, error) {
	return s.repository.GetConnectionLimiters(filters)
}

func (s *Service) GetConnectionLimitersCount(filters map[string][]string) (int, error) {
	return s.repository.GetConnectionLimitersCount(filters)
}

func (s *Service) GetConnectionLimiter(id int) (constant.ConnectionLimiter, error) {
	return s.repository.GetConnectionLimiter(id)
}

func (s *Service) UpdateConnectionLimiter(id int, limiter constant.ConnectionLimiterUpdate) (constant.ConnectionLimiter, error) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	err := s.defaultValidator.Struct(limiter)
	if err != nil {
		return constant.ConnectionLimiter{}, err
	}
	updatedLimiter, err := s.repository.UpdateConnectionLimiter(id, limiter)
	if err != nil {
		return updatedLimiter, err
	}
	nodes, err := s.repository.GetNodes(map[string][]string{
		"squad_id_in": convertIntSliceToStringSlice(updatedLimiter.SquadIDs),
	})
	if err != nil {
		s.closeAllNodes()
		return updatedLimiter, err
	}
	for _, node := range nodes {
		if node, ok := s.nodes[node.UUID]; ok {
			node.UpdateConnectionLimiter(updatedLimiter)
		}
	}
	if limiter.LockType != "manager" {
		s.connLockMtx.Lock()
		defer s.connLockMtx.Unlock()
		delete(s.limiterLocks, id)
	}
	return updatedLimiter, nil
}

func (s *Service) DeleteConnectionLimiter(id int) (constant.ConnectionLimiter, error) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	deletedLimiter, err := s.repository.DeleteConnectionLimiter(id)
	if err != nil {
		return deletedLimiter, err
	}
	nodes, err := s.repository.GetNodes(map[string][]string{
		"squad_id_in": convertIntSliceToStringSlice(deletedLimiter.SquadIDs),
	})
	if err != nil {
		s.closeAllNodes()
		return deletedLimiter, err
	}
	for _, node := range nodes {
		if node, ok := s.nodes[node.UUID]; ok {
			node.DeleteConnectionLimiter(deletedLimiter)
		}
	}
	if deletedLimiter.LockType == "manager" {
		s.connLockMtx.Lock()
		defer s.connLockMtx.Unlock()
		delete(s.limiterLocks, id)
	}
	return deletedLimiter, nil
}

func (s *Service) CreateRateLimiter(limiter constant.RateLimiterCreate) (constant.RateLimiter, error) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	err := s.defaultValidator.Struct(limiter)
	if err != nil {
		return constant.RateLimiter{}, err
	}
	createdLimiter, err := s.repository.CreateRateLimiter(limiter)
	if err != nil {
		return createdLimiter, err
	}
	nodes, err := s.repository.GetNodes(map[string][]string{
		"squad_id_in": convertIntSliceToStringSlice(createdLimiter.SquadIDs),
	})
	if err != nil {
		s.closeAllNodes()
		return createdLimiter, err
	}
	for _, node := range nodes {
		if node, ok := s.nodes[node.UUID]; ok {
			node.UpdateRateLimiter(createdLimiter)
		}
	}
	return createdLimiter, nil
}

func (s *Service) GetRateLimiters(filters map[string][]string) ([]constant.RateLimiter, error) {
	return s.repository.GetRateLimiters(filters)
}

func (s *Service) GetRateLimitersCount(filters map[string][]string) (int, error) {
	return s.repository.GetRateLimitersCount(filters)
}

func (s *Service) GetRateLimiter(id int) (constant.RateLimiter, error) {
	return s.repository.GetRateLimiter(id)
}

func (s *Service) UpdateRateLimiter(id int, limiter constant.RateLimiterUpdate) (constant.RateLimiter, error) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	err := s.defaultValidator.Struct(limiter)
	if err != nil {
		return constant.RateLimiter{}, err
	}
	updatedLimiter, err := s.repository.UpdateRateLimiter(id, limiter)
	if err != nil {
		return updatedLimiter, err
	}
	nodes, err := s.repository.GetNodes(map[string][]string{
		"squad_id_in": convertIntSliceToStringSlice(updatedLimiter.SquadIDs),
	})
	if err != nil {
		s.closeAllNodes()
		return updatedLimiter, err
	}
	for _, node := range nodes {
		if node, ok := s.nodes[node.UUID]; ok {
			node.UpdateRateLimiter(updatedLimiter)
		}
	}
	return updatedLimiter, nil
}

func (s *Service) DeleteRateLimiter(id int) (constant.RateLimiter, error) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	deletedLimiter, err := s.repository.DeleteRateLimiter(id)
	if err != nil {
		return deletedLimiter, err
	}
	nodes, err := s.repository.GetNodes(map[string][]string{
		"squad_id_in": convertIntSliceToStringSlice(deletedLimiter.SquadIDs),
	})
	if err != nil {
		s.closeAllNodes()
		return deletedLimiter, err
	}
	for _, node := range nodes {
		if node, ok := s.nodes[node.UUID]; ok {
			node.DeleteRateLimiter(deletedLimiter)
		}
	}
	return deletedLimiter, nil
}

func (s *Service) AddNode(uuid string, node constant.ConnectedNode) error {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	var node_ constant.Node
	var err error
	node_, err = s.repository.GetNode(uuid)
	if err != nil {
		return err
	}
	squadIDs := convertIntSliceToStringSlice(node_.SquadIDs)
	users, err := s.repository.GetUsers(map[string][]string{
		"squad_id_in": squadIDs,
	})
	if err != nil {
		return err
	}
	node.UpdateUsers(users)
	bandwidthLimiters, err := s.repository.GetBandwidthLimiters(map[string][]string{
		"squad_id_in": squadIDs,
	})
	if err != nil {
		return err
	}
	node.UpdateBandwidthLimiters(bandwidthLimiters)
	trafficLimiters, err := s.repository.GetTrafficLimiters(map[string][]string{
		"squad_id_in": squadIDs,
	})
	if err != nil {
		return err
	}
	node.UpdateTrafficLimiters(trafficLimiters)
	connectionLimiters, err := s.repository.GetConnectionLimiters(map[string][]string{
		"squad_id_in": squadIDs,
	})
	if err != nil {
		return err
	}
	node.UpdateConnectionLimiters(connectionLimiters)
	rateLimiters, err := s.repository.GetRateLimiters(map[string][]string{
		"squad_id_in": squadIDs,
	})
	if err != nil {
		return err
	}
	node.UpdateRateLimiters(rateLimiters)
	s.nodes[uuid] = node
	return nil
}

func (s *Service) AcquireLock(limiterId int, id string) (string, error) {
	s.connLockMtx.Lock()
	defer s.connLockMtx.Unlock()
	limiter, err := s.repository.GetConnectionLimiter(limiterId)
	if err != nil {
		return "", err
	}
	if limiter.LockType != "manager" {
		return "", E.New("invalid lock type")
	}
	locks, ok := s.limiterLocks[limiterId]
	if !ok {
		locks = make(map[string]*cache.Cache[string, struct{}])
		s.limiterLocks[limiter.ID] = locks
	}
	lock, ok := locks[id]
	if !ok {
		if len(locks) == int(limiter.Count) {
			return "", E.New("not enough free locks")
		}
		lock = cache.New[string, struct{}](time.Second*30, time.Second)
		lock.OnEvicted(func(_ string, _ struct{}) {
			s.connLockMtx.Lock()
			defer s.connLockMtx.Unlock()
			if lock.ItemCount() == 0 {
				delete(locks, id)
			}
		})
		locks[id] = lock
	}
	handleID, err := uuid.NewV4()
	if err != nil {
		return "", err
	}
	lock.SetDefault(handleID.String(), struct{}{})
	return handleID.String(), nil
}

func (s *Service) RefreshLock(limiterId int, id string, handleId string) error {
	s.connLockMtx.Lock()
	defer s.connLockMtx.Unlock()
	locks, ok := s.limiterLocks[limiterId]
	if !ok {
		return E.New("limiter not found")
	}
	lock, ok := locks[id]
	if !ok {
		return E.New("lock not found")
	}
	err := lock.Replace(handleId, struct{}{}, time.Second*30)
	return err
}

func (s *Service) ReleaseLock(limiterId int, id string, handleId string) error {
	s.connLockMtx.Lock()
	defer s.connLockMtx.Unlock()
	locks, ok := s.limiterLocks[limiterId]
	if !ok {
		return E.New("limiter not found")
	}
	lock, ok := locks[id]
	if !ok {
		return E.New("lock not found")
	}
	go lock.Delete(handleId)
	return nil
}

func (s *Service) AddTrafficUsage(limiterId int, n uint64) (uint64, error) {
	s.trafficMtx.Lock()
	defer s.trafficMtx.Unlock()
	trafficStat, ok := s.trafficUsage[limiterId]
	if !ok {
		return 0, E.New("limiter not found")
	}
	trafficStat.used = trafficStat.used + n
	if trafficStat.used > trafficStat.quota {
		trafficStat.used = trafficStat.quota
	}
	updatedLimiter, err := s.repository.UpdateTrafficLimiterUsed(limiterId, trafficStat.used)
	if err != nil {
		return 0, err
	}
	trafficStat.used = updatedLimiter.RawUsed
	return trafficStat.used, nil
}

func (s *Service) Start(stage adapter.StartStage) error {
	return nil
}

func (s *Service) Close() error {
	return nil
}

type TrafficUsage struct {
	used  uint64
	quota uint64
}

func (s *Service) closeAllNodes() {
	for _, node := range s.nodes {
		node.Close()
	}
}

func convertIntSliceToStringSlice(values []int) []string {
	result := make([]string, len(values))
	for i, v := range values {
		result[i] = strconv.Itoa(v)
	}
	return result
}
