package alert

import (
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"

	"gitea.lerkolabs.com/lerkolabs/uptop/internal/models"
)

func TestHTTPProviderDiscord(t *testing.T) {
	var received map[string]string
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		json.NewDecoder(r.Body).Decode(&received)
		w.WriteHeader(200)
	}))
	defer srv.Close()

	p := GetProvider(models.AlertConfig{Type: "discord", Settings: map[string]string{"url": srv.URL}})
	if err := p.Send(context.Background(), "Test Title", "Test Body"); err != nil {
		t.Fatalf("Send: %v", err)
	}

	if received["content"] != "**Test Title**\nTest Body" {
		t.Errorf("unexpected payload: %s", received["content"])
	}
}

func TestHTTPProviderSlack(t *testing.T) {
	var received map[string]string
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		json.NewDecoder(r.Body).Decode(&received)
		w.WriteHeader(200)
	}))
	defer srv.Close()

	p := GetProvider(models.AlertConfig{Type: "slack", Settings: map[string]string{"url": srv.URL}})
	if err := p.Send(context.Background(), "Alert", "Message"); err != nil {
		t.Fatalf("Send: %v", err)
	}

	if received["text"] != "*Alert*\nMessage" {
		t.Errorf("unexpected payload: %s", received["text"])
	}
}

func TestHTTPProviderWebhook(t *testing.T) {
	var received map[string]string
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		json.NewDecoder(r.Body).Decode(&received)
		w.WriteHeader(200)
	}))
	defer srv.Close()

	p := GetProvider(models.AlertConfig{Type: "webhook", Settings: map[string]string{"url": srv.URL}})
	if err := p.Send(context.Background(), "Title", "Body"); err != nil {
		t.Fatalf("Send: %v", err)
	}

	if received["title"] != "Title" || received["message"] != "Body" || received["status"] != "alert" {
		t.Errorf("unexpected webhook payload: %v", received)
	}
}

func TestHTTPProviderErrorOnHTTP4xx(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(403)
	}))
	defer srv.Close()

	p := GetProvider(models.AlertConfig{Type: "discord", Settings: map[string]string{"url": srv.URL}})
	if err := p.Send(context.Background(), "Test", "Test"); err == nil {
		t.Fatal("expected error on 403 response")
	}
}

func TestNtfyProvider(t *testing.T) {
	var title, body string
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		title = r.Header.Get("Title")
		buf := make([]byte, 1024)
		n, _ := r.Body.Read(buf)
		body = string(buf[:n])
		w.WriteHeader(200)
	}))
	defer srv.Close()

	p := GetProvider(models.AlertConfig{Type: "ntfy", Settings: map[string]string{
		"url":   srv.URL,
		"topic": "test",
	}})
	if err := p.Send(context.Background(), "Alert Title", "Alert Body"); err != nil {
		t.Fatalf("Send: %v", err)
	}

	if title != "Alert Title" {
		t.Errorf("expected title 'Alert Title', got '%s'", title)
	}
	if body != "Alert Body" {
		t.Errorf("expected body 'Alert Body', got '%s'", body)
	}
}

func TestHTTPProviderTelegram(t *testing.T) {
	var received map[string]string
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		json.NewDecoder(r.Body).Decode(&received)
		w.WriteHeader(200)
	}))
	defer srv.Close()

	p := &HTTPProvider{URL: srv.URL, Payload: telegramPayload("12345")}
	if err := p.Send(context.Background(), "Alert", "Down"); err != nil {
		t.Fatalf("Send: %v", err)
	}
	if received["chat_id"] != "12345" {
		t.Errorf("expected chat_id '12345', got '%s'", received["chat_id"])
	}
	if received["text"] != "*Alert*\nDown" {
		t.Errorf("unexpected text: %s", received["text"])
	}
	if received["parse_mode"] != "Markdown" {
		t.Errorf("expected parse_mode 'Markdown', got '%s'", received["parse_mode"])
	}
}

func TestHTTPProviderPagerDuty(t *testing.T) {
	var received map[string]any
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		json.NewDecoder(r.Body).Decode(&received)
		w.WriteHeader(200)
	}))
	defer srv.Close()

	p := &HTTPProvider{URL: srv.URL, Payload: pagerdutyPayload("test-key", "critical")}
	if err := p.Send(context.Background(), "Alert", "Down"); err != nil {
		t.Fatalf("Send: %v", err)
	}
	if received["routing_key"] != "test-key" {
		t.Errorf("expected routing_key 'test-key', got '%v'", received["routing_key"])
	}
	if received["event_action"] != "trigger" {
		t.Errorf("expected event_action 'trigger', got '%v'", received["event_action"])
	}
	payload := received["payload"].(map[string]any)
	if payload["summary"] != "Alert: Down" {
		t.Errorf("unexpected summary: %v", payload["summary"])
	}
	if payload["severity"] != "critical" {
		t.Errorf("expected severity 'critical', got '%v'", payload["severity"])
	}
}

