package main

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"sync"
	"testing"
)

func TestRequestWeighting(t *testing.T) {
	var mu sync.Mutex
	counts := make(map[string]int)
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		mu.Lock()
		counts[r.Method+" "+r.URL.RequestURI()]++
		mu.Unlock()
		w.WriteHeader(http.StatusOK)
	}))
	defer server.Close()

	g := &generator{client: server.Client(), target: server.URL, buoys: []string{"buoy-1"}, counts: make(map[string]int)}
	for roll := range 100 {
		g.sendRoll(context.Background(), roll)
	}

	want := map[string]int{
		"GET /api/v1/readings":                     60,
		"POST /api/v1/readings":                    25,
		"GET /api/v1/readings/aggregate?window=1h": 15,
	}
	if fmt.Sprint(counts) != fmt.Sprint(want) {
		t.Fatalf("request weighting = %v, want %v", counts, want)
	}
}

func TestSeedEmptyAPI(t *testing.T) {
	var mu sync.Mutex
	created := 0
	var posted []buoy
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		switch {
		case r.Method == http.MethodGet && r.URL.Path == "/api/v1/buoys":
			_ = json.NewEncoder(w).Encode([]buoy{})
		case r.Method == http.MethodPost && r.URL.Path == "/api/v1/buoys":
			var input buoy
			if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
				http.Error(w, "invalid buoy", http.StatusBadRequest)
				return
			}
			mu.Lock()
			created++
			posted = append(posted, input)
			id := fmt.Sprintf("buoy-%d", created)
			mu.Unlock()
			_ = json.NewEncoder(w).Encode(buoy{ID: id})
		default:
			http.NotFound(w, r)
		}
	}))
	defer server.Close()

	g := &generator{client: server.Client(), target: server.URL, counts: make(map[string]int)}
	if err := g.seed(context.Background()); err != nil {
		t.Fatalf("seed: %v", err)
	}
	if created != 42 || len(g.buoys) != 42 {
		t.Fatalf("created %d buoys and retained %d IDs, want 42 and 42", created, len(g.buoys))
	}
	want := [][2]float64{{44.5, -9}, {44.2, -8}, {44.3, -7}, {44.4, -6}, {44.5, -5}, {44.6, -4}, {44.7, -3}, {44.5, -2}, {43.8, -2}, {43.5, -9.8}, {42.5, -10.2}, {41.5, -10.5}, {40.5, -10.8}, {39.5, -11}, {38.5, -11}, {37.5, -10.8}, {36.5, -10}, {35.9, -9}, {35.8, -8}, {35.7, -7}, {35.2, -7.5}, {35, -8.5}, {35.8, -4.5}, {36, -3.5}, {36.2, -2.5}, {36.5, -1.5}, {36.8, -.5}, {37.2, .5}, {37.7, 1.2}, {38.1, 2}, {38, 3}, {38.5, 4}, {39.5, 1}, {40, 1.5}, {40.5, 2}, {41, 2.7}, {41.5, 3.3}, {42, 3.8}, {42.5, 4.5}, {43, -9.8}, {41, -10.5}, {39, -10.8}}
	for i, position := range want {
		if posted[i].Latitude != position[0] || posted[i].Longitude != position[1] {
			t.Errorf("buoy %d position = (%v, %v), want (%v, %v)", i, posted[i].Latitude, posted[i].Longitude, position[0], position[1])
		}
	}
}

func TestTrafficContinuesThroughHTTP500(t *testing.T) {
	const requests = 30
	var mu sync.Mutex
	received := 0
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		mu.Lock()
		received++
		mu.Unlock()
		http.Error(w, "unavailable", http.StatusInternalServerError)
	}))
	defer server.Close()

	g := &generator{client: server.Client(), target: server.URL, buoys: []string{"buoy-1"}, counts: make(map[string]int)}
	for roll := range requests {
		g.sendRoll(context.Background(), roll)
	}

	mu.Lock()
	got := received
	mu.Unlock()
	if got != requests {
		t.Fatalf("received %d requests, want %d", got, requests)
	}
	if g.counts["500"] != requests {
		t.Fatalf("recorded %d HTTP 500 responses, want %d", g.counts["500"], requests)
	}
}
