package notification

import (
	"context"
	"encoding/json"
	"net/http"
	"strings"
	"testing"
	"time"

	"github.com/prometheus/alertmanager/template"

	"github.com/pingcap-inc/clinic_cloud_server/pkg/entity"
)

func TestGenerateLarkBotSign(t *testing.T) {
	const (
		secret    = "test-secret"
		timestamp = int64(1700000000)
		expected  = "mbm4Y4oluIPQ00qlBIhX8vAZ0EKv3nw0LuTb91jPL84="
	)
	if actual := generateLarkBotSign(secret, timestamp); actual != expected {
		t.Fatalf("unexpected signature: %s", actual)
	}
}

func TestNewLarkBotPayload(t *testing.T) {
	startsAt := time.Date(2026, 7, 24, 1, 2, 3, 0, time.UTC)
	alert := template.Alert{
		Status: "firing",
		Labels: map[string]string{
			AlertLabelSeverity: "major",
		},
		Annotations: map[string]string{
			AlertAnnotationSubject: "high latency",
			AlertAnnotationContent: `cluster TiDBClusterName is slow.<br><a href="https://example.com">Details</a>`,
		},
		StartsAt: startsAt,
	}
	payload := newLarkBotPayload(alert, AlertMeta{
		ProjectName: "project-a",
		ClusterName: "cluster-a",
	}, "test-secret", 1700000000, false)

	if payload.MsgType != larkBotMessageTypeInteractive {
		t.Fatalf("unexpected message type: %s", payload.MsgType)
	}
	if payload.Timestamp != "1700000000" || payload.Sign == "" {
		t.Fatalf("signature fields were not populated: %#v", payload)
	}
	if payload.Card.Header.Template != "orange" {
		t.Fatalf("unexpected card template: %s", payload.Card.Header.Template)
	}
	content := payload.Card.Elements[0].Text.Content
	for _, expected := range []string{
		"**Status:** FIRING",
		"**Severity:** warning",
		"**Cluster:** cluster-a",
		"**Project:** project-a",
		"cluster cluster-a is slow.",
		"[Details](https://example.com)",
	} {
		if !strings.Contains(content, expected) {
			t.Errorf("card content does not contain %q: %s", expected, content)
		}
	}
	body, err := json.Marshal(payload)
	if err != nil {
		t.Fatalf("failed to marshal payload: %v", err)
	}
	if len(body) > larkBotMaxPayloadBytes {
		t.Fatalf("payload exceeds lark limit: %d", len(body))
	}
}

func TestNewLarkBotPayloadWithoutSecret(t *testing.T) {
	payload := newLarkBotPayload(template.Alert{
		Status:      "resolved",
		Labels:      map[string]string{AlertLabelSeverity: "critical"},
		Annotations: map[string]string{},
		StartsAt:    time.Now(),
	}, AlertMeta{}, "", 1700000000, false)

	if payload.Timestamp != "" || payload.Sign != "" {
		t.Fatalf("signature fields must be omitted without a secret: %#v", payload)
	}
	if payload.Card.Header.Template != "green" {
		t.Fatalf("resolved card must be green, got %s", payload.Card.Header.Template)
	}
}

func TestParseLarkBotResponse(t *testing.T) {
	tests := []struct {
		name     string
		body     string
		wantCode int
		wantErr  bool
	}{
		{
			name:     "current response",
			body:     `{"code":0,"msg":"success","StatusCode":0}`,
			wantCode: 0,
		},
		{
			name:     "business error",
			body:     `{"code":11232,"msg":"rate limited"}`,
			wantCode: 11232,
		},
		{
			name:     "legacy response",
			body:     `{"StatusCode":0,"StatusMessage":"success"}`,
			wantCode: 0,
		},
		{
			name:    "missing code",
			body:    `{"msg":"success"}`,
			wantErr: true,
		},
		{
			name:    "invalid json",
			body:    `{`,
			wantErr: true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			code, _, err := parseLarkBotResponse([]byte(tt.body))
			if (err != nil) != tt.wantErr {
				t.Fatalf("error = %v, wantErr %v", err, tt.wantErr)
			}
			if !tt.wantErr && code != tt.wantCode {
				t.Fatalf("code = %d, want %d", code, tt.wantCode)
			}
		})
	}
}

func TestLarkRetryPolicy(t *testing.T) {
	if !shouldRetryLarkHTTPStatus(http.StatusTooManyRequests) {
		t.Fatal("429 must be retried")
	}
	if !shouldRetryLarkHTTPStatus(http.StatusInternalServerError) {
		t.Fatal("5xx must be retried")
	}
	if shouldRetryLarkHTTPStatus(http.StatusBadRequest) {
		t.Fatal("4xx other than 429 must not be retried")
	}
	if !shouldRetryLarkBusinessCode(larkBotRateLimitCode) {
		t.Fatal("lark rate limit business code must be retried")
	}
	if shouldRetryLarkBusinessCode(19021) {
		t.Fatal("signature errors must not be retried")
	}
	if larkRetryDelay(0) != time.Second || larkRetryDelay(1) != 2*time.Second {
		t.Fatal("unexpected retry delay")
	}
}

func TestLarkRateLimiterIsPerSubscriber(t *testing.T) {
	notification := &LarkNotification{}
	first := entity.AlertSubscribersItem{ID: 1}
	second := entity.AlertSubscribersItem{ID: 2}
	if err := notification.waitForRateLimit(context.Background(), first); err != nil {
		t.Fatalf("first limiter wait failed: %v", err)
	}
	if err := notification.waitForRateLimit(context.Background(), second); err != nil {
		t.Fatalf("second limiter wait failed: %v", err)
	}
	firstLimiter, ok := notification.rateLimiters.Load("1")
	if !ok {
		t.Fatal("first subscriber limiter was not created")
	}
	secondLimiter, ok := notification.rateLimiters.Load("2")
	if !ok {
		t.Fatal("second subscriber limiter was not created")
	}
	if firstLimiter == secondLimiter {
		t.Fatal("different subscribers must not share a limiter")
	}
}

func TestTruncateUTF8ByBytes(t *testing.T) {
	value := strings.Repeat("告", 10)
	truncated := truncateUTF8ByBytes(value, 10)
	if !strings.HasSuffix(truncated, "…") {
		t.Fatalf("expected ellipsis: %s", truncated)
	}
	if !strings.HasPrefix(truncated, "告告告") {
		t.Fatalf("utf-8 content was truncated incorrectly: %s", truncated)
	}
}
