// tenants_test.go — Pure-Go tests for the validation helpers and
// the input structs on the tenants store. The DB-backed paths
// (Create, Update, SetStatus, List) are tested in
// tenants_pg_test.go under the 'postgres' build tag.

package authd

import (
	"errors"
	"testing"
)

func TestValidSlug(t *testing.T) {
	cases := []struct {
		in   string
		want bool
	}{
		// valid
		{"a", false},
		{"ab", true},
		{"acme", true},
		{"acme-corp", true},
		{"a-1-b-2", true},
		{"x" + repeat("y", 62) + "z", true}, // exactly 64 chars
		// invalid
		{"", false},
		{"A", false},       // uppercase
		{"-acme", false},   // leading dash
		{"acme-", false},   // trailing dash
		{"acme--corp", true}, // double dash is allowed by the regex
		{"acme_corp", false}, // underscore
		{"acme.corp", false}, // dot
		{"acme corp", false}, // space
		{repeat("a", 65), false}, // too long
	}
	for _, c := range cases {
		if got := validSlug(c.in); got != c.want {
			t.Errorf("validSlug(%q) = %v, want %v", c.in, got, c.want)
		}
	}
}

func TestLooksLikeEmail(t *testing.T) {
	cases := []struct {
		in   string
		want bool
	}{
		{"a@b.c", true},
		{"ops@acme.test", true},
		{"first.last@sub.acme.test", true},
		{"a@b", false},      // no dot in domain
		{"@b.c", false},     // no local part
		{"a@.c", false},     // dot at start of domain
		{"a@b.", false},     // dot at end of domain
		{"", false},
		{"  a@b.c  ", true}, // trimmed
		{repeat("a", 251) + "@b.c", false}, // 255 chars (RFC max 254)
	}
	for _, c := range cases {
		if got := looksLikeEmail(c.in); got != c.want {
			t.Errorf("looksLikeEmail(%q) = %v, want %v", c.in, got, c.want)
		}
	}
}

func TestCreateTenantInput_Validate(t *testing.T) {
	good := CreateTenantInput{
		Slug: "acme", DisplayName: "Acme", ContactEmail: "ops@acme.test",
		RateLimitPerSec: 1000, FCMShared: nil,
	}
	if err := good.Validate(); err != nil {
		t.Errorf("good input rejected: %v", err)
	}

	cases := []struct {
		name string
		in   CreateTenantInput
		ok   bool
	}{
		{"empty slug", CreateTenantInput{Slug: "", DisplayName: "Acme", ContactEmail: "a@b.c", RateLimitPerSec: 100}, false},
		{"bad slug", CreateTenantInput{Slug: "Bad Slug", DisplayName: "Acme", ContactEmail: "a@b.c", RateLimitPerSec: 100}, false},
		{"empty display name", CreateTenantInput{Slug: "acme", DisplayName: "", ContactEmail: "a@b.c", RateLimitPerSec: 100}, false},
		{"bad email", CreateTenantInput{Slug: "acme", DisplayName: "Acme", ContactEmail: "not-an-email", RateLimitPerSec: 100}, false},
		{"rl=0", CreateTenantInput{Slug: "acme", DisplayName: "Acme", ContactEmail: "a@b.c", RateLimitPerSec: 0}, false},
		{"rl too high", CreateTenantInput{Slug: "acme", DisplayName: "Acme", ContactEmail: "a@b.c", RateLimitPerSec: 2_000_000}, false},
		{"rl at max", CreateTenantInput{Slug: "acme", DisplayName: "Acme", ContactEmail: "a@b.c", RateLimitPerSec: 1_000_000}, true},
		{"rl at min", CreateTenantInput{Slug: "acme", DisplayName: "Acme", ContactEmail: "a@b.c", RateLimitPerSec: 1}, true},
	}
	for _, c := range cases {
		err := c.in.Validate()
		if c.ok && err != nil {
			t.Errorf("%s: expected ok, got %v", c.name, err)
		}
		if !c.ok && err == nil {
			t.Errorf("%s: expected error, got nil", c.name)
		}
		if !c.ok && err != nil && !errors.Is(err, ErrTenantInvalid) {
			t.Errorf("%s: expected ErrTenantInvalid, got %v", c.name, err)
		}
	}
}

func TestUpdateTenantInput_Validate(t *testing.T) {
	// empty patch → ok
	empty := &UpdateTenantInput{}
	if err := empty.Validate(); err != nil {
		t.Errorf("empty patch rejected: %v", err)
	}
	dn := "Acme Corp"
	ce := "ops@acme.test"
	rl := 500
	good := &UpdateTenantInput{DisplayName: &dn, ContactEmail: &ce, RateLimitPerSec: &rl}
	if err := good.Validate(); err != nil {
		t.Errorf("good patch rejected: %v", err)
	}

	badName := ""
	if err := (&UpdateTenantInput{DisplayName: &badName}).Validate(); err == nil {
		t.Error("empty display_name should fail")
	}
	badEmail := "not-an-email"
	if err := (&UpdateTenantInput{ContactEmail: &badEmail}).Validate(); err == nil {
		t.Error("bad contact_email should fail")
	}
	badRl := 0
	if err := (&UpdateTenantInput{RateLimitPerSec: &badRl}).Validate(); err == nil {
		t.Error("rl=0 should fail")
	}
}

func repeat(s string, n int) string {
	out := make([]byte, 0, len(s)*n)
	for i := 0; i < n; i++ {
		out = append(out, s...)
	}
	return string(out)
}
