package taskboard

import (
	"context"
	"testing"

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

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

	created, err := fixture.service.CreateTasks(ctx, CreateTasksRequest{
		Context: NewUserServiceContext(fixture.workspaceID, fixture.ownerUserID, fixture.ownerDisplayName),
		Board:   BoardRef{ChannelID: fixture.channelID},
		Titles:  []string{"Human repeated claim"},
	})
	require.NoError(t, err)
	require.Len(t, created.Tasks, 1)
	taskID := created.Tasks[0].ID

	first, err := fixture.service.ClaimTasks(ctx, ClaimTasksRequest{
		Context: NewUserServiceContext(fixture.workspaceID, fixture.ownerUserID, fixture.ownerDisplayName),
		Tasks:   []TaskRef{{TaskID: taskID}},
	})
	require.NoError(t, err)
	require.NotNil(t, first.Task.AssigneeProfile)
	require.NotNil(t, first.Task.AssignedAt)
	require.NotNil(t, first.Task.LastActivityAt)
	firstAssignedAt := *first.Task.AssignedAt
	firstLastActivityAt := *first.Task.LastActivityAt
	require.Equal(t, 1, countTaskAssignedActivities(t, ctx, fixture, taskID))

	second, err := fixture.service.ClaimTasks(ctx, ClaimTasksRequest{
		Context: NewUserServiceContext(fixture.workspaceID, fixture.ownerUserID, fixture.ownerDisplayName),
		Tasks:   []TaskRef{{TaskID: taskID}},
	})
	require.NoError(t, err)
	require.NotNil(t, second.Task.AssigneeProfile)
	require.Equal(t, fixture.ownerUserID, second.Task.AssigneeProfile.PrincipalID)
	require.NotNil(t, second.Task.AssignedAt)
	require.NotNil(t, second.Task.LastActivityAt)
	require.Equal(t, firstAssignedAt, *second.Task.AssignedAt)
	require.Equal(t, firstLastActivityAt, *second.Task.LastActivityAt)
	require.Equal(t, 1, countTaskAssignedActivities(t, ctx, fixture, taskID))
}

func TestAgentClaimIsIdempotentForCurrentAssignee(t *testing.T) {
	ctx := context.Background()
	fixture := seedTaskboardUserFixture(t, ctx)
	agentContext := NewAgentServiceContext(fixture.workspaceID, fixture.agentID, fixture.agentMemberID)

	created, err := fixture.service.CreateTasks(ctx, CreateTasksRequest{
		Context: agentContext,
		Board:   BoardRef{Channel: "#all"},
		Titles:  []string{"Agent repeated claim"},
	})
	require.NoError(t, err)
	require.Len(t, created.Tasks, 1)
	taskID := created.Tasks[0].ID
	taskNumber := created.Tasks[0].TaskNumber

	first, err := fixture.service.ClaimTasks(ctx, ClaimTasksRequest{
		Context: agentContext,
		Board:   BoardRef{Channel: "#all"},
		Tasks:   []TaskRef{{TaskNumber: taskNumber}},
	})
	require.NoError(t, err)
	require.Len(t, first.Tasks, 1)
	require.NotNil(t, first.Tasks[0].AssignedAt)
	firstAssignedAt := *first.Tasks[0].AssignedAt
	require.Equal(t, 1, countTaskAssignedActivities(t, ctx, fixture, taskID))

	second, err := fixture.service.ClaimTasks(ctx, ClaimTasksRequest{
		Context: agentContext,
		Board:   BoardRef{Channel: "#all"},
		Tasks:   []TaskRef{{TaskNumber: taskNumber}},
	})
	require.NoError(t, err)
	require.Len(t, second.Results, 1)
	require.True(t, second.Results[0].Success)
	require.Empty(t, second.ChangedTaskNumbers)
	require.Empty(t, second.Tasks)

	detail, err := fixture.service.GetTaskDetail(ctx, GetTaskDetailRequest{
		Context: agentContext,
		Board:   BoardRef{Channel: "#all"},
		Task:    TaskRef{TaskNumber: taskNumber},
	})
	require.NoError(t, err)
	require.NotNil(t, detail.Detail.Task.AssigneeProfile)
	require.Equal(t, fixture.agentID, detail.Detail.Task.AssigneeProfile.PrincipalID)
	require.NotNil(t, detail.Detail.Task.AssignedAt)
	require.Equal(t, firstAssignedAt, *detail.Detail.Task.AssignedAt)
	require.Equal(t, 1, countTaskAssignedActivities(t, ctx, fixture, taskID))
}

func countTaskAssignedActivities(t *testing.T, ctx context.Context, fixture taskboardUserFixture, taskID string) int {
	t.Helper()

	var count int
	require.NoError(t, fixture.pool.QueryRow(ctx, `
SELECT COUNT(*)
FROM tasks t
JOIN scope_activity_events ae
  ON ae.scope_id = t.task_scope_id
 AND ae.activity_ref_type = 'task_event'
WHERE t.id = $1
  AND JSON_UNQUOTE(JSON_EXTRACT(ae.payload, '$.taskEvent.type')) = 'assigned'
`, taskID).Scan(&count))
	return count
}
