package transport

import (
	"context"
	"errors"
	"net/netip"
	"testing"

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

type fakeResolver struct {
	answers [][]netip.Addr
	err     error
	calls   int
	hosts   []string
}

func (resolver *fakeResolver) LookupNetIP(
	_ context.Context,
	_ string,
	host string,
) ([]netip.Addr, error) {
	resolver.calls++
	resolver.hosts = append(resolver.hosts, host)
	if resolver.err != nil {
		return nil, resolver.err
	}
	index := resolver.calls - 1
	if index >= len(resolver.answers) {
		index = len(resolver.answers) - 1
	}
	return append([]netip.Addr(nil), resolver.answers[index]...), nil
}

func TestNormalizeHostnameRejectsLocalLiteralAndMalformedNames(
	t *testing.T,
) {
	t.Parallel()

	for _, host := range []string{
		"",
		"127.0.0.1",
		"::1",
		"localhost",
		"mail.localhost",
		"mail.local",
		"singlelabel",
		"bad_label.example.com",
		"-bad.example.com",
	} {
		_, err := NormalizeHostname(host)
		require.Error(t, err, host)
	}
	normalized, err := NormalizeHostname("IMAP.Example.COM.")
	require.NoError(t, err)
	require.Equal(t, "imap.example.com", normalized)
}

func TestValidateAddressRejectsEveryNonPublicAddressClass(t *testing.T) {
	t.Parallel()

	for _, raw := range []string{
		"0.0.0.0",
		"10.0.0.1",
		"100.64.0.1",
		"100.100.100.200",
		"127.0.0.1",
		"169.254.169.254",
		"172.16.0.1",
		"192.0.2.1",
		"192.168.1.1",
		"198.18.0.1",
		"198.51.100.1",
		"203.0.113.1",
		"224.0.0.1",
		"240.0.0.1",
		"::",
		"::1",
		"::ffff:127.0.0.1",
		"64:ff9b::c000:201",
		"100::1",
		"2001:db8::1",
		"2002::1",
		"fc00::1",
		"fe80::1",
		"ff02::1",
	} {
		err := ValidateAddress(netip.MustParseAddr(raw))
		require.Error(t, err, raw)
	}
	for _, raw := range []string{
		"8.8.8.8",
		"1.1.1.1",
		"2606:4700:4700::1111",
		"2001:4860:4860::8888",
	} {
		require.NoError(t, ValidateAddress(netip.MustParseAddr(raw)), raw)
	}
}

func TestResolveAllowedRejectsMixedAnswersAndResolverFailure(
	t *testing.T,
) {
	t.Parallel()

	resolver := &fakeResolver{answers: [][]netip.Addr{{
		netip.MustParseAddr("8.8.8.8"),
		netip.MustParseAddr("10.0.0.1"),
	}}}
	_, _, err := ResolveAllowed(
		context.Background(),
		resolver,
		"imap.example.com",
	)
	require.ErrorContains(t, err, "forbidden")

	resolver = &fakeResolver{err: errors.New("timeout")}
	_, _, err = ResolveAllowed(
		context.Background(),
		resolver,
		"imap.example.com",
	)
	require.ErrorContains(t, err, "timeout")
}
