package domain

import (
	"testing"
)

func TestGenerateSlug(t *testing.T) {
	t.Run("generates correct length", func(t *testing.T) {
		slug, err := GenerateSlug()
		if err != nil {
			t.Fatalf("unexpected error: %v", err)
		}
		if len(slug) != SlugLength {
			t.Errorf("slug length = %d, want %d", len(slug), SlugLength)
		}
	})

	t.Run("contains only allowed characters", func(t *testing.T) {
		for i := 0; i < 100; i++ {
			slug, err := GenerateSlug()
			if err != nil {
				t.Fatalf("unexpected error: %v", err)
			}
			if err := ValidateSlug(slug); err != nil {
				t.Errorf("generated slug %q failed validation: %v", slug, err)
			}
		}
	})

	t.Run("generates unique slugs", func(t *testing.T) {
		seen := make(map[string]bool)
		for i := 0; i < 1000; i++ {
			slug, err := GenerateSlug()
			if err != nil {
				t.Fatalf("unexpected error: %v", err)
			}
			if seen[slug] {
				t.Errorf("duplicate slug generated: %s", slug)
			}
			seen[slug] = true
		}
	})
}

func TestValidateSlug(t *testing.T) {
	tests := []struct {
		slug    string
		wantErr bool
	}{
		{"k7m2x9p4", false},
		{"abcdefgh", false},
		{"23456789", false},
		{"", true},            // empty
		{"short", true},       // too short
		{"toolongslug", true}, // too long
		{"ABCDEFGH", true},    // uppercase
		{"k7m2x9p0", true},    // contains 0
		{"k7m2x9p1", true},    // contains 1
		{"k7m2x9po", true},    // contains o
		{"k7m2x9pl", true},    // contains l
		{"k7m2x9pi", true},    // contains i
		{"k7m2-9p4", true},    // contains hyphen
		{"k7m2_9p4", true},    // contains underscore
	}

	for _, tt := range tests {
		t.Run(tt.slug, func(t *testing.T) {
			err := ValidateSlug(tt.slug)
			if (err != nil) != tt.wantErr {
				t.Errorf("ValidateSlug(%q) error = %v, wantErr = %v", tt.slug, err, tt.wantErr)
			}
		})
	}
}

func TestDomainType(t *testing.T) {
	t.Run("Valid", func(t *testing.T) {
		tests := []struct {
			dtype DomainType
			want  bool
		}{
			{DomainTypePrimaryAuto, true},
			{DomainTypePrimaryCustom, true},
			{DomainTypeAlias, true},
			{DomainType("invalid"), false},
			{DomainType(""), false},
		}
		for _, tt := range tests {
			if got := tt.dtype.Valid(); got != tt.want {
				t.Errorf("%q.Valid() = %v, want %v", tt.dtype, got, tt.want)
			}
		}
	})

	t.Run("IsPrimary", func(t *testing.T) {
		tests := []struct {
			dtype DomainType
			want  bool
		}{
			{DomainTypePrimaryAuto, true},
			{DomainTypePrimaryCustom, true},
			{DomainTypeAlias, false},
		}
		for _, tt := range tests {
			if got := tt.dtype.IsPrimary(); got != tt.want {
				t.Errorf("%q.IsPrimary() = %v, want %v", tt.dtype, got, tt.want)
			}
		}
	})
}

func TestValidateSubdomain(t *testing.T) {
	tests := []struct {
		subdomain string
		wantErr   bool
	}{
		{"my-app", false},
		{"myapp123", false},
		{"a", false},
		{"landing-page", false},
		{"", true},       // empty
		{"-myapp", true}, // starts with hyphen
		{"123app", true}, // starts with number
		{"my_app", true}, // contains underscore
		{"MyApp", true},  // contains uppercase
		{"my.app", true}, // contains dot
		{"www", true},    // reserved
		{"api", true},    // reserved
		{"git", true},    // reserved
	}

	for _, tt := range tests {
		t.Run(tt.subdomain, func(t *testing.T) {
			err := ValidateSubdomain(tt.subdomain)
			if (err != nil) != tt.wantErr {
				t.Errorf("ValidateSubdomain(%q) error = %v, wantErr = %v", tt.subdomain, err, tt.wantErr)
			}
		})
	}
}

func TestValidateFQDN(t *testing.T) {
	tests := []struct {
		domain  string
		wantErr bool
	}{
		{"example.com", false},
		{"www.example.com", false},
		{"sub.domain.example.com", false},
		{"my-app.threesix.ai", false},
		{"a.b.c", false},
		{"", true},
		{"example", false},     // single label is valid
		{"-example.com", true}, // starts with hyphen
		{"example-.com", true}, // ends with hyphen
		{"exam ple.com", true}, // contains space
		{"example..com", true}, // double dot
	}

	for _, tt := range tests {
		t.Run(tt.domain, func(t *testing.T) {
			err := ValidateFQDN(tt.domain)
			if (err != nil) != tt.wantErr {
				t.Errorf("ValidateFQDN(%q) error = %v, wantErr = %v", tt.domain, err, tt.wantErr)
			}
		})
	}
}

func TestIsSubdomainOf(t *testing.T) {
	tests := []struct {
		domain     string
		baseDomain string
		want       bool
	}{
		{"my-app.threesix.ai", "threesix.ai", true},
		{"deep.sub.threesix.ai", "threesix.ai", true},
		{"example.com", "threesix.ai", false},
		{"threesix.ai", "threesix.ai", false},       // same domain, not subdomain
		{"MY-APP.THREESIX.AI", "threesix.ai", true}, // case insensitive
	}

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

func TestExtractSubdomain(t *testing.T) {
	tests := []struct {
		domain     string
		baseDomain string
		want       string
	}{
		{"my-app.threesix.ai", "threesix.ai", "my-app"},
		{"deep.sub.threesix.ai", "threesix.ai", "deep.sub"},
		{"example.com", "threesix.ai", ""},
		{"threesix.ai", "threesix.ai", ""},
		{"MY-APP.THREESIX.AI", "threesix.ai", "my-app"}, // case normalized
	}

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