package message

import (
	"context"
	"fmt"
	"testing"

	"github.com/stretchr/testify/require"

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

type recordingOwnerQuoteSourceMessageQueryer struct {
	query string
	args  []any
	rows  coredb.Rows
	err   error
}

func (queryer *recordingOwnerQuoteSourceMessageQueryer) Query(_ context.Context, query string, args ...any) (coredb.Rows, error) {
	queryer.query = query
	queryer.args = append([]any(nil), args...)
	if queryer.err != nil {
		return nil, queryer.err
	}
	if queryer.rows == nil {
		return &scriptedOwnerQuoteRows{}, nil
	}
	return queryer.rows, nil
}

func (queryer *recordingOwnerQuoteSourceMessageQueryer) QueryRow(context.Context, string, ...any) coredb.Row {
	return nil
}

type scriptedOwnerQuoteRows struct {
	rows   [][]any
	cursor int
	err    error
}

func (rows *scriptedOwnerQuoteRows) Close() {}

func (rows *scriptedOwnerQuoteRows) Err() error {
	return rows.err
}

func (rows *scriptedOwnerQuoteRows) Next() bool {
	if rows.cursor >= len(rows.rows) {
		return false
	}
	rows.cursor++
	return rows.cursor <= len(rows.rows)
}

func (rows *scriptedOwnerQuoteRows) Scan(dest ...any) error {
	if rows.cursor == 0 || rows.cursor > len(rows.rows) {
		return fmt.Errorf("scan called without active row")
	}
	row := rows.rows[rows.cursor-1]
	if len(row) != len(dest) {
		return fmt.Errorf("scan column mismatch: have %d want %d", len(row), len(dest))
	}
	for index, value := range row {
		switch target := dest[index].(type) {
		case *string:
			cast, ok := value.(string)
			if !ok {
				return fmt.Errorf("column %d: want string, got %T", index, value)
			}
			*target = cast
		case *int64:
			cast, ok := value.(int64)
			if !ok {
				return fmt.Errorf("column %d: want int64, got %T", index, value)
			}
			*target = cast
		default:
			return fmt.Errorf("unsupported scan target %T", dest[index])
		}
	}
	return nil
}

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

	queryer := &recordingOwnerQuoteSourceMessageQueryer{}
	sources := []OwnerQuoteSourceMessage{
		{ID: "msg-1"},
		{ID: "msg-2"},
	}

	attached, err := AttachOwnerQuoteAttachmentSnapshots(context.Background(), queryer, "wsp-1", sources)
	require.NoError(t, err)
	require.Equal(t, sources, attached)

	require.Contains(t, queryer.query, "JOIN attachments a USE INDEX (attachments_message_id_idx)")
	require.Contains(t, queryer.query, "ORDER BY message_ids.ordinality ASC, a.created_at ASC, a.id ASC")
	require.NotContains(t, queryer.query, "payload")
	require.NotContains(t, queryer.query, "EXISTS")
	require.Contains(t, queryer.query, "ordinality")
	require.Equal(t, []any{"wsp-1", "msg-1", "msg-2"}, queryer.args)
}

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

	queryer := &recordingOwnerQuoteSourceMessageQueryer{
		rows: &scriptedOwnerQuoteRows{
			rows: [][]any{
				{"msg-1", "att-1", "first.txt", "text/plain", int64(12)},
				{"msg-2", "att-2", "second.txt", "text/markdown", int64(34)},
				{"msg-2", "att-3", "third.txt", "text/plain", int64(56)},
			},
		},
	}

	sources := []OwnerQuoteSourceMessage{
		{ID: "msg-1"},
		{ID: "msg-2"},
	}

	attached, err := AttachOwnerQuoteAttachmentSnapshots(context.Background(), queryer, "wsp-1", sources)
	require.NoError(t, err)
	require.Len(t, attached[0].Attachments, 1)
	require.Equal(t, []OwnerQuoteAttachmentSnapshot{
		{ID: "att-1", Filename: "first.txt", ContentType: "text/plain", SizeBytes: 12},
	}, attached[0].Attachments)
	require.Equal(t, []OwnerQuoteAttachmentSnapshot{
		{ID: "att-2", Filename: "second.txt", ContentType: "text/markdown", SizeBytes: 34},
		{ID: "att-3", Filename: "third.txt", ContentType: "text/plain", SizeBytes: 56},
	}, attached[1].Attachments)
}
