package privateagent

import (
	"context"
	"fmt"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/core/domain"
	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/attachment"
	"github.com/tidbcloud/AgentTeam/internal/store/message"
)

func TestScopeDeliveryForMessageRecordUsesPersistedPayload(t *testing.T) {
	ctx := context.Background()
	createdAt := time.Date(2026, 5, 21, 10, 0, 0, 0, time.UTC)
	encodedMetadata, _, err := encodeMessageMetadata(map[string]any{"side": "winner"})
	require.NoError(t, err)

	tx := &privateAgentReplayDeliveryTx{
		t:           t,
		workspaceID: "wsp-1",
		messageID:   "msg-1",
		attachments: []attachment.AppAttachment{
			{ID: "att-winner", Filename: "winner.txt", ContentType: "text/plain", SizeBytes: 1},
		},
	}
	store := New(nil, Options{
		LoadPublicMemberNamesByMemberID: func(ctx context.Context, queryer coredb.Queryer, workspaceID string, memberIDs []string) (map[string]string, error) {
			require.Equal(t, "wsp-1", workspaceID)
			require.Equal(t, []string{"mem-sender"}, memberIDs)
			return map[string]string{"mem-sender": "Sender"}, nil
		},
	})

	delivery, err := store.scopeDeliveryForMessageRecord(ctx, tx, "wsp-1", message.ThreadAccess{
		ThreadID:    "chn-1",
		ThreadType:  domain.ThreadTypeChannel,
		ChannelID:   "chn-1",
		ChannelName: "all",
	}, message.ScopeMessageRecord{
		ID:                 "msg-1",
		ScopeID:            "chn-1",
		Seq:                7,
		WorkspaceGlobalSeq: 9,
		ClientMessageID:    "client-1",
		MessageType:        "text",
		Content:            "winner content",
		Metadata:           encodedMetadata,
		CreatedAt:          createdAt,
	}, "mem-sender", domain.MemberTypeAgent, true)
	require.NoError(t, err)

	require.True(t, tx.queriedAttachments)
	require.True(t, delivery.IdempotentReplay)
	require.Equal(t, "msg-1", delivery.MessageID)
	require.Equal(t, "client-1", delivery.ClientMessageID)
	require.Equal(t, int64(7), delivery.Seq)
	require.Equal(t, int64(9), delivery.GlobalSeq)
	require.Equal(t, "Sender", delivery.Message.AuthorProfile.DisplayName)
	require.Equal(t, "winner content", delivery.Message.Content)
	require.Equal(t, "winner", delivery.Message.Metadata["side"])
	require.Equal(t, createdAt, delivery.Message.Timestamp)
	require.Equal(t, []attachment.AppAttachment{
		{ID: "att-winner", Filename: "winner.txt", ContentType: "text/plain", SizeBytes: 1},
	}, delivery.Message.Attachments)
}

type privateAgentReplayDeliveryTx struct {
	t                  *testing.T
	workspaceID        string
	messageID          string
	attachments        []attachment.AppAttachment
	queriedAttachments bool
}

func (tx *privateAgentReplayDeliveryTx) Query(ctx context.Context, query string, args ...any) (coredb.Rows, error) {
	tx.t.Helper()
	require.Contains(tx.t, query, "FROM attachments AS a")
	require.Equal(tx.t, []any{tx.workspaceID, tx.messageID}, args)
	tx.queriedAttachments = true
	return &privateAgentReplayDeliveryRows{
		messageID:   tx.messageID,
		attachments: tx.attachments,
	}, nil
}

func (tx *privateAgentReplayDeliveryTx) QueryRow(context.Context, string, ...any) coredb.Row {
	return privateAgentReplayDeliveryErrorRow{err: fmt.Errorf("unexpected QueryRow")}
}

func (tx *privateAgentReplayDeliveryTx) Exec(context.Context, string, ...any) (coredb.Result, error) {
	return nil, fmt.Errorf("unexpected Exec")
}

func (tx *privateAgentReplayDeliveryTx) Commit(context.Context) error {
	return fmt.Errorf("unexpected Commit")
}

func (tx *privateAgentReplayDeliveryTx) Rollback(context.Context) error {
	return fmt.Errorf("unexpected Rollback")
}

type privateAgentReplayDeliveryRows struct {
	messageID   string
	attachments []attachment.AppAttachment
	index       int
}

func (rows *privateAgentReplayDeliveryRows) Close() {}

func (rows *privateAgentReplayDeliveryRows) Err() error { return nil }

func (rows *privateAgentReplayDeliveryRows) Next() bool {
	if rows.index >= len(rows.attachments) {
		return false
	}
	rows.index++
	return true
}

func (rows *privateAgentReplayDeliveryRows) Scan(dest ...any) error {
	if rows.index == 0 || rows.index > len(rows.attachments) {
		return fmt.Errorf("scan before Next")
	}
	if len(dest) != 5 {
		return fmt.Errorf("expected 5 scan destinations, got %d", len(dest))
	}
	item := rows.attachments[rows.index-1]
	*dest[0].(*string) = rows.messageID
	*dest[1].(*string) = item.ID
	*dest[2].(*string) = item.Filename
	*dest[3].(*string) = item.ContentType
	*dest[4].(*int64) = item.SizeBytes
	return nil
}

type privateAgentReplayDeliveryErrorRow struct {
	err error
}

func (row privateAgentReplayDeliveryErrorRow) Scan(...any) error {
	return row.err
}
