package main

import (
	"fmt"
	"sync"
)

type Ledger struct {
	mu       sync.Mutex
	balances map[string]int
}

func NewLedger(initial map[string]int) *Ledger {
	balances := make(map[string]int, len(initial))
	for account, balance := range initial {
		balances[account] = balance
	}
	return &Ledger{balances: balances}
}

func (l *Ledger) Transfer(from, to string, amount int) bool {
	if amount <= 0 {
		return false
	}

	l.mu.Lock()
	defer l.mu.Unlock()

	if l.balances[from] < amount {
		return false
	}
	l.balances[from] -= amount
	l.balances[to] += amount
	return true
}

func (l *Ledger) Snapshot() map[string]int {
	l.mu.Lock()
	defer l.mu.Unlock()

	copyOfBalances := make(map[string]int, len(l.balances))
	for account, balance := range l.balances {
		copyOfBalances[account] = balance
	}
	return copyOfBalances
}

func main() {
	ledger := NewLedger(map[string]int{"a": 100, "b": 100})
	var wg sync.WaitGroup

	for range 100 {
		wg.Go(func() {
			ledger.Transfer("a", "b", 1)
		})
		wg.Go(func() {
			ledger.Transfer("b", "a", 1)
		})
	}

	wg.Wait()
	balances := ledger.Snapshot()
	fmt.Println(balances["a"], balances["b"], balances["a"]+balances["b"])
}
