package server

import (
	"context"
	"sync"

	"github.com/sagernet/sing-box/log"
	CS "github.com/sagernet/sing-box/service/manager/constant"
	pb "github.com/sagernet/sing-box/service/node_manager_api/manager"
	E "github.com/sagernet/sing/common/exceptions"
	"google.golang.org/grpc"
)

type RemoteNode struct {
	ctx    context.Context
	logger log.ContextLogger
	stream grpc.ServerStreamingServer[pb.NodeData]

	err     error
	errChan chan error

	mtx sync.Mutex
}

func NewRemoteNode(ctx context.Context, logger log.ContextLogger, stream grpc.ServerStreamingServer[pb.NodeData]) (*RemoteNode, chan error) {
	errChan := make(chan error)
	return &RemoteNode{
		ctx:     ctx,
		logger:  logger,
		stream:  stream,
		errChan: errChan,
	}, errChan
}

func (s *RemoteNode) UpdateUser(user CS.User) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	s.send(&pb.NodeData{
		Op:   pb.OpType_updateUser,
		Data: &pb.NodeData_User{User: s.convertUser(user)},
	})
}

func (s *RemoteNode) UpdateUsers(users []CS.User) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	pbUsers := make([]*pb.User, len(users))
	for i, user := range users {
		pbUsers[i] = s.convertUser(user)
	}
	s.send(&pb.NodeData{
		Op:   pb.OpType_updateUsers,
		Data: &pb.NodeData_Users{Users: &pb.UserList{Values: pbUsers}},
	})
}

func (s *RemoteNode) DeleteUser(user CS.User) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	s.send(&pb.NodeData{
		Op:   pb.OpType_deleteUser,
		Data: &pb.NodeData_User{User: s.convertUser(user)},
	})
}

func (s *RemoteNode) UpdateConnectionLimiter(limiter CS.ConnectionLimiter) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	s.send(&pb.NodeData{
		Op:   pb.OpType_updateConnectionLimiter,
		Data: &pb.NodeData_ConnectionLimiter{ConnectionLimiter: s.convertConnectionLimiter(limiter)},
	})
}

func (s *RemoteNode) UpdateConnectionLimiters(limiters []CS.ConnectionLimiter) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	pbLimiters := make([]*pb.ConnectionLimiter, len(limiters))
	for i, limiters := range limiters {
		pbLimiters[i] = s.convertConnectionLimiter(limiters)
	}
	s.send(&pb.NodeData{
		Op:   pb.OpType_updateConnectionLimiters,
		Data: &pb.NodeData_ConnectionLimiters{ConnectionLimiters: &pb.ConnectionLimiterList{Values: pbLimiters}},
	})
}

func (s *RemoteNode) DeleteConnectionLimiter(limiter CS.ConnectionLimiter) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	s.send(&pb.NodeData{
		Op:   pb.OpType_deleteConnectionLimiter,
		Data: &pb.NodeData_ConnectionLimiter{ConnectionLimiter: s.convertConnectionLimiter(limiter)},
	})
}

func (s *RemoteNode) UpdateBandwidthLimiter(limiter CS.BandwidthLimiter) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	s.send(&pb.NodeData{
		Op:   pb.OpType_updateBandwidthLimiter,
		Data: &pb.NodeData_BandwidthLimiter{BandwidthLimiter: s.convertBandwidthLimiter(limiter)},
	})
}

func (s *RemoteNode) UpdateBandwidthLimiters(limiters []CS.BandwidthLimiter) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	pbLimiters := make([]*pb.BandwidthLimiter, len(limiters))
	for i, limiters := range limiters {
		pbLimiters[i] = s.convertBandwidthLimiter(limiters)
	}
	s.send(&pb.NodeData{
		Op:   pb.OpType_updateBandwidthLimiters,
		Data: &pb.NodeData_BandwidthLimiters{BandwidthLimiters: &pb.BandwidthLimiterList{Values: pbLimiters}},
	})
}

func (s *RemoteNode) DeleteBandwidthLimiter(limiter CS.BandwidthLimiter) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	s.send(&pb.NodeData{
		Op:   pb.OpType_deleteBandwidthLimiter,
		Data: &pb.NodeData_BandwidthLimiter{BandwidthLimiter: s.convertBandwidthLimiter(limiter)},
	})
}

func (s *RemoteNode) UpdateTrafficLimiter(limiter CS.TrafficLimiter) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	s.send(&pb.NodeData{
		Op:   pb.OpType_updateTrafficLimiter,
		Data: &pb.NodeData_TrafficLimiter{TrafficLimiter: s.convertTrafficLimiter(limiter)},
	})
}

func (s *RemoteNode) UpdateTrafficLimiters(limiters []CS.TrafficLimiter) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	pbLimiters := make([]*pb.TrafficLimiter, len(limiters))
	for i, limiters := range limiters {
		pbLimiters[i] = s.convertTrafficLimiter(limiters)
	}
	s.send(&pb.NodeData{
		Op:   pb.OpType_updateTrafficLimiters,
		Data: &pb.NodeData_TrafficLimiters{TrafficLimiters: &pb.TrafficLimiterList{Values: pbLimiters}},
	})
}

