// Copyright (c) The OpenTofu Authors
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2023 HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package regsrc

import (
	"strings"
	"testing"
)

func TestFriendlyHost(t *testing.T) {
	tests := []struct {
		name        string
		source      string
		wantHost    string
		wantDisplay string
		wantNorm    string
		wantValid   bool
	}{
		{
			name:        "simple ascii",
			source:      "registry.opentofu.org",
			wantHost:    "registry.opentofu.org",
			wantDisplay: "registry.opentofu.org",
			wantNorm:    "registry.opentofu.org",
			wantValid:   true,
		},
		{
			name:        "mixed-case ascii",
			source:      "Registry.OpenTofu.org",
			wantHost:    "Registry.OpenTofu.org",
			wantDisplay: "registry.opentofu.org", // Display case folded
			wantNorm:    "registry.opentofu.org",
			wantValid:   true,
		},
		{
			name:        "IDN",
			source:      "ʎɹʇsıƃǝɹ.ɯɹoɟɐɹɹǝʇ.io",
			wantHost:    "ʎɹʇsıƃǝɹ.ɯɹoɟɐɹɹǝʇ.io",
			wantDisplay: "ʎɹʇsıƃǝɹ.ɯɹoɟɐɹɹǝʇ.io",
			wantNorm:    "xn--s-fka0wmm0zea7g8b.xn--o-8ta85a3b1dwcda1k.io",
			wantValid:   true,
		},
		{
			name:        "IDN TLD",
			source:      "zhongwen.中国",
			wantHost:    "zhongwen.中国",
			wantDisplay: "zhongwen.中国",
			wantNorm:    "zhongwen.xn--fiqs8s",
			wantValid:   true,
		},
		{
			name:        "IDN Case Folding",
			source:      "Испытание.com",
			wantHost:    "Испытание.com", // Raw input retains case
			wantDisplay: "испытание.com", // Display form is unicode but case-folded
			wantNorm:    "xn--80akhbyknj4f.com",
			wantValid:   true,
		},
		{
			name:        "Punycode is invalid as an input format",
			source:      "xn--s-fka0wmm0zea7g8b.xn--o-8ta85a3b1dwcda1k.io",
			wantHost:    "xn--s-fka0wmm0zea7g8b.xn--o-8ta85a3b1dwcda1k.io",
			wantDisplay: "ʎɹʇsıƃǝɹ.ɯɹoɟɐɹɹǝʇ.io",
			wantNorm:    InvalidHostString,
			wantValid:   false,
		},
		{
			name:        "non-host prefix is left alone",
			source:      "foo/bar/baz",
			wantHost:    "",
			wantDisplay: "",
			wantNorm:    "",
			wantValid:   false,
		},
	}
	for _, tt := range tests {
		// Matrix each test with prefix and total match variants
		for _, sfx := range []string{"", "/", "/foo/bar/baz"} {
			t.Run(tt.name+" suffix:"+sfx, func(t *testing.T) {
				gotHost, gotRest := ParseFriendlyHost(tt.source + sfx)

				if gotHost == nil {
					if tt.wantHost != "" {
						t.Fatalf("ParseFriendlyHost() gotHost = nil, want %v", tt.wantHost)
					}
					// If we return nil host, the whole input string should be in rest
					if gotRest != (tt.source + sfx) {
						t.Fatalf("ParseFriendlyHost() was nil rest = %s, want %v", gotRest, tt.source+sfx)
					}
					return
				}

				if tt.wantHost == "" {
					t.Fatalf("ParseFriendlyHost() gotHost.Raw = %v, want nil", gotHost.Raw)
				}

				if v := gotHost.String(); v != tt.wantHost {
					t.Fatalf("String() = %v, want %v", v, tt.wantHost)
				}
				if v := gotHost.Display(); v != tt.wantDisplay {
					t.Fatalf("Display() = %v, want %v", v, tt.wantDisplay)
				}
				if v := gotHost.Normalized(); v != tt.wantNorm {
					t.Fatalf("Normalized() = %v, want %v", v, tt.wantNorm)
				}
				if v := gotHost.Valid(); v != tt.wantValid {
					t.Fatalf("Valid() = %v, want %v", v, tt.wantValid)
				}
				if gotRest != strings.TrimLeft(sfx, "/") {
					t.Fatalf("ParseFriendlyHost() rest = %v, want %v", gotRest, strings.TrimLeft(sfx, "/"))
				}

				// Also verify that host compares equal with all the variants.
				if gotHost.Valid() && !gotHost.Equal(&FriendlyHost{Raw: tt.wantDisplay}) {
					t.Fatalf("Equal() should be true for %s and %s", tt.wantHost, tt.wantDisplay)
				}
			})
		}
	}
}

func TestInvalidHostEquals(t *testing.T) {
	invalid := NewFriendlyHost("NOT_A_HOST_NAME")
	valid := PublicRegistryHost

	// invalid hosts are not comparable
	if invalid.Equal(invalid) {
		t.Fatal("invalid host names are not comparable")
	}

	if valid.Equal(invalid) {
		t.Fatalf("%q is not equal to %q", valid, invalid)
	}

	puny := NewFriendlyHost("xn--s-fka0wmm0zea7g8b.xn--o-8ta85a3b1dwcda1k.io")
	display := NewFriendlyHost("ʎɹʇsıƃǝɹ.ɯɹoɟɐɹɹǝʇ.io")

	// The pre-normalized host is not a valid source, and therefore not
	// comparable to the display version.
	if display.Equal(puny) {
		t.Fatalf("invalid host %q should not be comparable", puny)
	}
}
