package taskboard

import (
	"context"
	"testing"

	"github.com/stretchr/testify/require"

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

func TestMarkTaskReadAdvancesMessageAndActivityCursors(t *testing.T) {
	ctx := context.Background()
	fixture := seedTaskboardUserFixture(t, ctx)

	created, err := fixture.service.CreateTasks(ctx, CreateTasksRequest{
		Context: taskboardUserContext(fixture),
		Board:   BoardRef{ChannelID: fixture.channelID},
		Titles:  []string{"Read-state advances the unified Task conversation"},
	})
	require.NoError(t, err)
	require.Len(t, created.Tasks, 1)
	task := created.Tasks[0]

	checkpoint, err := fixture.service.AddTaskCheckpoint(ctx, AddTaskCheckpointRequest{
		Context: NewUserServiceContext(fixture.workspaceID, fixture.reviewerUserID, fixture.reviewerDisplayName),
		Task:    TaskRef{TaskID: task.ID},
		Checkpoint: TaskCheckpointInput{
			Text: "checkpoint should become readable Task attention",
		},
	})
	require.NoError(t, err)
	require.Equal(t, task.ID, checkpoint.Task.ID)

	_, err = fixture.service.UpdateTask(ctx, UpdateTaskRequest{
		Context: taskboardUserContext(fixture),
		Task:    TaskRef{TaskID: task.ID},
		Patch: TaskUpdatePatch{
			Description: stringPointer("task update after checkpoint must share one read cursor action"),
		},
	})
	require.NoError(t, err)

	checkpointSeq := taskScopeActivitySeqForTest(t, ctx, fixture.pool, task.TaskScopeID, "task.checkpoint_added")
	updatedSeq := taskScopeActivitySeqForTest(t, ctx, fixture.pool, task.TaskScopeID, "task.updated")
	require.Greater(t, updatedSeq, checkpointSeq)
	require.Equal(t, int64(0), taskScopeActivityReadSeqForTest(t, ctx, fixture.pool, task.TaskScopeID, fixture.ownerMemberID))
	insertTaskManualUnreadStateForTest(t, ctx, fixture.pool, task.TaskScopeID, fixture.ownerMemberID)
	require.True(t, taskManualUnreadStateForTest(t, ctx, fixture.pool, task.TaskScopeID, fixture.ownerMemberID))

	read, err := fixture.service.MarkTaskRead(ctx, MarkTaskReadRequest{
		Context: taskboardUserContext(fixture),
		Task:    TaskRef{TaskID: task.ID},
	})
	require.NoError(t, err)
	require.Equal(t, int64(0), read.ReadState.UnreadCount)
	require.Equal(t, updatedSeq, read.ReadState.LastActivityReadSeq)
	require.Equal(t, int64(2), read.ReadState.ManualUnreadGeneration)
	require.Equal(t, updatedSeq, taskScopeActivityReadSeqForTest(t, ctx, fixture.pool, task.TaskScopeID, fixture.ownerMemberID), "opening a Task must advance the typed-Activity read cursor")
	require.False(t, taskManualUnreadStateForTest(t, ctx, fixture.pool, task.TaskScopeID, fixture.ownerMemberID), "opening a task must clear conversation manual-unread state")
}

func insertTaskManualUnreadStateForTest(t *testing.T, ctx context.Context, db coredb.QueryExecer, taskScopeID string, memberID string) {
	t.Helper()

	_, err := db.Exec(ctx, `
INSERT INTO conversation_member_states (
  scope_id,
  member_id,
  manual_unread_at,
  manual_unread_seq,
  unread_generation,
  created_at,
  updated_at
) VALUES ($1, $2, NOW(), 1, 1, NOW(), NOW())
ON DUPLICATE KEY UPDATE
  manual_unread_at = VALUES(manual_unread_at),
  manual_unread_seq = VALUES(manual_unread_seq),
  unread_generation = unread_generation + 1,
  updated_at = VALUES(updated_at)
`, taskScopeID, memberID)
	require.NoError(t, err)
}

func taskManualUnreadStateForTest(t *testing.T, ctx context.Context, db coredb.QueryRower, taskScopeID string, memberID string) bool {
	t.Helper()

	var manualUnread bool
	err := db.QueryRow(ctx, `
SELECT (manual_unread_at IS NOT NULL) AS manual_unread
FROM conversation_member_states
WHERE scope_id = $1
  AND member_id = $2
`, taskScopeID, memberID).Scan(&manualUnread)
	require.NoError(t, err)
	return manualUnread
}

func taskScopeActivitySeqForTest(t *testing.T, ctx context.Context, db coredb.QueryRower, taskScopeID string, activityType string) int64 {
	t.Helper()

	var seq int64
	err := db.QueryRow(ctx, `
SELECT CASE WHEN MAX(seq) IS NULL THEN 0 ELSE MAX(seq) END
FROM scope_activity_events
WHERE scope_id = $1
  AND activity_type = $2
  AND activity_ref_type = 'task_event'
  AND suppressed_at IS NULL
`, taskScopeID, activityType).Scan(&seq)
	require.NoError(t, err)
	return seq
}

func taskScopeActivityReadSeqForTest(t *testing.T, ctx context.Context, db coredb.QueryRower, taskScopeID string, memberID string) int64 {
	t.Helper()

	var seq int64
	err := db.QueryRow(ctx, `
SELECT last_activity_global_seq
FROM scope_memberships
WHERE scope_id = $1
  AND member_id = $2
  AND effective_to_seq IS NULL
  AND removed_at IS NULL
  AND archived_at IS NULL
`, taskScopeID, memberID).Scan(&seq)
	require.NoError(t, err)
	return seq
}