func (s *RemoteNode) DeleteTrafficLimiter(limiter CS.TrafficLimiter) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	s.send(&pb.NodeData{
		Op:   pb.OpType_deleteTrafficLimiter,
		Data: &pb.NodeData_TrafficLimiter{TrafficLimiter: s.convertTrafficLimiter(limiter)},
	})
}

func (s *RemoteNode) UpdateRateLimiter(limiter CS.RateLimiter) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	s.send(&pb.NodeData{
		Op:   pb.OpType_updateRateLimiter,
		Data: &pb.NodeData_RateLimiter{RateLimiter: s.convertRateLimiter(limiter)},
	})
}

func (s *RemoteNode) UpdateRateLimiters(limiters []CS.RateLimiter) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	pbLimiters := make([]*pb.RateLimiter, len(limiters))
	for i, limiters := range limiters {
		pbLimiters[i] = s.convertRateLimiter(limiters)
	}
	s.send(&pb.NodeData{
		Op:   pb.OpType_updateRateLimiters,
		Data: &pb.NodeData_RateLimiters{RateLimiters: &pb.RateLimiterList{Values: pbLimiters}},
	})
}

func (s *RemoteNode) DeleteRateLimiter(limiter CS.RateLimiter) {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	s.send(&pb.NodeData{
		Op:   pb.OpType_deleteRateLimiter,
		Data: &pb.NodeData_RateLimiter{RateLimiter: s.convertRateLimiter(limiter)},
	})
}

func (s *RemoteNode) IsLocal() bool {
	return false
}

func (s *RemoteNode) IsOnline() bool {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	select {
	case <-s.stream.Context().Done():
		return false
	default:
		return true
	}
}

func (s *RemoteNode) Close() error {
	s.mtx.Lock()
	defer s.mtx.Unlock()
	s.close(E.New("server connection is closed"))
	return nil
}

func (s *RemoteNode) send(data *pb.NodeData) {
	if s.err != nil {
		return
	}
	select {
	case <-s.ctx.Done():
		s.close(E.New("server connection is closed"))
		return
	case <-s.stream.Context().Done():
		s.close(E.New("client connection is closed"))
		return
	default:
	}
	err := s.stream.Send(data)
	if err != nil {
		s.close(err)
	}
}

func (s *RemoteNode) close(err error) {
	if err != nil {
		return
	}
	s.err = err
	s.errChan <- err
	close(s.errChan)
}

func (s *RemoteNode) convertUser(user CS.User) *pb.User {
	return &pb.User{
		Id:       int32(user.ID),
		Username: user.Username,
		Inbound:  user.Inbound,
		Type:     user.Type,
		Uuid:     user.UUID,
		Password: user.Password,
		Secret:   user.Secret,
		Flow:     user.Flow,
		AlterId:  int32(user.AlterID),
	}
}

func (s *RemoteNode) convertConnectionLimiter(limiter CS.ConnectionLimiter) *pb.ConnectionLimiter {
	return &pb.ConnectionLimiter{
		Id:             int32(limiter.ID),
		Username:       limiter.Username,
		Outbound:       limiter.Outbound,
		Strategy:       limiter.Strategy,
		ConnectionType: limiter.ConnectionType,
		LockType:       limiter.LockType,
		Count:          limiter.Count,
	}
}

func (s *RemoteNode) convertBandwidthLimiter(limiter CS.BandwidthLimiter) *pb.BandwidthLimiter {
	return &pb.BandwidthLimiter{
		Id:             int32(limiter.ID),
		Username:       limiter.Username,
		Outbound:       limiter.Outbound,
		Strategy:       limiter.Strategy,
		ConnectionType: limiter.ConnectionType,
		Mode:           limiter.Mode,
		FlowKeys:       limiter.FlowKeys,
		Speed:          limiter.Speed,
		RawSpeed:       limiter.RawSpeed,
	}
}

func (s *RemoteNode) convertTrafficLimiter(limiter CS.TrafficLimiter) *pb.TrafficLimiter {
	return &pb.TrafficLimiter{
		Id:       int32(limiter.ID),
		Username: limiter.Username,
		Outbound: limiter.Outbound,
		Strategy: limiter.Strategy,
		Mode:     limiter.Mode,
		RawUsed:  limiter.RawUsed,
		Quota:    limiter.Quota,
		RawQuota: limiter.RawQuota,
	}
}

func (s *RemoteNode) convertRateLimiter(limiter CS.RateLimiter) *pb.RateLimiter {
	return &pb.RateLimiter{
		Id:             int32(limiter.ID),
		Username:       limiter.Username,
		Outbound:       limiter.Outbound,
		Strategy:       limiter.Strategy,
		ConnectionType: limiter.ConnectionType,
		Count:          limiter.Count,
		Interval:       limiter.Interval,
	}
}
