package attachment

import (
	"context"
	"os"
	"testing"

	"github.com/stretchr/testify/require"
)

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

	require.Equal(
		t,
		"/api/v1/users/usr_1/avatar?v=avt_1",
		VersionedUserAvatarURL("usr_1", "avt_1"),
	)
	require.Equal(
		t,
		"/api/v1/workspaces/wsp_1/agents/agt_1/avatar?v=avt_1",
		VersionedAgentAvatarURL("wsp_1", "agt_1", "avt_1"),
	)
}

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

	require.True(t, isAgentTeamUserAvatarURL("/api/v1/users/usr_1/avatar?v=avt_1", "usr_1"))
	require.False(t, isAgentTeamUserAvatarURL("https://example.com/api/v1/users/usr_1/avatar?v=avt_1", "usr_1"))
}

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

	tx := &uploadCleanupTxStub{}
	txs := TxStore{tx: tx}
	avatarID, err := txs.UploadUserAvatar(context.Background(), "usr_1", "wsp_1", CreateAttachmentParams{
		Filename:    "avatar.png",
		ContentType: "image/png",
		Blob:        []byte("png"),
	})
	require.NoError(t, err)
	require.Len(t, tx.execArgs, 2)
	require.Equal(t, avatarID, tx.execArgs[0][0])
	require.Equal(t, VersionedUserAvatarURL("usr_1", avatarID), tx.execArgs[1][1])
}

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

	query, args, err := BuildSelectClaimableAttachmentsQuery("wsp-1", "thr-1", "mem-1", []string{"att-1", "att-2"})
	require.NoError(t, err)
	require.Equal(t, []any{"wsp-1", "thr-1", "mem-1", "att-1", "att-2"}, args)
	require.Contains(t, query, "WITH attachment_ids AS (")
	require.Contains(t, query, "SELECT $4 AS attachment_id")
	require.Contains(t, query, "SELECT $5 AS attachment_id")
	require.Contains(t, query, "a.content_type")
	require.Contains(t, query, "a.size_bytes")
	require.Contains(t, query, "a.scope_id = $2")
	require.Contains(t, query, "a.message_id IS NULL")
	require.NotContains(t, query, "a."+"space"+"_id")
	require.Contains(t, query, "FOR UPDATE")
	require.NotContains(t, query, "ANY(")
}

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

	query, args, err := BuildClaimAttachmentsForMessageUpdateQuery("wsp-1", "thr-1", "mem-1", "msg-1", []string{"att-1", "att-2"})
	require.NoError(t, err)
	require.Equal(t, []any{"wsp-1", "thr-1", "mem-1", "msg-1", "att-1", "att-2"}, args)
	require.Contains(t, query, "WITH attachment_ids AS (")
	require.Contains(t, query, "UPDATE attachments AS a")
	require.Contains(t, query, "SELECT $5 AS attachment_id")
	require.Contains(t, query, "SELECT $6 AS attachment_id")
	require.Contains(t, query, "a.scope_id = $2")
	require.Contains(t, query, "a.message_id IS NULL")
	require.NotContains(t, query, "a."+"space"+"_id")
	require.NotContains(t, query, "RETURNING")
	require.NotContains(t, query, "ANY(")
}

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

	query, args, err := BuildLoadAttachmentsForMessagesQuery("wsp-1", []string{"msg-1", "msg-2"})
	require.NoError(t, err)
	require.Equal(t, []any{"wsp-1", "msg-1", "msg-2"}, args)
	require.Contains(t, query, "WITH message_ids AS (")
	require.Contains(t, query, "SELECT $2 AS message_id")
	require.Contains(t, query, "SELECT $3 AS message_id")
	require.Contains(t, query, "a.content_type")
	require.Contains(t, query, "a.size_bytes")
	require.Contains(t, query, "message_ordinality")
	require.NotContains(t, query, "ANY(")
}

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

	query, args, err := BuildLoadAttachmentRowsForMessagesQuery("wsp-1", []string{"msg-1", "msg-2"})
	require.NoError(t, err)
	require.Equal(t, []any{"wsp-1", "msg-1", "msg-2"}, args)
	require.Contains(t, query, "message_ordinality")
	require.Contains(t, query, "a.storage_driver")
	require.Contains(t, query, "a.object_key")
	require.Contains(t, query, "a.payload")
	require.Contains(t, query, "a.created_at")
	require.NotContains(t, query, "ANY(")
}

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

	contents, err := os.ReadFile("app_attachments.go")
	require.NoError(t, err)
	source := string(contents)
	require.Contains(t, source, "authorizeAttachmentMessageReadForUser")
	require.Contains(t, source, "authorizeAttachmentMessageReadForMember")
	require.Contains(t, source, "record.MessageID")
	require.NotContains(t, source, "ensureUserCanAccessAttachmentScope")
	require.NotContains(t, source, "ensureMemberCanAccessAttachmentScope")
	require.NotContains(t, source, "LEFT JOIN scope_memberships task_sm")
	require.NotContains(t, source, "LEFT JOIN scope_memberships channel_sm")
	require.NotContains(t, source, "FROM "+legacyChannelAgentEntriesTableNameForTest())
}

func legacyChannelAgentEntriesTableNameForTest() string {
	return "channel" + "_" + "agent" + "_" + "entries"
}
