package commands

import (
	"strings"
	"testing"
)

// sampleULIDs are valid-looking ULID strings used in tests; they don't
// correspond to real domains but satisfy ulidRegex.
var (
	ulidA = "01JA82XXXXXXXXXXXXXXXXXXXX"
	ulidB = "01JA83YYYYYYYYYYYYYYYYYYYY"
)

func TestBuildNginxRateLimitFragment_Empty(t *testing.T) {
	got := BuildNginxRateLimitFragment(nil, 0)
	if !strings.Contains(got, "Auto-generated by jabali") {
		t.Errorf("fragment missing header: %q", got)
	}
	if strings.Contains(got, "limit_req_zone") || strings.Contains(got, "limit_conn_zone") {
		t.Error("empty input should produce no zone directives")
	}
}

func TestBuildNginxRateLimitFragment_Single(t *testing.T) {
	got := BuildNginxRateLimitFragment([]rateLimitDomain{
		{DomainID: ulidA, RateLimitRPS: 10, ConnectionLimit: 50},
	}, 0)
	if !strings.Contains(got, "limit_req_zone $binary_remote_addr zone=rl_"+ulidA+":10240k rate=10r/s;") {
		t.Errorf("req zone missing or wrong: %q", got)
	}
	if !strings.Contains(got, "limit_conn_zone $binary_remote_addr zone=cn_"+ulidA+":10240k;") {
		t.Errorf("conn zone missing or wrong: %q", got)
	}
}

func TestBuildNginxRateLimitFragment_RPSOnly(t *testing.T) {
	got := BuildNginxRateLimitFragment([]rateLimitDomain{
		{DomainID: ulidA, RateLimitRPS: 100},
	}, 0)
	if !strings.Contains(got, "zone=rl_") {
		t.Errorf("missing rl_ zone: %q", got)
	}
	if strings.Contains(got, "zone=cn_") {
		t.Error("conn zone should not be emitted when ConnectionLimit=0")
	}
}

func TestBuildNginxRateLimitFragment_ConnOnly(t *testing.T) {
	got := BuildNginxRateLimitFragment([]rateLimitDomain{
		{DomainID: ulidA, ConnectionLimit: 100},
	}, 0)
	if strings.Contains(got, "zone=rl_") {
		t.Error("rl zone should not be emitted when RateLimitRPS=0")
	}
	if !strings.Contains(got, "zone=cn_") {
		t.Errorf("missing cn_ zone: %q", got)
	}
}

func TestBuildNginxRateLimitFragment_Deterministic(t *testing.T) {
	// Same input in different order → identical output.
	a := BuildNginxRateLimitFragment([]rateLimitDomain{
		{DomainID: ulidA, RateLimitRPS: 10},
		{DomainID: ulidB, RateLimitRPS: 20},
	}, 0)
	b := BuildNginxRateLimitFragment([]rateLimitDomain{
		{DomainID: ulidB, RateLimitRPS: 20},
		{DomainID: ulidA, RateLimitRPS: 10},
	}, 0)
	if a != b {
		t.Errorf("output not deterministic:\n%s\n---\n%s", a, b)
	}
}

func TestBuildNginxRateLimitFragment_CustomZoneSize(t *testing.T) {
	got := BuildNginxRateLimitFragment([]rateLimitDomain{
		{DomainID: ulidA, RateLimitRPS: 10},
	}, 20480)
	if !strings.Contains(got, ":20480k") {
		t.Errorf("custom zone size not applied: %q", got)
	}
}

func TestBuildRateLimitDirectives_BothZero(t *testing.T) {
	if got := BuildRateLimitDirectives(ulidA, 0, 0); got != "" {
		t.Errorf("expected empty for both-zero, got: %q", got)
	}
}

func TestBuildRateLimitDirectives_InvalidULID(t *testing.T) {
	if got := BuildRateLimitDirectives("not-a-ulid", 10, 5); got != "" {
		t.Errorf("expected empty for bad ULID, got: %q", got)
	}
}

func TestBuildRateLimitDirectives_BothSet(t *testing.T) {
	got := BuildRateLimitDirectives(ulidA, 10, 5)
	// burst = rps*2 = 20
	wantReq := "limit_req zone=rl_" + ulidA + " burst=20 nodelay;"
	wantConn := "limit_conn cn_" + ulidA + " 5;"
	if !strings.Contains(got, wantReq) {
		t.Errorf("missing req directive: %q", got)
	}
	if !strings.Contains(got, wantConn) {
		t.Errorf("missing conn directive: %q", got)
	}
}

func TestBuildRateLimitDirectives_BurstEscapesToLeastZero(t *testing.T) {
	// rps=1 → burst=2, not 0. Nginx rejects burst=0.
	got := BuildRateLimitDirectives(ulidA, 1, 0)
	if !strings.Contains(got, "burst=2") {
		t.Errorf("burst should be rps*2=2: %q", got)
	}
}
