package app

import (
	"context"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/message"
	"github.com/tidbcloud/AgentTeam/internal/store/model"
)

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

	ctx := context.Background()
	pool := startAppRollbackDB(t, ctx)
	store := New(pool, Options{})
	now := time.Now().UTC()

	owner := seedAppAdminFixture(t, pool, "dm_view_state_owner", now)
	peer := seedWorkspaceMemberFixture(t, pool, owner.WorkspaceID, "dmviewpeer", now)

	var dm message.DMChannel
	err := coredb.WithTx(ctx, store.db, func(tx coredb.Tx) error {
		opened, err := message.OpenDMChannelForMember(ctx, tx, message.MemberActor{
			WorkspaceID: owner.WorkspaceID,
			MemberID:    owner.MemberID,
		}, "dmviewpeer")
		if err != nil {
			return err
		}
		if _, err := store.ensureDMScopeProjection(ctx, tx, opened.ID); err != nil {
			return err
		}
		dm = opened
		return nil
	})
	require.NoError(t, err)

	_, _, err = store.SendAppMessageForUserWithOptions(
		ctx,
		model.User{ID: owner.UserID},
		owner.WorkspaceID,
		dm.ID,
		"owner message",
		nil,
		SendAppMessageOptions{},
	)
	require.NoError(t, err)

	ownerList, err := store.ListDMChannelsForUserWithLimit(ctx, owner.UserID, owner.WorkspaceID, 0)
	require.NoError(t, err)
	require.Len(t, ownerList, 1)
	require.False(t, ownerList[0].SidebarHidden)

	hidden, err := store.HideDMChannelForUser(ctx, owner.UserID, owner.WorkspaceID, dm.ID)
	require.NoError(t, err)
	require.True(t, hidden.SidebarHidden)

	ownerList, err = store.ListDMChannelsForUserWithLimit(ctx, owner.UserID, owner.WorkspaceID, 0)
	require.NoError(t, err)
	require.Len(t, ownerList, 1)
	require.True(t, ownerList[0].SidebarHidden)

	peerList, err := store.ListDMChannelsForUserWithLimit(ctx, peer.UserID, owner.WorkspaceID, 0)
	require.NoError(t, err)
	require.Len(t, peerList, 1)
	require.False(t, peerList[0].SidebarHidden)

	_, _, err = store.SendAppMessageForUserWithOptions(
		ctx,
		model.User{ID: peer.UserID},
		owner.WorkspaceID,
		dm.ID,
		"peer reply",
		nil,
		SendAppMessageOptions{},
	)
	require.NoError(t, err)

	ownerList, err = store.ListDMChannelsForUserWithLimit(ctx, owner.UserID, owner.WorkspaceID, 0)
	require.NoError(t, err)
	require.Len(t, ownerList, 1)
	require.False(t, ownerList[0].SidebarHidden)
}

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

	ctx := context.Background()
	pool := startAppRollbackDB(t, ctx)
	now := time.Unix(100, 0).UTC()
	owner := seedAppAdminFixture(t, pool, "dm_agent_batch_owner", now)
	peer := seedWorkspaceMemberFixture(t, pool, owner.WorkspaceID, "dm_agent_batch_peer", now)
	seedMachineWorkspaceAgent(t, pool, owner.WorkspaceID, owner.UserID, "mac_dm_owner_1", "agt_dm_owner_1", "mem_agt_dm_owner_1", "Owner Agent 1", "active", now)
	seedMachineWorkspaceAgent(t, pool, owner.WorkspaceID, owner.UserID, "mac_dm_owner_2", "agt_dm_owner_2", "mem_agt_dm_owner_2", "Owner Agent 2", "active", now)
	seedMachineWorkspaceAgent(t, pool, owner.WorkspaceID, peer.UserID, "mac_dm_peer", "agt_dm_peer", "mem_agt_dm_peer", "Peer Agent", "active", now)

	store := New(pool, Options{})
	openAgentDMForTest := func(agentMemberID string) string {
		t.Helper()
		var openedID string
		err := coredb.WithTx(ctx, store.db, func(tx coredb.Tx) error {
			dm, err := message.OpenDMChannelForMember(ctx, tx, message.MemberActor{
				WorkspaceID: owner.WorkspaceID,
				MemberID:    owner.MemberID,
			}, agentMemberID)
			if err != nil {
				return err
			}
			openedID = dm.ID
			_, err = store.ensureDMScopeProjection(ctx, tx, dm.ID)
			return err
		})
		require.NoError(t, err)
		return openedID
	}
	ownerDM1 := openAgentDMForTest("mem_agt_dm_owner_1")
	ownerDM2 := openAgentDMForTest("mem_agt_dm_owner_2")
	peerDM := openAgentDMForTest("mem_agt_dm_peer")
	mustExec(t, pool, `UPDATE scopes SET latest_message_at = $2 WHERE id = $1`, ownerDM1, now.Add(time.Minute))
	mustExec(t, pool, `UPDATE scopes SET latest_message_at = $2 WHERE id = $1`, ownerDM2, now.Add(2*time.Minute))
	mustExec(t, pool, `UPDATE scopes SET latest_message_at = $2 WHERE id = $1`, peerDM, now.Add(3*time.Minute))

	recordingDB := &appQueryRecordingDB{DB: pool}
	recordingStore := New(recordingDB, Options{})
	dms, err := recordingStore.ListDMChannelsForUserWithLimit(ctx, owner.UserID, owner.WorkspaceID, 10)
	require.NoError(t, err)

	agentIDs := make([]string, 0, len(dms))
	for _, dm := range dms {
		if dm.PeerType == "agent" {
			agentIDs = append(agentIDs, dm.PeerID)
		}
	}
	require.ElementsMatch(t, []string{"agt_dm_owner_1", "agt_dm_owner_2"}, agentIDs)
	limited, err := recordingStore.ListDMChannelsForUserWithLimit(ctx, owner.UserID, owner.WorkspaceID, 1)
	require.NoError(t, err)
	require.Len(t, limited, 1)
	require.Equal(t, "agt_dm_owner_2", limited[0].PeerID)
	require.NotEmpty(t, recordingDB.QueriesContaining("mac.created_by_user_id = $3"))
	require.Empty(t, recordingDB.QueriesContaining("WHERE a.workspace_id = $1 AND a.id = $2"))
	require.Empty(t, recordingDB.QueriesContaining("WHERE agents.workspace_id = $1 AND agents.id = $2"))
}
