package server

import (
	"context"
	"errors"
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/store/serviceaccount"
	"github.com/tidbcloud/AgentTeam/internal/unread"
)

type recordingServiceAccountUnreadRuntime struct {
	events []unread.ScopeInvalidated
	err    error
}

func (runtime *recordingServiceAccountUnreadRuntime) ApplyScopeInvalidatedWithDeltas(event unread.ScopeInvalidated) ([]unread.UnreadDelta, error) {
	runtime.events = append(runtime.events, event)
	if runtime.err != nil {
		return nil, runtime.err
	}
	return []unread.UnreadDelta{{
		WorkspaceID: event.WorkspaceID,
		UserID:      "usr_1",
		MemberID:    "mem_1",
		ScopeID:     event.ScopeID,
	}}, nil
}

type recordingServiceAccountUnreadSink struct {
	workspaceIDs []string
	deltas       []unread.UnreadDelta
	deleted      []serviceaccount.Account
}

func (sink *recordingServiceAccountUnreadSink) EmitUnreadUpdated(workspaceID string, delta unread.UnreadDelta) {
	sink.workspaceIDs = append(sink.workspaceIDs, workspaceID)
	sink.deltas = append(sink.deltas, delta)
}

func (sink *recordingServiceAccountUnreadSink) EmitServiceAccountDeleted(workspaceID string, accountID string, principalID string, scopeID string) {
	sink.deleted = append(sink.deleted, serviceaccount.Account{
		ID: accountID, WorkspaceID: workspaceID, PrincipalID: principalID, MessageScopeID: scopeID,
	})
}

func TestServiceAccountUnreadRuntimeBridgeInvalidatesEveryDeletedLocalScope(t *testing.T) {
	runtime := &recordingServiceAccountUnreadRuntime{}
	sink := &recordingServiceAccountUnreadSink{}
	bridge := newServiceAccountUnreadRuntimeBridge(runtime, sink)

	err := bridge.LocalAccountsDeleted(context.Background(), []serviceaccount.Account{
		{ID: " sacct_1 ", WorkspaceID: " wsp_1 ", PrincipalID: " svc_1 ", MessageScopeID: " sasc_1 "},
		{ID: "sacct_2", WorkspaceID: "wsp_2", PrincipalID: "svc_2", MessageScopeID: "sasc_2"},
		{WorkspaceID: "wsp_ignored"},
	})
	require.NoError(t, err)

	require.Equal(t, []unread.ScopeInvalidated{
		{WorkspaceID: "wsp_1", ScopeID: "sasc_1", Reason: "service_account_delete", Permanent: true},
		{WorkspaceID: "wsp_2", ScopeID: "sasc_2", Reason: "service_account_delete", Permanent: true},
	}, runtime.events)
	require.Equal(t, []string{"wsp_1", "wsp_2"}, sink.workspaceIDs)
	require.Len(t, sink.deltas, 2)
	require.Equal(t, []serviceaccount.Account{
		{ID: "sacct_1", WorkspaceID: "wsp_1", PrincipalID: "svc_1", MessageScopeID: "sasc_1"},
		{ID: "sacct_2", WorkspaceID: "wsp_2", PrincipalID: "svc_2", MessageScopeID: "sasc_2"},
	}, sink.deleted)
}

func TestServiceAccountUnreadRuntimeBridgeReturnsRuntimeInvalidationErrors(t *testing.T) {
	boom := errors.New("boom")
	bridge := newServiceAccountUnreadRuntimeBridge(
		&recordingServiceAccountUnreadRuntime{err: boom},
		&recordingServiceAccountUnreadSink{},
	)

	err := bridge.LocalAccountsDeleted(context.Background(), []serviceaccount.Account{{
		ID: "sacct_1", WorkspaceID: "wsp_1", PrincipalID: "svc_1", MessageScopeID: "sasc_1",
	}})

	require.ErrorIs(t, err, boom)
	require.ErrorContains(t, err, "invalidate deleted service account scope sasc_1")
}
