package message

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

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/archive"
	"github.com/tidbcloud/AgentTeam/internal/coredb"
)

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

	ctx := context.Background()
	pool := startMessagingTestPool(t, ctx)
	now := time.Date(2026, 6, 10, 9, 0, 0, 0, time.UTC)
	archiveStorage, err := archive.NewFilesystem(t.TempDir())
	require.NoError(t, err)
	dataStore := New(pool, archiveStorage, 30, Options{})

	const (
		workspaceID     = "wsp_arch_short_index"
		userID          = "usr_arch_short_index"
		memberID        = "mem_arch_short_index"
		channelID       = "chn_arch_short_index"
		threadID        = "thr_arch_short_index"
		targetMessageID = "abcdef12000000000000000000000000"
	)

	insertMessagingWorkspace(t, ctx, pool, workspaceID, now)
	insertMessagingUser(t, ctx, pool, userID, "arch-short-index@example.com", now)
	insertMessagingHumanMember(t, ctx, pool, memberID, workspaceID, userID, now)
	insertMessagingChannel(t, ctx, pool, channelID, workspaceID, "arch-short-index", memberID, threadID, now)
	mustExecMessaging(t, ctx, pool, `
UPDATE scopes
SET latest_seq = $2,
    archived_until_seq = $2,
    updated_at = $3
WHERE id = $1
`, channelID, int64(2*archive.ChunkMessageCount), now)

	firstChunk := archivedShortIDLookupTestChunk(1, "00000001", memberID, now)
	firstChunk[499].ID = targetMessageID
	secondChunk := archivedShortIDLookupTestChunk(archive.ChunkMessageCount+1, "ffffffff", memberID, now)
	require.NoError(t, archiveStorage.WriteChunk(ctx, workspaceID, channelID, 1, firstChunk))
	require.NoError(t, archiveStorage.WriteChunk(ctx, workspaceID, channelID, archive.ChunkMessageCount+1, secondChunk))
	insertArchivedMessageIndexForTest(t, ctx, pool, workspaceID, channelID, firstChunk[499], now)

	tx, err := pool.BeginTx(ctx, coredb.TxOptions{})
	require.NoError(t, err)
	defer tx.Rollback(ctx)

	messageID, shortID, err := dataStore.InTx(tx).resolveArchivedParentMessageIDByShortID(ctx, tx, workspaceID, channelID, "abcdef")
	require.NoError(t, err)
	require.Equal(t, targetMessageID, messageID)
	require.Equal(t, "abcdef12", shortID)
}

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

	ctx := context.Background()
	pool := startMessagingTestPool(t, ctx)
	now := time.Date(2026, 6, 10, 9, 20, 0, 0, time.UTC)
	archiveStorage, err := archive.NewFilesystem(t.TempDir())
	require.NoError(t, err)
	dataStore := New(pool, archiveStorage, 30, Options{})

	const (
		workspaceID = "wsp_arch_short_no_index"
		userID      = "usr_arch_short_no_index"
		memberID    = "mem_arch_short_no_index"
		channelID   = "chn_arch_short_no_index"
		threadID    = "thr_arch_short_no_index"
	)

	insertMessagingWorkspace(t, ctx, pool, workspaceID, now)
	insertMessagingUser(t, ctx, pool, userID, "arch-short-no-index@example.com", now)
	insertMessagingHumanMember(t, ctx, pool, memberID, workspaceID, userID, now)
	insertMessagingChannel(t, ctx, pool, channelID, workspaceID, "arch-short-no-index", memberID, threadID, now)
	mustExecMessaging(t, ctx, pool, `
UPDATE scopes
SET latest_seq = $2,
    archived_until_seq = $2,
    updated_at = $3
WHERE id = $1
`, channelID, int64(2*archive.ChunkMessageCount), now)
	require.NoError(t, archiveStorage.WriteChunk(ctx, workspaceID, channelID, 1, archivedShortIDLookupTestChunk(1, "00000001", memberID, now)))
	require.NoError(t, archiveStorage.WriteChunk(ctx, workspaceID, channelID, archive.ChunkMessageCount+1, archivedShortIDLookupTestChunk(archive.ChunkMessageCount+1, "ffffffff", memberID, now)))

	tx, err := pool.BeginTx(ctx, coredb.TxOptions{})
	require.NoError(t, err)
	defer tx.Rollback(ctx)

	_, _, err = dataStore.InTx(tx).resolveArchivedParentMessageIDByShortID(ctx, tx, workspaceID, channelID, "abcdef")
	require.ErrorContains(t, err, "refusing unbounded archive scan")
}

func insertArchivedMessageIndexForTest(t *testing.T, ctx context.Context, pool coredb.QueryExecer, workspaceID string, scopeID string, message archive.Message, indexedAt time.Time) {
	t.Helper()

	shortID, ok := messageShortID8(message.ID)
	require.True(t, ok)
	mustExecMessaging(t, ctx, pool, `
INSERT INTO scope_archived_messages (
  workspace_id, scope_id, message_id, message_short_id, seq, indexed_at
) VALUES ($1, $2, $3, $4, $5, $6)
`, workspaceID, scopeID, message.ID, shortID, message.Seq, indexedAt)
}

func archivedShortIDLookupTestChunk(startSeq int64, shortID string, senderMemberID string, base time.Time) []archive.Message {
	messages := make([]archive.Message, 0, archive.ChunkMessageCount)
	for offset := int64(0); offset < archive.ChunkMessageCount; offset++ {
		seq := startSeq + offset
		messages = append(messages, archive.Message{
			ID:         fmt.Sprintf("%s%024x", shortID, seq),
			Seq:        seq,
			SenderType: "human",
			SenderName: senderMemberID,
			Content:    fmt.Sprintf("archived message %d", seq),
			Timestamp:  base.Add(time.Duration(seq) * time.Second),
		})
	}
	return messages
}
