package mail

import (
	"strings"
	"testing"

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

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

	shared := validInstallRequest()
	normalized, err := NormalizeInstallRequest(shared)
	require.NoError(t, err)
	require.NotNil(t, normalized.SMTP.Auth)
	require.Equal(t, normalized.IMAP.Auth, *normalized.SMTP.Auth)

	separate := validInstallRequest()
	separate.SMTPSameAsIMAP = false
	separate.SMTP.Auth = &Auth{
		Username: "submit-user",
		Password: "submit-secret",
	}
	normalized, err = NormalizeInstallRequest(separate)
	require.NoError(t, err)
	require.Equal(t, "submit-user", normalized.SMTP.Auth.Username)
}

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

	withUnexpectedAuth := validInstallRequest()
	withUnexpectedAuth.SMTP.Auth = &Auth{
		Username: "submit-user",
		Password: "submit-secret",
	}
	_, err := NormalizeInstallRequest(withUnexpectedAuth)
	require.ErrorContains(t, err, "forbidden")

	withoutRequiredAuth := validInstallRequest()
	withoutRequiredAuth.SMTPSameAsIMAP = false
	_, err = NormalizeInstallRequest(withoutRequiredAuth)
	require.ErrorContains(t, err, "required")
}

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

	tests := []struct {
		name    string
		mutate  func(*InstallRequest)
		wantErr bool
	}{
		{
			name: "IMAP implicit",
		},
		{
			name: "IMAP STARTTLS",
			mutate: func(request *InstallRequest) {
				request.IMAP.Port = 143
				request.IMAP.TLSMode = TLSModeStartTLS
			},
		},
		{
			name: "SMTP implicit",
			mutate: func(request *InstallRequest) {
				request.SMTP.Port = 465
				request.SMTP.TLSMode = TLSModeImplicit
			},
		},
		{
			name: "reject SMTP 25",
			mutate: func(request *InstallRequest) {
				request.SMTP.Port = 25
			},
			wantErr: true,
		},
		{
			name: "reject IMAP plaintext mode",
			mutate: func(request *InstallRequest) {
				request.IMAP.Port = 143
				request.IMAP.TLSMode = "plain"
			},
			wantErr: true,
		},
		{
			name: "reject implicit mode on STARTTLS port",
			mutate: func(request *InstallRequest) {
				request.SMTP.Port = 587
				request.SMTP.TLSMode = TLSModeImplicit
			},
			wantErr: true,
		},
	}
	for _, test := range tests {
		test := test
		t.Run(test.name, func(t *testing.T) {
			t.Parallel()
			request := validInstallRequest()
			if test.mutate != nil {
				test.mutate(&request)
			}
			_, err := NormalizeInstallRequest(request)
			if test.wantErr {
				require.Error(t, err)
			} else {
				require.NoError(t, err)
			}
		})
	}
}

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

	_, err := DecodeInstallRequest(strings.NewReader(`{
		"extensionName":"work-mail",
		"displayName":"Work Mail",
		"senderAddress":"CaseSensitive@example.com",
		"imap":{"host":"imap.example.com","port":993,"tlsMode":"implicit","auth":{"username":"u","password":"p"}},
		"smtpSameAsImap":true,
		"smtp":{"host":"smtp.example.com","port":587,"tlsMode":"starttls"},
		"provider":"forbidden"
	}`))
	require.ErrorContains(t, err, "unknown field")

	_, err = DecodeReconnectRequest(strings.NewReader(`{
		"senderAddress":"other@example.com",
		"imap":{"host":"imap.example.com","port":993,"tlsMode":"implicit","auth":{"username":"u","password":"p"}},
		"smtpSameAsImap":true,
		"smtp":{"host":"smtp.example.com","port":587,"tlsMode":"starttls"}
	}`))
	require.ErrorContains(t, err, "unknown field")
}

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

	request := validInstallRequest()
	request.IMAP.Auth.Password = ""
	_, err := NormalizeInstallRequest(request)
	require.ErrorContains(t, err, "authentication")

	request = validInstallRequest()
	request.IMAP.Auth.Password = strings.Repeat("x", maxPasswordBytes+1)
	_, err = NormalizeInstallRequest(request)
	require.ErrorContains(t, err, "authentication")
}

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

	for _, name := range []string{"Work-Mail", "work_mail", "-work-mail"} {
		request := validInstallRequest()
		request.ExtensionName = name
		_, err := NormalizeInstallRequest(request)
		require.ErrorContains(t, err, "name", name)
	}
	request := validInstallRequest()
	request.DisplayName = "Work\nMail"
	_, err := NormalizeInstallRequest(request)
	require.ErrorContains(t, err, "name")
}

func validInstallRequest() InstallRequest {
	return InstallRequest{
		ExtensionName: "work-mail",
		DisplayName:   "Work Mail",
		SenderAddress: "CaseSensitive@EXAMPLE.COM",
		IMAP: IMAPSettings{
			Host:    "imap.example.com",
			Port:    993,
			TLSMode: TLSModeImplicit,
			Auth: Auth{
				Username: "CaseSensitive@example.com",
				Password: "imap-secret",
			},
		},
		SMTPSameAsIMAP: true,
		SMTP: SMTPSettings{
			Host:    "smtp.example.com",
			Port:    587,
			TLSMode: TLSModeStartTLS,
		},
	}
}
