package main

import (
	"fmt"
	"sync"
	"time"
)

// section: hits
// Hits tracks URL hit counts with read-heavy access patterns
type Hits struct {
	mu     sync.RWMutex
	counts map[string]int
}

// NewHits creates a new Hits tracker
func NewHits() *Hits {
	return &Hits{
		counts: make(map[string]int),
	}
}

// Increment increments the hit count for a URL (write operation)
func (h *Hits) Increment(url string) {
	h.mu.Lock()
	defer h.mu.Unlock()
	h.counts[url]++
}

// Count returns the hit count for a URL (read operation)
func (h *Hits) Count(url string) int {
	h.mu.RLock()
	defer h.mu.RUnlock()
	return h.counts[url]
}

// Total returns the total number of hits across all URLs (read operation)
func (h *Hits) Total() int {
	h.mu.RLock()
	defer h.mu.RUnlock()
	total := 0
	for _, count := range h.counts {
		total += count
	}
	return total
}

// Stats returns a snapshot of all counts (read operation)
func (h *Hits) Stats() map[string]int {
	h.mu.RLock()
	defer h.mu.RUnlock()

	// Create a copy to avoid race conditions
	stats := make(map[string]int, len(h.counts))
	for url, count := range h.counts {
		stats[url] = count
	}
	return stats
}

// section: hits

// section: main
func main() {
	hits := NewHits()
	var wg sync.WaitGroup

	urls := []string{
		"/api/users",
		"/api/products",
		"/api/orders",
	}

	// Simulate write operations (increment hits)
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func(iteration int) {
			defer wg.Done()
			for _, url := range urls {
				hits.Increment(url)
				time.Sleep(10 * time.Millisecond)
			}
		}(i)
	}

	// Simulate read-heavy operations (checking counts)
	// Many concurrent reads can happen simultaneously with RWMutex
	for i := 0; i < 50; i++ {
		wg.Add(1)
		go func(iteration int) {
			defer wg.Done()
			for _, url := range urls {
				count := hits.Count(url)
				if iteration%10 == 0 {
					fmt.Printf("Read %d: %s has %d hits\n", iteration, url, count)
				}
			}
			time.Sleep(5 * time.Millisecond)
		}(i)
	}

	// Periodically check total
	for i := 0; i < 5; i++ {
		wg.Add(1)
		go func(iteration int) {
			defer wg.Done()
			time.Sleep(50 * time.Millisecond)
			total := hits.Total()
			fmt.Printf("Total hits at check %d: %d\n", iteration, total)
		}(i)
	}

	wg.Wait()

	// Final statistics
	fmt.Println("\n=== Final Statistics ===")
	stats := hits.Stats()
	for url, count := range stats {
		fmt.Printf("%s: %d hits\n", url, count)
	}
	fmt.Printf("Total: %d hits\n", hits.Total())
}

// section: main

// section: output
// Output (order may vary due to concurrency):
// Read 0: /api/users has 0 hits
// Read 0: /api/products has 0 hits
// Read 0: /api/orders has 0 hits
// Read 10: /api/users has 3 hits
// Read 10: /api/products has 3 hits
// Total hits at check 0: 9
// Read 20: /api/users has 6 hits
// Total hits at check 1: 18
// ...
// === Final Statistics ===
// /api/users: 10 hits
// /api/products: 10 hits
// /api/orders: 10 hits
// Total: 30 hits
// section: output
