package grpcserver

import (
	"testing"

	pbv1 "git3.techno-world.net/lrosales/broad-announce/gen/go/broadannounce/v1"
	"git3.techno-world.net/lrosales/broad-announce/internal/pipeline"
	"google.golang.org/grpc/metadata"
)

func TestAuthenticate(t *testing.T) {
	sources := map[string]pipeline.SourceConfig{
		"acme-001:prom-prod": {
			CompanyID:       "acme-001",
			SourceID:        "prom-prod",
			HMACSecret:      []byte("s3cret"),
			RateLimitPerSec: 1000,
		},
	}

	tests := []struct {
		name      string
		apiKey    string
		wantErr   bool
		wantSrcID string
	}{
		{name: "valid key", apiKey: "acme-001:prom-prod:s3cret", wantErr: false, wantSrcID: "prom-prod"},
		{name: "wrong secret", apiKey: "acme-001:prom-prod:wrong", wantErr: true},
		{name: "unknown source", apiKey: "acme-001:unknown:s3cret", wantErr: true},
		{name: "malformed key", apiKey: "acme-001", wantErr: true},
		{name: "empty key", apiKey: "", wantErr: true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ctx := metadata.NewIncomingContext(t.Context(), metadata.MD{
				"authorization": []string{"Bearer " + tt.apiKey},
			})
			src, err := authenticate(ctx, sources)
			if tt.wantErr {
				if err == nil {
					t.Errorf("authenticate() = %v, want error", src)
				}
				return
			}
			if err != nil {
				t.Errorf("authenticate() error = %v, want nil", err)
				return
			}
			if src.SourceID != tt.wantSrcID {
				t.Errorf("authenticate() sourceID = %v, want %v", src.SourceID, tt.wantSrcID)
			}
		})
	}
}

func TestErrorCode(t *testing.T) {
	tests := []struct {
		reason string
		want   pbv1.Error_Code
	}{
		{"unknown_source", pbv1.Error_UNAUTHENTICATED},
		{"bad_signature", pbv1.Error_UNAUTHENTICATED},
		{"rate_limited_source", pbv1.Error_RATE_LIMITED},
		{"rate_limited_company", pbv1.Error_RATE_LIMITED},
		{"invalid", pbv1.Error_INVALID},
		{"invalid_json", pbv1.Error_INVALID},
		{"quarantined", pbv1.Error_INVALID},
		{"circuit_open", pbv1.Error_INTERNAL},
		{"broker_unavailable", pbv1.Error_INTERNAL},
		{"marshal_failed", pbv1.Error_INTERNAL},
		{"unknown_foo", pbv1.Error_UNKNOWN},
	}

	for _, tt := range tests {
		t.Run(tt.reason, func(t *testing.T) {
			if got := errorCode(tt.reason); got != tt.want {
				t.Errorf("errorCode(%q) = %v, want %v", tt.reason, got, tt.want)
			}
		})
	}
}

func TestParseRetryAfter(t *testing.T) {
	tests := []struct {
		input string
		want  int
		ok    bool
	}{
		{"123", 123, true},
		{"0", 0, true},
		{"5s", 5, true},
		{"", 0, false},
		{"abc", 0, false},
	}

	for _, tt := range tests {
		t.Run(tt.input, func(t *testing.T) {
			n, ok := parseRetryAfter(tt.input)
			if n != tt.want || ok != tt.ok {
				t.Errorf("parseRetryAfter(%q) = (%d, %v), want (%d, %v)",
					tt.input, n, ok, tt.want, tt.ok)
			}
		})
	}
}

func TestAlertToJSON(t *testing.T) {
	alert := &pbv1.Alert{
		CompanyId:  "acme-001",
		SourceId:   "prom-prod",
		Severity:   "critical",
		Category:   "monitoring",
		Title:      "CPU spike",
		Body:       "CPU usage above 90%",
		DedupeKey:  "cpu-spike-001",
		ClientTsMs: 1712000000000,
		Data:       map[string]string{"host": "prod-01", "value": "95"},
	}

	body, err := alertToJSON(alert)
	if err != nil {
		t.Fatalf("alertToJSON() error = %v", err)
	}

	bodyStr := string(body)
	for _, want := range []string{
		`"company_id":"acme-001"`,
		`"source_id":"prom-prod"`,
		`"severity":"critical"`,
		`"category":"monitoring"`,
		`"title":"CPU spike"`,
		`"dedupe_key":"cpu-spike-001"`,
		`"client_ts_ms":1712000000000`,
	} {
		if !contains(bodyStr, want) {
			t.Errorf("alertToJSON() body missing %q:\n%s", want, body)
		}
	}
}

func contains(s, substr string) bool {
	for i := 0; i <= len(s)-len(substr); i++ {
		if s[i:i+len(substr)] == substr {
			return true
		}
	}
	return false
}

func TestServerImplementsIngestServer(t *testing.T) {
	var _ pbv1.IngestServer = (*Server)(nil)
}
