//go:build e2e

package messaging

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

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

func TestHumanDMDelivery(t *testing.T) {
	sc := testkit.NewHostPeerScenario(t)
	t.Logf("host userId=%s peer userId=%s workspaceId=%s", sc.Host.UserID, sc.Peer.UserID, sc.Workspace.ID)

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

	dm, status, raw, err := sc.HostAPI.OpenDMWithUser(ctx, sc.Peer.UserID)
	if err != nil {
		t.Fatalf("open dm from host to peer: %v", err)
	}
	assertions.RequireStatus(t, status, 200, raw)
	if dm.ID == "" {
		t.Fatalf("empty dm channel id")
	}
	t.Logf("opened dm channel: id=%s", dm.ID)

	messageText := testkit.UniqueMessageText("hello-from-a")
	sent, status, raw, err := sc.HostAPI.CreateMessage(ctx, dm.ID, messageText)
	if err != nil {
		t.Fatalf("send dm message: %v", err)
	}
	assertions.RequireStatus(t, status, 200, raw)
	if sent.Content != messageText {
		t.Fatalf("unexpected sent message content: got %q want %q", sent.Content, messageText)
	}
	t.Logf("host sent dm message: messageId=%s", sent.ID)

	peerDM, status, raw, err := sc.Peer.API.OpenDMWithUser(ctx, sc.Host.UserID)
	if err != nil {
		t.Fatalf("open dm from peer to host: %v", err)
	}
	assertions.RequireStatus(t, status, 200, raw)
	if peerDM.ID != dm.ID {
		t.Fatalf("peer dm channel mismatch: got %s want %s", peerDM.ID, dm.ID)
	}

	page, status, raw, err := sc.Peer.API.ListChannelMessages(ctx, dm.ID, 50)
	if err != nil {
		t.Fatalf("list dm messages as peer: %v", err)
	}
	assertions.RequireStatus(t, status, 200, raw)

	if !messagePageContains(page, messageText) {
		t.Fatalf("peer did not receive message %q in dm %s (messages=%d)", messageText, dm.ID, len(page.Messages))
	}
	t.Logf("peer received dm message: content=%s", strings.TrimSpace(messageText))
}

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

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

	first, status, raw, err := sc.HostAPI.OpenDMWithUser(ctx, sc.Peer.UserID)
	if err != nil {
		t.Fatalf("open first dm: %v", err)
	}
	assertions.RequireStatus(t, status, 200, raw)
	if first.ID == "" {
		t.Fatalf("first dm id is empty")
	}

	second, status, raw, err := sc.HostAPI.OpenDMWithUser(ctx, sc.Peer.UserID)
	if err != nil {
		t.Fatalf("open second dm: %v", err)
	}
	assertions.RequireStatus(t, status, 200, raw)
	if second.ID == "" {
		t.Fatalf("second dm id is empty")
	}
	if second.ID != first.ID {
		t.Fatalf("dm open should be idempotent: first=%s second=%s", first.ID, second.ID)
	}
	t.Logf("dm open is idempotent: dm=%s", first.ID)
}

func TestHumanDMMultiStepRoutingMatrix(t *testing.T) {
	sc := testkit.NewHostPeerScenario(t)
	env := sc.Env
	third := testkit.RequireRegisteredUser(t, env, "dm-third-user")
	thirdAPI := env.API.WithToken(third.AccessToken)

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

	dm, status, raw, err := sc.HostAPI.OpenDMWithUser(ctx, sc.Peer.UserID)
	if err != nil {
		t.Fatalf("host open dm with peer: %v", err)
	}
	assertions.RequireStatus(t, status, 200, raw)
	if dm.ID == "" {
		t.Fatalf("dm channel id is empty")
	}

	var hostMsg string
	t.Run("01-host-send-peer-can-read", func(t *testing.T) {
		hostMsg = testkit.UniqueMessageText("dm-host-msg")
		_, s, r, e := sc.HostAPI.CreateMessage(ctx, dm.ID, hostMsg)
		if e != nil {
			t.Fatalf("host send dm message: %v", e)
		}
		assertions.RequireStatus(t, s, 200, r)
		testkit.AssertMessageInChannel(t, sc.Peer.API, dm.ID, hostMsg)
	})

	var peerMsg string
	t.Run("02-peer-reply-host-can-read", func(t *testing.T) {
		peerMsg = testkit.UniqueMessageText("dm-peer-reply")
		_, s, r, e := sc.Peer.API.CreateMessage(ctx, dm.ID, peerMsg)
		if e != nil {
			t.Fatalf("peer send dm reply: %v", e)
		}
		assertions.RequireStatus(t, s, 200, r)
		testkit.AssertMessageInChannel(t, sc.HostAPI, dm.ID, peerMsg)
	})

	t.Run("03-peer-reopen-dm-same-channel", func(t *testing.T) {
		peerDM, s, r, e := sc.Peer.API.OpenDMWithUser(ctx, sc.Host.UserID)
		if e != nil {
			t.Fatalf("peer reopen dm: %v", e)
		}
		assertions.RequireStatus(t, s, 200, r)
		if peerDM.ID != dm.ID {
			t.Fatalf("dm id mismatch after peer reopen: got=%s want=%s", peerDM.ID, dm.ID)
		}
	})

	t.Run("04-third-user-cannot-read-dm", func(t *testing.T) {
		_, s, r, e := thirdAPI.ListChannelMessages(ctx, dm.ID, 20)
		if e != nil {
			t.Fatalf("third user read dm failed unexpectedly: %v", e)
		}
		if s == 200 {
			t.Fatalf("third user should not read dm messages, raw=%s", string(r))
		}
	})

	t.Run("05-third-user-cannot-send-dm", func(t *testing.T) {
		_, s, r, e := thirdAPI.CreateMessage(ctx, dm.ID, testkit.UniqueMessageText("dm-third-send"))
		if e != nil {
			t.Fatalf("third user send dm failed unexpectedly: %v", e)
		}
		if s == 200 {
			t.Fatalf("third user should not send dm messages, raw=%s", string(r))
		}
	})

	t.Run("06-dm-history-contains-both-directions", func(t *testing.T) {
		page, s, r, e := sc.HostAPI.ListChannelMessages(ctx, dm.ID, 50)
		if e != nil {
			t.Fatalf("host list dm history: %v", e)
		}
		assertions.RequireStatus(t, s, 200, r)
		if !messagePageContains(page, hostMsg) || !messagePageContains(page, peerMsg) {
			t.Fatalf("dm history missing host/peer messages, got=%d", len(page.Messages))
		}
	})
}

func messagePageContains(page client.AppMessagePage, content string) bool {
	for _, message := range page.Messages {
		if message.Content == content {
			return true
		}
	}
	return false
}
