package providers

import (
	"context"
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/extension"
	"github.com/tidbcloud/AgentTeam/internal/extension/registry"
	"github.com/tidbcloud/AgentTeam/internal/extension/runner"
)

func TestValidateGenericProviderConnectionRequiresSupportedContract(t *testing.T) {
	valid := extension.Extension{
		Definition: registry.Definition{Domain: "valid"},
		Distribution: extension.Distribution{
			AppID:                     "app_valid",
			GenericProviderConnection: true,
		},
		Implementation: runner.Implementation{
			Auth: runner.AuthSpec{
				Kind: runner.AuthBearer,
				Fields: []runner.CredentialField{
					runner.APIKeyField("Token", "Provider token."),
				},
			},
			ValidateCredentials: func(context.Context, runner.Credentials) error {
				return nil
			},
		},
	}
	require.NoError(t, validateGenericProviderConnection(valid))
	prepareOnly := valid
	prepareOnly.Implementation.ValidateCredentials = nil
	prepareOnly.Implementation.PrepareConnection = func(
		context.Context,
		runner.Credentials,
	) (runner.ConnectionConfig, error) {
		return runner.ConnectionConfig{"topology": "aggregate"}, nil
	}
	prepareOnly.Implementation.ConnectionConfig =
		runner.ConnectionConfigContract{
			Fields: []runner.ConnectionConfigField{
				{
					Name:     "topology",
					Required: true,
					Values:   []string{"aggregate", "fallback"},
				},
			},
		}
	require.NoError(t, validateGenericProviderConnection(prepareOnly))

	tests := []struct {
		name   string
		mutate func(*extension.Extension)
		want   string
	}{
		{
			name: "no auth",
			mutate: func(ext *extension.Extension) {
				ext.Implementation.Auth = runner.AuthSpec{Kind: runner.AuthNone}
			},
			want: "non-empty auth contract",
		},
		{
			name: "invalid auth",
			mutate: func(ext *extension.Extension) {
				ext.Implementation.Auth = runner.AuthSpec{
					Kind:   runner.AuthHeader,
					Fields: []runner.CredentialField{runner.APIKeyField("Token", "Provider token.")},
				}
			},
			want: "invalid auth contract",
		},
		{
			name: "no validator",
			mutate: func(ext *extension.Extension) {
				ext.Implementation.ValidateCredentials = nil
				ext.Implementation.PrepareConnection = nil
			},
			want: "exactly one credential validator",
		},
		{
			name: "validator and preparer",
			mutate: func(ext *extension.Extension) {
				ext.Implementation.PrepareConnection = func(
					context.Context,
					runner.Credentials,
				) (runner.ConnectionConfig, error) {
					return nil, nil
				}
			},
			want: "exactly one credential validator",
		},
		{
			name: "config without preparer",
			mutate: func(ext *extension.Extension) {
				ext.Implementation.ConnectionConfig =
					runner.ConnectionConfigContract{
						Fields: []runner.ConnectionConfigField{
							{
								Name:   "topology",
								Values: []string{"aggregate"},
							},
						},
					}
			},
			want: "config without a preparer",
		},
		{
			name: "invalid config contract",
			mutate: func(ext *extension.Extension) {
				ext.Implementation.ValidateCredentials = nil
				ext.Implementation.PrepareConnection = func(
					context.Context,
					runner.Credentials,
				) (runner.ConnectionConfig, error) {
					return nil, nil
				}
				ext.Implementation.ConnectionConfig =
					runner.ConnectionConfigContract{
						Fields: []runner.ConnectionConfigField{
							{Name: "Topology", Values: []string{"aggregate"}},
						},
					}
			},
			want: "invalid config contract",
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			ext := valid
			test.mutate(&ext)
			require.ErrorContains(t, validateGenericProviderConnection(ext), test.want)
		})
	}

	notOptedIn := extension.Extension{}
	require.NoError(t, validateGenericProviderConnection(notOptedIn),
		"the zero-value distribution must remain default-off")
}
