package mail

import (
	"encoding/json"
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/extension/providers/mail/connection"
	"github.com/tidbcloud/AgentTeam/internal/extension/registry"
	appregistry "github.com/tidbcloud/AgentTeam/internal/store/app/registry"
	"github.com/tidbcloud/AgentTeam/internal/store/serviceactor"
)

func validCatalogConnection(
	subjectRef string,
) registry.CatalogConnection {
	return registry.CatalogConnection{
		ID:                      "conn_1",
		WorkspaceID:             "wsp_1",
		ServiceDefinitionID:     appregistry.EmailAppID,
		ServiceInstallationID:   "inst_1",
		ServiceKind:             serviceactor.KindApp,
		Status:                  serviceactor.StatusActive,
		ExternalSubjectRef:      subjectRef,
		CurrentCredentialDigest: "sha256:" + "1",
		Config:                  validCatalogConfigJSON(),
	}
}

func validCatalogConfigJSON() []byte {
	config := connection.Config{
		SchemaVersion:         1,
		SenderAddress:         "team@example.com",
		SenderAddressAccepted: true,
		IMAP: connection.Endpoint{
			Host: "imap.example.com", Port: 993, TLSMode: "implicit",
			Capabilities: []string{
				"CONDSTORE", "IMAP4REV1", "SPECIAL-USE", "UIDPLUS",
			},
			DraftMailbox: "Drafts",
		},
		SMTP: connection.Endpoint{
			Host: "smtp.example.com", Port: 465, TLSMode: "implicit",
		},
	}
	digest, err := config.ComputeDigest()
	if err != nil {
		panic(err)
	}
	config.CapabilityDigest = digest
	encoded, err := json.Marshal(config)
	if err != nil {
		panic(err)
	}
	return encoded
}

func validPolicyInput(
	connections []registry.CatalogConnection,
	explicit bool,
) registry.CatalogPolicyInput {
	return registry.CatalogPolicyInput{
		WorkspaceID:           "wsp_1",
		ServiceDefinitionID:   appregistry.EmailAppID,
		ServiceInstallationID: "inst_1",
		DefinitionDomain:      Domain,
		DefinitionVersion:     1,
		DefinitionDigest:      "sha256:abc",
		Connections:           connections,
		GrantedScopes:         []string{"mail.send"},
		ExplicitAuthorization: explicit,
		Actions: []string{
			ActionSearchMessages,
			ActionSendDraft,
		},
	}
}

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

	actions, available, err := CatalogPolicy().FilterActions(
		validPolicyInput(
			[]registry.CatalogConnection{
				validCatalogConnection("email:h1:" + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"),
			},
			true,
		),
	)
	require.NoError(t, err)
	require.True(t, available)
	require.ElementsMatch(
		t,
		[]string{ActionSearchMessages, ActionSendDraft},
		actions,
	)
}

// TestCatalogPolicyRejectsWrongSubjectIdentity proves a connection whose
// external subject is not a Mail h1 HMAC identity (for example a raw
// imap:// URI) never yields an active catalog surface.
func TestCatalogPolicyRejectsWrongSubjectIdentity(t *testing.T) {
	t.Parallel()

	_, available, err := CatalogPolicy().FilterActions(
		validPolicyInput(
			[]registry.CatalogConnection{
				validCatalogConnection("imap://team@example.com"),
			},
			true,
		),
	)
	require.NoError(t, err)
	require.False(t, available)
}

// TestCatalogPolicyRejectsMultipleActiveConnections proves an ambiguity of
// active connections is unavailable rather than picking the strongest one.
func TestCatalogPolicyRejectsMultipleActiveConnections(t *testing.T) {
	t.Parallel()

	_, available, err := CatalogPolicy().FilterActions(
		validPolicyInput(
			[]registry.CatalogConnection{
				validCatalogConnection("email:h1:" + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"),
				validCatalogConnection("email:h1:" + "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210"),
			},
			true,
		),
	)
	require.NoError(t, err)
	require.False(t, available)
}

// TestCatalogPolicyRequiresExplicitAuthorization proves the Mail product
// contract holds even if the durable capability is rewritten to
// default-allow: without an explicit per-principal authorization the
// surface is unavailable.
func TestCatalogPolicyRequiresExplicitAuthorization(t *testing.T) {
	t.Parallel()

	_, available, err := CatalogPolicy().FilterActions(
		validPolicyInput(
			[]registry.CatalogConnection{
				validCatalogConnection("email:h1:" + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"),
			},
			false,
		),
	)
	require.NoError(t, err)
	require.False(t, available)
}

// TestCatalogPolicyRejectsMissingCredentialGeneration proves an empty
// credential-generation digest cannot serve the catalog.
func TestCatalogPolicyRejectsMissingCredentialGeneration(t *testing.T) {
	t.Parallel()

	connection := validCatalogConnection(
		"email:h1:" + "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
	)
	connection.CurrentCredentialDigest = ""
	_, available, err := CatalogPolicy().FilterActions(
		validPolicyInput([]registry.CatalogConnection{connection}, true),
	)
	require.NoError(t, err)
	require.False(t, available)
}
