package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"os"
	"strings"
	"sync"
	"time"

	"golang.org/x/sync/semaphore"
)

// create a custom type to receive our responses
type resp struct {
	site     string
	duration time.Duration
	err      error
}

func main() {
	args := os.Args[1:]
	// Args might be a single string with line returns to delimit sites, break them apart
	if len(args) == 1 {
		args = strings.Split(args[0], "\n")
	}

	// Create a context with timeout for the entire operation
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	wg := &sync.WaitGroup{}
	wg.Add(len(args))

	// Create a semaphore to limit concurrent requests to 30
	sem := semaphore.NewWeighted(30)

	// Create a channel to receive responses
	responses := make(chan resp, len(args))

	now := time.Now()

	for _, a := range args {
		go get(ctx, a, wg, sem, responses)
	}

	// Wait for all go routines to finish
	wg.Wait()
	close(responses)

	// Collect and print results
	var successes, failures int
	for r := range responses {
		if r.err != nil {
			failures++
			log.Printf("failed to retrieve %s: %s\n", r.site, r.err)
		} else {
			successes++
			log.Printf("Retrieved site %s in %s\n", r.site, r.duration)
		}
	}

	fmt.Printf("It took %s for the entire process to finish\n", time.Since(now))
	fmt.Printf("Successes: %d, Failures: %d\n", successes, failures)
}

func get(ctx context.Context, s string, wg *sync.WaitGroup, sem *semaphore.Weighted, responses chan<- resp) {
	defer wg.Done()

	// Acquire semaphore
	if err := sem.Acquire(ctx, 1); err != nil {
		responses <- resp{site: s, err: fmt.Errorf("failed to acquire semaphore: %s", err)}
		return
	}
	defer sem.Release(1)

	now := time.Now()

	_, err := http.Get(s)
	if err != nil {
		responses <- resp{site: s, duration: time.Since(now), err: err}
		return
	}
	responses <- resp{site: s, duration: time.Since(now)}
}