func TestHTTPProviderPushover(t *testing.T) {
	var received map[string]string
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		json.NewDecoder(r.Body).Decode(&received)
		w.WriteHeader(200)
	}))
	defer srv.Close()

	p := &HTTPProvider{URL: srv.URL, Payload: pushoverPayload("app-tok", "user-key")}
	if err := p.Send(context.Background(), "Alert", "Down"); err != nil {
		t.Fatalf("Send: %v", err)
	}
	if received["token"] != "app-tok" {
		t.Errorf("expected token 'app-tok', got '%s'", received["token"])
	}
	if received["user"] != "user-key" {
		t.Errorf("expected user 'user-key', got '%s'", received["user"])
	}
	if received["title"] != "Alert" || received["message"] != "Down" {
		t.Errorf("unexpected payload: %v", received)
	}
}

func TestHTTPProviderGotify(t *testing.T) {
	var received map[string]any
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		json.NewDecoder(r.Body).Decode(&received)
		w.WriteHeader(200)
	}))
	defer srv.Close()

	p := &HTTPProvider{URL: srv.URL, Payload: gotifyPayload("8")}
	if err := p.Send(context.Background(), "Alert", "Down"); err != nil {
		t.Fatalf("Send: %v", err)
	}
	if received["title"] != "Alert" || received["message"] != "Down" {
		t.Errorf("unexpected payload: %v", received)
	}
	if pri, ok := received["priority"].(float64); !ok || pri != 8 {
		t.Errorf("expected priority 8, got %v", received["priority"])
	}
}

func TestHTTPProviderOpsgenie(t *testing.T) {
	var received map[string]any
	var authHeader string
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		authHeader = r.Header.Get("Authorization")
		json.NewDecoder(r.Body).Decode(&received)
		w.WriteHeader(202)
	}))
	defer srv.Close()

	p := GetProvider(models.AlertConfig{Type: "opsgenie", Settings: map[string]string{
		"api_key":  "test-genie-key",
		"priority": "P1",
	}})
	hp := p.(*HTTPProvider)
	hp.URL = srv.URL

	if err := p.Send(context.Background(), "Site Down", "mysite.com is unreachable"); err != nil {
		t.Fatalf("Send: %v", err)
	}
	if authHeader != "GenieKey test-genie-key" {
		t.Errorf("expected auth 'GenieKey test-genie-key', got '%s'", authHeader)
	}
	if received["message"] != "Site Down" {
		t.Errorf("unexpected message: %v", received["message"])
	}
	if received["description"] != "mysite.com is unreachable" {
		t.Errorf("unexpected description: %v", received["description"])
	}
	if received["source"] != "uptop" {
		t.Errorf("expected source 'uptop', got '%v'", received["source"])
	}
	if received["priority"] != "P1" {
		t.Errorf("expected priority 'P1', got '%v'", received["priority"])
	}
}

func TestOpsgenieEUEndpoint(t *testing.T) {
	p := GetProvider(models.AlertConfig{Type: "opsgenie", Settings: map[string]string{
		"api_key": "key", "eu": "true",
	}})
	hp := p.(*HTTPProvider)
	if hp.URL != "https://api.eu.opsgenie.com/v2/alerts" {
		t.Errorf("expected EU URL, got '%s'", hp.URL)
	}
}

func TestOpsgenieUSEndpoint(t *testing.T) {
	p := GetProvider(models.AlertConfig{Type: "opsgenie", Settings: map[string]string{
		"api_key": "key",
	}})
	hp := p.(*HTTPProvider)
	if hp.URL != "https://api.opsgenie.com/v2/alerts" {
		t.Errorf("expected US URL, got '%s'", hp.URL)
	}
}

func TestLimitMessage(t *testing.T) {
	short := "short"
	if got := limitMessage(short, 130); got != short {
		t.Errorf("expected '%s', got '%s'", short, got)
	}
	long := string(make([]byte, 200))
	if got := limitMessage(long, 130); len(got) != 130 {
		t.Errorf("expected length 130, got %d", len(got))
	}
}

func TestGetProviderNewTypes(t *testing.T) {
	for _, typ := range []string{"telegram", "pagerduty", "pushover", "gotify", "opsgenie"} {
		p := GetProvider(models.AlertConfig{Type: typ, Settings: map[string]string{
			"token": "x", "chat_id": "1", "routing_key": "k", "user": "u", "url": "http://localhost",
		}})
		if p == nil {
			t.Errorf("GetProvider(%q) returned nil", typ)
		}
	}
}

func TestGetProviderUnknown(t *testing.T) {
	p := GetProvider(models.AlertConfig{Type: "unknown"})
	if p != nil {
		t.Error("expected nil for unknown provider type")
	}
}

func TestSanitizeHeader(t *testing.T) {
	tests := []struct {
		input, want string
	}{
		{"normal subject", "normal subject"},
		{"inject\r\nBcc: evil@bad.com", "injectBcc: evil@bad.com"},
		{"has\nnewline", "hasnewline"},
		{"has\rcarriage", "hascarriage"},
	}
	for _, tt := range tests {
		got := sanitizeHeader(tt.input)
		if got != tt.want {
			t.Errorf("sanitizeHeader(%q) = %q, want %q", tt.input, got, tt.want)
		}
	}
}
