package api

import (
	"strings"
	"testing"

	"github.com/alexei/tinyforge/internal/store"
)

func TestValidateWebhookURL(t *testing.T) {
	cases := []struct {
		name    string
		url     string
		wantErr string // substring; empty = pass
	}{
		{"https valid", "https://example.com/hook", ""},
		{"http valid", "http://example.com:8080/hook", ""},
		{"RFC1918 private LAN allowed", "http://192.168.1.50:9090/hook", ""},
		{"loopback rejected", "http://127.0.0.1:8090/hook", "loopback"},
		{"ipv6 loopback rejected", "http://[::1]:9000/hook", "loopback"},
		{"link-local rejected", "http://169.254.169.254/latest/meta-data", "reserved"},
		{"unspecified rejected", "http://0.0.0.0:9000/hook", "reserved"},
		{"file scheme rejected", "file:///etc/passwd", "http:// or https://"},
		{"missing host rejected", "https://", "missing host"},
		{"malformed url rejected", "://nope", "invalid URL"},
	}
	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			got := validateWebhookURL(c.url)
			if c.wantErr == "" {
				if got != "" {
					t.Fatalf("expected pass, got error: %q", got)
				}
				return
			}
			if !strings.Contains(got, c.wantErr) {
				t.Fatalf("error mismatch:\n  got:  %q\n  want substring: %q", got, c.wantErr)
			}
		})
	}
}

func TestValidateTrigger(t *testing.T) {
	cases := []struct {
		name string
		in   store.EventTrigger
		want string // substring of error; empty = pass
	}{
		{
			name: "missing name",
			in:   store.EventTrigger{ActionTarget: "https://x.example.com/h"},
			want: "name is required",
		},
		{
			name: "missing target",
			in:   store.EventTrigger{Name: "n"},
			want: "action_target is required",
		},
		{
			name: "bad scheme",
			in:   store.EventTrigger{Name: "n", ActionTarget: "ftp://x.example.com/h"},
			want: "http:// or https://",
		},
		{
			name: "loopback target",
			in:   store.EventTrigger{Name: "n", ActionTarget: "http://127.0.0.1/hook"},
			want: "loopback",
		},
		{
			name: "unsupported action_type",
			in:   store.EventTrigger{Name: "n", ActionType: "email", ActionTarget: "https://x.example.com/h"},
			want: "action_type must be",
		},
		{
			name: "invalid regex",
			in: store.EventTrigger{
				Name: "n", ActionTarget: "https://x.example.com/h",
				FilterMessageRegex: "([unclosed",
			},
			want: "filter_message_regex invalid",
		},
		{
			name: "all valid",
			in: store.EventTrigger{
				Name:         "n",
				ActionTarget: "https://x.example.com/h",
				FilterSeverity:     "warn,error",
				FilterMessageRegex: `\bpanic\b`,
			},
			want: "",
		},
	}
	for _, c := range cases {
		t.Run(c.name, func(t *testing.T) {
			got := validateTrigger(c.in)
			if c.want == "" {
				if got != "" {
					t.Fatalf("expected pass, got error: %q", got)
				}
				return
			}
			if !strings.Contains(got, c.want) {
				t.Fatalf("error mismatch:\n  got:  %q\n  want substring: %q", got, c.want)
			}
		})
	}
}

func TestRedactTriggerSecret(t *testing.T) {
	withSecret := store.EventTrigger{Name: "n", ActionSecret: "shh-real-secret"}
	got := redactTriggerSecret(withSecret)
	if got.ActionSecret != actionSecretPlaceholder {
		t.Errorf("expected placeholder, got %q", got.ActionSecret)
	}
	if withSecret.ActionSecret != "shh-real-secret" {
		t.Errorf("original mutated: %q", withSecret.ActionSecret)
	}

	noSecret := store.EventTrigger{Name: "n", ActionSecret: ""}
	got2 := redactTriggerSecret(noSecret)
	if got2.ActionSecret != "" {
		t.Errorf("empty secret should stay empty, got %q", got2.ActionSecret)
	}
}

func TestDerefString(t *testing.T) {
	if derefString(nil) != "" {
		t.Error("nil should deref to empty string")
	}
	s := "value"
	if derefString(&s) != "value" {
		t.Error("non-nil should deref to value")
	}
}

func TestFirstNonEmpty(t *testing.T) {
	if firstNonEmpty("a", "b") != "a" {
		t.Error("non-empty first wins")
	}
	if firstNonEmpty("", "b") != "b" {
		t.Error("fallback when first empty")
	}
}
