package taskboard

import (
	"context"
	"strings"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

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

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

	creatorAgentID := "agt_task_discussion_private_creator"
	creatorAgentMemberID := seedTaskboardOwnerPrivateAgent(
		t,
		ctx,
		fixture.pool,
		fixture.workspaceID,
		fixture.channelID,
		fixture.ownerMemberID,
		fixture.machineID,
		creatorAgentID,
		"Task Discussion Private Creator",
	)
	replacementAgentID := "agt_task_discussion_private_replacement"
	seedTaskboardOwnerPrivateAgent(
		t,
		ctx,
		fixture.pool,
		fixture.workspaceID,
		fixture.channelID,
		fixture.ownerMemberID,
		fixture.machineID,
		replacementAgentID,
		"Task Discussion Private Replacement",
	)

	created, err := fixture.service.CreateTasks(ctx, CreateTasksRequest{
		Context: NewAgentServiceContext(fixture.workspaceID, creatorAgentID, creatorAgentMemberID),
		Board:   BoardRef{Channel: "#all"},
		Task: &TaskCreateInput{
			Title:       "Keep owner discussion access after creator private agent deletion",
			AssigneeRef: creatorAgentID,
		},
	})
	require.NoError(t, err)
	require.Len(t, created.Tasks, 1)
	task := created.Tasks[0]

	var executionOwnerMemberID string
	require.NoError(t, fixture.pool.QueryRow(ctx, `
SELECT execution_owner_member_id
FROM tasks
WHERE id = $1
`, task.ID).Scan(&executionOwnerMemberID))
	require.Equal(t, fixture.ownerMemberID, executionOwnerMemberID, "new private-agent-created tasks should snapshot the human execution owner")

	// Simulate a legacy row that predates the snapshot column so the runtime
	// fallback path must still recover the owner from the removed private-agent
	// membership record.
	mustExecTaskboard(t, ctx, fixture.pool, `
UPDATE tasks
SET execution_owner_member_id = ''
WHERE id = $1
`, task.ID)

	removedAt := time.Date(2026, 4, 13, 10, 45, 0, 0, time.UTC)
	mustExecTaskboard(t, ctx, fixture.pool, `
DELETE FROM scope_notification_settings
WHERE member_id = $1
`, creatorAgentMemberID)
	mustExecTaskboard(t, ctx, fixture.pool, `
UPDATE scope_memberships
SET unread_generation = unread_generation + 1,
    removed_at = $2,
    updated_at = $2
WHERE member_id = $1
  AND removed_at IS NULL
`, creatorAgentMemberID, removedAt)
	mustExecTaskboard(t, ctx, fixture.pool, `
DELETE FROM agents
WHERE workspace_id = $1
  AND id = $2
`, fixture.workspaceID, creatorAgentID)

	updated, err := fixture.service.UpdateTask(ctx, UpdateTaskRequest{
		Context: NewUserServiceContext(fixture.workspaceID, fixture.ownerUserID, fixture.ownerDisplayName),
		Board:   BoardRef{ChannelID: fixture.channelID},
		Task:    TaskRef{TaskID: task.ID},
		Patch: TaskUpdatePatch{
			AssigneeRef: taskboardTestStringPtr(replacementAgentID),
		},
	})
	require.NoError(t, err)
	require.NotNil(t, updated.Task.AssigneeProfile)
	require.Equal(t, replacementAgentID, updated.Task.AssigneeProfile.PrincipalID)

	response, err := fixture.service.SendTaskScopeMessage(ctx, SendTaskScopeMessageRequest{
		Context: NewUserServiceContext(fixture.workspaceID, fixture.ownerUserID, fixture.ownerDisplayName),
		Task:    TaskRef{TaskID: task.ID},
		Message: TaskScopeMessageInput{
			Content: "owner can still comment after creator private agent deletion and reassign",
		},
	})
	require.NoError(t, err)
	require.Equal(t, fixture.ownerUserID, response.Message.CreatedBy.ID)
	require.Equal(t, fixture.ownerDisplayName, response.Message.CreatedBy.Name)
}

func seedTaskboardOwnerPrivateAgent(t *testing.T, ctx context.Context, db interface {
	Exec(context.Context, string, ...any) (coredb.Result, error)
}, workspaceID string, channelID string, ownerMemberID string, machineID string, agentID string, displayName string) string {
	t.Helper()

	now := time.Date(2026, 4, 13, 9, 40, 0, 0, time.UTC)
	memberID := "mem_" + agentID

	mustExecTaskboard(t, ctx, db, `
INSERT INTO members (id, workspace_id, type, display_name, status, created_at, updated_at)
VALUES ($1, $2, 'agent', $3, 'active', $4, $4)
`, memberID, workspaceID, displayName, now)

	mustExecTaskboard(t, ctx, db, `
INSERT IGNORE INTO scope_memberships (
  scope_id, member_id, visibility, owner_member_id, last_read_seq, last_read_global_seq,
  owner_context_dismissed_through_global_seq, created_at, updated_at, removed_at, archived_at
)
VALUES ($1, $2, 'private', $3, 0, 0, 0, $4, $4, NULL, NULL)
`, channelID, memberID, ownerMemberID, now)

	mustExecTaskboard(t, ctx, db, `
INSERT INTO agents (id, workspace_id, machine_id, member_id, name, avatar_url, runtime, model, reasoning_effort, description, session_id, auto_wake_enabled, status, activity, activity_detail, created_at, updated_at)
VALUES ($1, $2, $3, $4, $5, '', 'codex', 'gpt-5.4', '', '', '', TRUE, 'active', 'online', '', $6, $6)
`, agentID, workspaceID, machineID, memberID, strings.ToLower(strings.ReplaceAll(displayName, " ", "-")), now)

	return memberID
}
