//go:build e2e

package collaboration

import (
	"context"
	"testing"
	"time"

	"github.com/tidbcloud/AgentTeam/e2e/assertions"
	"github.com/tidbcloud/AgentTeam/e2e/client"
	"github.com/tidbcloud/AgentTeam/e2e/testkit"
)

func TestReplyThreadFromChannelMessage(t *testing.T) {
	sc := testkit.NewSingleUserScenario(t)

	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	allChannel := testkit.RequireAllChannel(t, sc.API)
	parentText := testkit.UniqueMessageText("parent-for-thread")
	parent, status, raw, err := sc.API.CreateMessage(ctx, allChannel.ID, parentText)
	if err != nil {
		t.Fatalf("create parent message: %v", err)
	}
	assertions.RequireStatus(t, status, 200, raw)
	if parent.ID == "" {
		t.Fatalf("empty parent message id")
	}

	thread, status, raw, err := sc.API.OpenReplyThread(ctx, allChannel.ID, parent.ID)
	if err != nil {
		t.Fatalf("open reply thread: %v", err)
	}
	assertions.RequireStatus(t, status, 200, raw)
	if thread.ThreadChannelID == "" {
		t.Fatalf("empty thread channel id")
	}
	t.Logf("opened reply thread: threadChannelId=%s parentMessageId=%s", thread.ThreadChannelID, parent.ID)

	replyText := testkit.UniqueMessageText("thread-reply")
	reply, status, raw, err := sc.API.CreateMessage(ctx, thread.ThreadChannelID, replyText)
	if err != nil {
		t.Fatalf("send thread reply: %v", err)
	}
	assertions.RequireStatus(t, status, 200, raw)
	if reply.ChannelID != thread.ThreadChannelID {
		t.Fatalf("reply channel mismatch: got %s want %s", reply.ChannelID, thread.ThreadChannelID)
	}

	testkit.AssertMessageInChannel(t, sc.API, thread.ThreadChannelID, replyText)
	t.Logf("thread reply delivered: messageId=%s", reply.ID)
}

func TestPrivateChannelTaskVisibility(t *testing.T) {
	sc := testkit.NewHostPeerScenario(t)

	ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
	defer cancel()

	privateName := testkit.UniqueChannelName("exec-room")
	privateChannel, status, raw, err := sc.HostAPI.CreateChannel(ctx, privateName, "private execution room")
	if err != nil {
		t.Fatalf("create private channel: %v", err)
	}
	assertions.RequireStatus(t, status, 200, raw)

	hiddenTitle := testkit.UniqueMessageText("hidden-task")
	created, status, raw, err := sc.HostAPI.CreateChannelTasks(ctx, privateChannel.ID, hiddenTitle)
	if err != nil {
		t.Fatalf("create private channel task: %v", err)
	}
	assertions.RequireStatus(t, status, 200, raw)
	if len(created.Tasks) != 1 {
		t.Fatalf("expected one private task, got %d", len(created.Tasks))
	}
	privateTaskID := created.Tasks[0].ID

	visibleTitle := testkit.UniqueMessageText("visible-task")
	allChannel := testkit.RequireAllChannel(t, sc.HostAPI)
	visibleCreated, status, raw, err := sc.HostAPI.CreateChannelTasks(ctx, allChannel.ID, visibleTitle)
	if err != nil {
		t.Fatalf("create #all task: %v", err)
	}
	assertions.RequireStatus(t, status, 200, raw)
	visibleTaskID := visibleCreated.Tasks[0].ID

	peerPage, status, raw, err := sc.Peer.API.ListWorkspaceTasks(ctx)
	if err != nil {
		t.Fatalf("list peer workspace tasks: %v", err)
	}
	assertions.RequireStatus(t, status, 200, raw)

	if taskPageContains(peerPage, privateTaskID) {
		t.Fatalf("peer should not see private channel task %s", privateTaskID)
	}
	if !taskPageContains(peerPage, visibleTaskID) {
		t.Fatalf("peer should see #all task %s", visibleTaskID)
	}
	t.Logf("task visibility matches channel membership")
}

