package main

import (
	"errors"
	"fmt"
	"sync"
)

type Endpoint struct {
	Name            string
	Weight, Active  int
	Ready, Draining bool
}
type Snapshot struct {
	Revision  int
	Endpoints []Endpoint
}
type Registry struct {
	mu sync.RWMutex
	s  Snapshot
}

func (r *Registry) Publish(s Snapshot) error {
	r.mu.Lock()
	defer r.mu.Unlock()
	if s.Revision <= r.s.Revision {
		return errors.New("stale revision")
	}
	seen := map[string]bool{}
	for _, e := range s.Endpoints {
		if e.Name == "" || e.Weight <= 0 || e.Weight > 1000 || e.Active < 0 || e.Active > 1000000 || seen[e.Name] {
			return errors.New("invalid endpoint")
		}
		seen[e.Name] = true
	}
	r.s = Snapshot{s.Revision, append([]Endpoint(nil), s.Endpoints...)}
	return nil
}
func (r *Registry) Read() Snapshot {
	r.mu.RLock()
	defer r.mu.RUnlock()
	return Snapshot{r.s.Revision, append([]Endpoint(nil), r.s.Endpoints...)}
}

// Demo's two choices are supplied explicitly to keep its output reproducible.
func p2c(a, b Endpoint) (string, error) {
	usable := func(e Endpoint) bool { return e.Ready && !e.Draining && e.Weight > 0 }
	if !usable(a) && !usable(b) {
		return "", errors.New("no ready endpoint")
	}
	if !usable(a) {
		return b.Name, nil
	}
	if !usable(b) {
		return a.Name, nil
	}
	if int64(a.Active)*int64(b.Weight) <= int64(b.Active)*int64(a.Weight) {
		return a.Name, nil
	}
	return b.Name, nil
}
func main() {
	r := &Registry{}
	a := Endpoint{"A", 1, 8, true, false}
	b := Endpoint{"B", 2, 6, true, false}
	must(r.Publish(Snapshot{10, []Endpoint{a, b}}))
	name, err := p2c(a, b)
	fmt.Println("P2C:", name, err)
	a.Draining = true
	b.Draining = true
	_, err = p2c(a, b)
	fmt.Println("all draining:", err)
	fmt.Println("old update:", r.Publish(Snapshot{9, nil}), "kept:", r.Read().Revision)
	fmt.Println("bad update:", r.Publish(Snapshot{11, []Endpoint{{Name: "C"}}}), "kept:", r.Read().Revision)
	copy := r.Read()
	copy.Endpoints[0].Name = "changed"
	fmt.Println("snapshot isolated:", r.Read().Endpoints[0].Name)
}
func must(err error) {
	if err != nil {
		panic(err)
	}
}
