package utils

import (
	"context"
	"errors"
	"net"
	"testing"

	"github.com/stretchr/testify/require"
)

func TestValidatePublicEndpointHostWithResolver(t *testing.T) {
	resolver := func(_ context.Context, host string) ([]net.IPAddr, error) {
		switch host {
		case "public.example.com":
			return []net.IPAddr{{IP: net.ParseIP("8.8.8.8")}}, nil
		case "private.example.com":
			return []net.IPAddr{{IP: net.ParseIP("10.1.2.3")}}, nil
		case "mixed.example.com":
			return []net.IPAddr{
				{IP: net.ParseIP("8.8.8.8")},
				{IP: net.ParseIP("192.168.1.10")},
			}, nil
		case "nil.example.com":
			return []net.IPAddr{{IP: nil}}, nil
		case "empty.example.com":
			return nil, nil
		default:
			return nil, errors.New("not found")
		}
	}

	tests := map[string]struct {
		host    string
		wantErr bool
	}{
		"public ipv4 literal":           {host: "8.8.8.8"},
		"public ipv6 literal":           {host: "2001:4860:4860::8888"},
		"bracketed public ipv6 literal": {host: "[2001:4860:4860::8888]"},
		"bracketed public ipv6 literal with whitespace": {
			host: " [2001:4860:4860::8888] ",
		},
		"public hostname":               {host: "public.example.com"},
		"private ipv4 literal":          {host: "10.0.0.1", wantErr: true},
		"private ipv6 literal":          {host: "fd00::1", wantErr: true},
		"loopback literal":              {host: "127.0.0.1", wantErr: true},
		"link local literal":            {host: "169.254.1.1", wantErr: true},
		"carrier grade nat literal":     {host: "100.64.1.1", wantErr: true},
		"private hostname":              {host: "private.example.com", wantErr: true},
		"mixed hostname":                {host: "mixed.example.com", wantErr: true},
		"hostname resolving to nil ip":  {host: "nil.example.com", wantErr: true},
		"hostname resolving to no ip":   {host: "empty.example.com", wantErr: true},
		"hostname resolving with error": {host: "missing.example.com", wantErr: true},
		"empty host":                    {host: "", wantErr: true},
	}

	for name, tc := range tests {
		t.Run(name, func(t *testing.T) {
			err := ValidatePublicEndpointHostWithResolver(context.Background(), tc.host, resolver)
			if tc.wantErr {
				require.Error(t, err)
				return
			}
			require.NoError(t, err)
		})
	}
}

func TestIsDisallowedPublicEndpointIP(t *testing.T) {
	require.False(t, IsDisallowedPublicEndpointIP(net.ParseIP("8.8.8.8")))
	require.True(t, IsDisallowedPublicEndpointIP(nil))
	require.True(t, IsDisallowedPublicEndpointIP(net.ParseIP("0.0.0.0")))
	require.True(t, IsDisallowedPublicEndpointIP(net.ParseIP("224.0.0.1")))
	require.True(t, IsDisallowedPublicEndpointIP(net.ParseIP("::1")))
	require.True(t, IsDisallowedPublicEndpointIP(net.ParseIP("fe80::1")))
}
