package main

import (
	"fmt"
	"strings"
)

// These matchers model nonempty dot-separated tokens, not full broker validation.
func rabbitMatch(pattern, key string) bool {
	p, k := strings.Split(pattern, "."), strings.Split(key, ".")
	var match func(int, int) bool
	match = func(i, j int) bool {
		if i == len(p) {
			return j == len(k)
		}
		if p[i] == "#" {
			return match(i+1, j) || (j < len(k) && match(i, j+1))
		}
		return j < len(k) && (p[i] == "*" || p[i] == k[j]) && match(i+1, j+1)
	}
	return match(0, 0)
}

func natsMatch(pattern, subject string) bool {
	p, s := strings.Split(pattern, "."), strings.Split(subject, ".")
	for i, token := range p {
		if token == ">" {
			return i == len(p)-1 && i < len(s)
		}
		if i >= len(s) || (token != "*" && token != s[i]) {
			return false
		}
	}
	return len(p) == len(s)
}

type Pending struct {
	Owner        string
	LastDelivery int
}
type Stream struct {
	entries []string
	pending map[int]Pending
}

func (s *Stream) Deliver(index int, owner string, now int) {
	if s.pending == nil {
		s.pending = make(map[int]Pending)
	}
	s.pending[index] = Pending{owner, now}
}
func (s *Stream) Claim(index int, owner string, now, minIdle int) bool {
	p, ok := s.pending[index]
	if !ok || now-p.LastDelivery < minIdle {
		return false
	}
	s.pending[index] = Pending{owner, now}
	return true
}
func (s *Stream) Ack(index int) { delete(s.pending, index) }

func demo() {
	fmt.Println("rabbit order.# / order:", rabbitMatch("order.#", "order"))
	fmt.Println("nats order.> / order:", natsMatch("order.>", "order"))
	fmt.Println("rabbit order.* / order.paid:", rabbitMatch("order.*", "order.paid"))
	fmt.Println("nats order.* / order.paid.eu:", natsMatch("order.*", "order.paid.eu"))
	s := Stream{entries: []string{"event-1"}}
	s.Deliver(0, "worker-a", 0)
	fmt.Println("claim too soon:", s.Claim(0, "worker-b", 3, 5))
	fmt.Println("claim after idle:", s.Claim(0, "worker-b", 8, 5))
	fmt.Println("new owner:", s.pending[0].Owner)
	s.Ack(0)
	fmt.Println("pending, stored:", len(s.pending), len(s.entries))
}
func main() { demo() }
