package app

import (
	"fmt"
	"math/rand"
	"sync"

	"context"

	"github.com/weaveworks/scope/common/xfer"
)

// ControlRouter is a thing that can route control requests and responses
// between the UI and a probe.
type ControlRouter interface {
	Handle(ctx context.Context, probeID string, req xfer.Request) (xfer.Response, error)
	Register(ctx context.Context, probeID string, handler xfer.ControlHandlerFunc) (int64, error)
	Deregister(ctx context.Context, probeID string, id int64) error
}

// NewLocalControlRouter creates a new ControlRouter that does everything
// locally, in memory.
func NewLocalControlRouter() ControlRouter {
	return &localControlRouter{
		probes: map[string]probe{},
	}
}

type localControlRouter struct {
	sync.Mutex
	probes map[string]probe
}

type probe struct {
	id      int64
	handler xfer.ControlHandlerFunc
}

func (l *localControlRouter) Handle(_ context.Context, probeID string, req xfer.Request) (xfer.Response, error) {
	l.Lock()
	probe, ok := l.probes[probeID]
	l.Unlock()
	if !ok {
		return xfer.Response{}, fmt.Errorf("probe %s is not connected right now", probeID)
	}
	return probe.handler(req), nil
}

func (l *localControlRouter) Register(_ context.Context, probeID string, handler xfer.ControlHandlerFunc) (int64, error) {
	l.Lock()
	defer l.Unlock()
	id := rand.Int63()
	l.probes[probeID] = probe{
		id:      id,
		handler: handler,
	}
	return id, nil
}

func (l *localControlRouter) Deregister(_ context.Context, probeID string, id int64) error {
	l.Lock()
	defer l.Unlock()
	// NB probe might have reconnected in the mean time, need to ensure we do not
	// delete new connection!  Also, it might have connected then deleted itself!
	if l.probes[probeID].id == id {
		delete(l.probes, probeID)
	}
	return nil
}