func TestReplyThreadRoutingMatrix(t *testing.T) {
	sc := testkit.NewHostPeerScenario(t)
	ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
	defer cancel()

	allChannel := testkit.RequireAllChannel(t, sc.HostAPI)
	peerB := testkit.RequirePeerInWorkspace(t, sc.Env, sc.Host, sc.Workspace, "thread-peer-b")

	var parentID string
	var threadID string
	var hostReplyText string

	t.Run("01-host-creates-parent-message", func(t *testing.T) {
		parentText := testkit.UniqueMessageText("thread-parent")
		parent, status, raw, err := sc.HostAPI.CreateMessage(ctx, allChannel.ID, parentText)
		if err != nil {
			t.Fatalf("host create parent message: %v", err)
		}
		assertions.RequireStatus(t, status, 200, raw)
		if parent.ID == "" {
			t.Fatalf("parent message id is empty")
		}
		parentID = parent.ID
	})

	t.Run("02-host-opens-thread", func(t *testing.T) {
		thread, status, raw, err := sc.HostAPI.OpenReplyThread(ctx, allChannel.ID, parentID)
		if err != nil {
			t.Fatalf("host open reply thread: %v", err)
		}
		assertions.RequireStatus(t, status, 200, raw)
		if thread.ThreadChannelID == "" {
			t.Fatalf("thread channel id is empty")
		}
		threadID = thread.ThreadChannelID
	})

	t.Run("03-peer-opens-same-thread-idempotent", func(t *testing.T) {
		thread, status, raw, err := sc.Peer.API.OpenReplyThread(ctx, allChannel.ID, parentID)
		if err != nil {
			t.Fatalf("peer open reply thread: %v", err)
		}
		assertions.RequireStatus(t, status, 200, raw)
		if thread.ThreadChannelID != threadID {
			t.Fatalf("thread id mismatch from peer open: got=%s want=%s", thread.ThreadChannelID, threadID)
		}
	})

	t.Run("04-host-reply-visible-to-peer-and-peerB", func(t *testing.T) {
		hostReplyText = testkit.UniqueMessageText("thread-host-reply")
		reply, status, raw, err := sc.HostAPI.CreateMessage(ctx, threadID, hostReplyText)
		if err != nil {
			t.Fatalf("host reply in thread: %v", err)
		}
		assertions.RequireStatus(t, status, 200, raw)
		if reply.ChannelID != threadID {
			t.Fatalf("reply channel mismatch: got=%s want=%s", reply.ChannelID, threadID)
		}

		testkit.AssertMessageInChannel(t, sc.Peer.API, threadID, hostReplyText)
		testkit.AssertMessageInChannel(t, peerB.API, threadID, hostReplyText)
	})

	t.Run("05-peer-reply-visible-to-host", func(t *testing.T) {
		peerReply := testkit.UniqueMessageText("thread-peer-reply")
		_, status, raw, err := sc.Peer.API.CreateMessage(ctx, threadID, peerReply)
		if err != nil {
			t.Fatalf("peer reply in thread: %v", err)
		}
		assertions.RequireStatus(t, status, 200, raw)
		testkit.AssertMessageInChannel(t, sc.HostAPI, threadID, peerReply)
	})

	t.Run("06-thread-history-contains-bidirectional-replies", func(t *testing.T) {
		page, status, raw, err := sc.HostAPI.ListChannelMessages(ctx, threadID, 50)
		if err != nil {
			t.Fatalf("list thread messages: %v", err)
		}
		assertions.RequireStatus(t, status, 200, raw)
		if !messagePageContainsText(page.Messages, hostReplyText) {
			t.Fatalf("thread history missing host reply")
		}
	})
}

func taskPageContains(page client.TaskPage, id string) bool {
	for _, task := range page.Tasks {
		if task.ID == id {
			return true
		}
	}
	return false
}

func messagePageContainsText(messages []client.AppMessage, text string) bool {
	for _, msg := range messages {
		if msg.Content == text {
			return true
		}
	}
	return false
}
