//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 TestSingleChannelVisibilityScenarios(t *testing.T) {
	sc := testkit.NewHostPeerScenario(t)
	ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second)
	defer cancel()

	peerB := testkit.RequirePeerInWorkspace(t, sc.Env, sc.Host, sc.Workspace, "single-chan-peer-b")

	channelName := testkit.UniqueChannelName("scope-single")
	privateChannel, status, raw, err := sc.HostAPI.CreateChannel(ctx, channelName, "single-channel visibility matrix")
	if err != nil {
		t.Fatalf("create private channel: %v", err)
	}
	assertions.RequireStatus(t, status, 200, raw)

	t.Run("01-host-sees-created-channel", func(t *testing.T) {
		hostChannels, s, r, e := sc.HostAPI.ListChannels(ctx)
		if e != nil {
			t.Fatalf("host list channels: %v", e)
		}
		assertions.RequireStatus(t, s, 200, r)
		if !channelListContains(hostChannels, privateChannel.ID) {
			t.Fatalf("host should see private channel %s", privateChannel.ID)
		}
	})

	t.Run("02-peerA-not-listed-before-add", func(t *testing.T) {
		peerChannels, s, r, e := sc.Peer.API.ListChannels(ctx)
		if e != nil {
			t.Fatalf("peerA list channels: %v", e)
		}
		assertions.RequireStatus(t, s, 200, r)
		if channelListContains(peerChannels, privateChannel.ID) {
			t.Fatalf("peerA should not see private channel before add")
		}
	})

	t.Run("03-peerA-cannot-read-before-add", func(t *testing.T) {
		assertCannotReadChannel(t, ctx, sc.Peer.API, privateChannel.ID, "peerA-read-before-add")
	})

	t.Run("04-peerA-cannot-send-before-add", func(t *testing.T) {
		assertCannotSendChannel(t, ctx, sc.Peer.API, privateChannel.ID, "peerA-send-before-add")
	})

	var hostSeedMsg string
	t.Run("05-host-send-seed-message", func(t *testing.T) {
		hostSeedMsg = testkit.UniqueMessageText("single-seed")
		_, s, r, e := sc.HostAPI.CreateMessage(ctx, privateChannel.ID, hostSeedMsg)
		if e != nil {
			t.Fatalf("host send seed: %v", e)
		}
		assertions.RequireStatus(t, s, 200, r)
		testkit.AssertMessageInChannel(t, sc.HostAPI, privateChannel.ID, hostSeedMsg)
	})

	t.Run("06-add-peerA-and-read-history", func(t *testing.T) {
		s, r, e := sc.HostAPI.AddChannelUserMember(ctx, privateChannel.ID, sc.Peer.UserID)
		if e != nil {
			t.Fatalf("add peerA: %v", e)
		}
		assertions.RequireStatus(t, s, 200, r)
		testkit.AssertMessageInChannel(t, sc.Peer.API, privateChannel.ID, hostSeedMsg)
	})

	t.Run("07-peerA-send-host-visible", func(t *testing.T) {
		peerText := testkit.UniqueMessageText("single-peerA-msg")
		_, s, r, e := sc.Peer.API.CreateMessage(ctx, privateChannel.ID, peerText)
		if e != nil {
			t.Fatalf("peerA send after add: %v", e)
		}
		assertions.RequireStatus(t, s, 200, r)
		testkit.AssertMessageInChannel(t, sc.HostAPI, privateChannel.ID, peerText)
	})

	t.Run("08-peerB-still-not-listed-before-add", func(t *testing.T) {
		peerChannels, s, r, e := peerB.API.ListChannels(ctx)
		if e != nil {
			t.Fatalf("peerB list channels before add: %v", e)
		}
		assertions.RequireStatus(t, s, 200, r)
		if channelListContains(peerChannels, privateChannel.ID) {
			t.Fatalf("peerB should not see private channel before add")
		}
	})

	t.Run("09-add-peerB-and-read-existing", func(t *testing.T) {
		s, r, e := sc.HostAPI.AddChannelUserMember(ctx, privateChannel.ID, peerB.UserID)
		if e != nil {
			t.Fatalf("add peerB: %v", e)
		}
		assertions.RequireStatus(t, s, 200, r)
		testkit.AssertMessageInChannel(t, peerB.API, privateChannel.ID, hostSeedMsg)
	})

	t.Run("10-peerB-send-both-peers-visible", func(t *testing.T) {
		peerBText := testkit.UniqueMessageText("single-peerB-msg")
		_, s, r, e := peerB.API.CreateMessage(ctx, privateChannel.ID, peerBText)
		if e != nil {
			t.Fatalf("peerB send: %v", e)
		}
		assertions.RequireStatus(t, s, 200, r)
		testkit.AssertMessageInChannel(t, sc.Peer.API, privateChannel.ID, peerBText)
		testkit.AssertMessageInChannel(t, sc.HostAPI, privateChannel.ID, peerBText)
	})
}

func TestMultiChannelMultiMemberRoutingMatrix(t *testing.T) {
	env := testkit.StartEnvironment(t)
	host := testkit.RequirePrimaryUser(t, env)
	workspace := testkit.RequireWorkspace(t, host)
	hostAPI := testkit.WorkspaceAPI(host, workspace)
	peerA := testkit.RequirePeerInWorkspace(t, env, host, workspace, "matrix-peer-a")
	peerB := testkit.RequirePeerInWorkspace(t, env, host, workspace, "matrix-peer-b")

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

	alpha, status, raw, err := hostAPI.CreateChannel(ctx, testkit.UniqueChannelName("matrix-alpha"), "alpha channel")
	if err != nil {
		t.Fatalf("create alpha: %v", err)
	}
	assertions.RequireStatus(t, status, 200, raw)
	beta, status, raw, err := hostAPI.CreateChannel(ctx, testkit.UniqueChannelName("matrix-beta"), "beta channel")
	if err != nil {
		t.Fatalf("create beta: %v", err)
	}
	assertions.RequireStatus(t, status, 200, raw)
	gamma, status, raw, err := hostAPI.CreateChannel(ctx, testkit.UniqueChannelName("matrix-gamma"), "gamma channel")
	if err != nil {
		t.Fatalf("create gamma: %v", err)
	}
	assertions.RequireStatus(t, status, 200, raw)

	for _, add := range []struct {
		channelID string
		userID    string
		label     string
	}{
		{alpha.ID, peerA.UserID, "peerA-alpha"},
		{gamma.ID, peerA.UserID, "peerA-gamma"},
		{beta.ID, peerB.UserID, "peerB-beta"},
		{gamma.ID, peerB.UserID, "peerB-gamma"},
	} {
		s, r, e := hostAPI.AddChannelUserMember(ctx, add.channelID, add.userID)
		if e != nil {
			t.Fatalf("add member %s: %v", add.label, e)
		}
		assertions.RequireStatus(t, s, 200, r)
	}

	allChannel := testkit.RequireAllChannel(t, hostAPI)

	alphaMsg := testkit.UniqueMessageText("matrix-alpha-host")
	betaMsg := testkit.UniqueMessageText("matrix-beta-host")
	gammaMsg := testkit.UniqueMessageText("matrix-gamma-host")
	allMsg := testkit.UniqueMessageText("matrix-all-host")
	for _, post := range []struct {
		channelID string
		content   string
		label     string
	}{
		{alpha.ID, alphaMsg, "host-alpha"},
		{beta.ID, betaMsg, "host-beta"},
		{gamma.ID, gammaMsg, "host-gamma"},
		{allChannel.ID, allMsg, "host-all"},
	} {
		_, s, r, e := hostAPI.CreateMessage(ctx, post.channelID, post.content)
		if e != nil {
			t.Fatalf("send %s: %v", post.label, e)
		}
		assertions.RequireStatus(t, s, 200, r)
	}

	t.Run("11-peerA-visible-alpha", func(t *testing.T) {
		testkit.AssertMessageInChannel(t, peerA.API, alpha.ID, alphaMsg)
	})
	t.Run("12-peerA-visible-gamma", func(t *testing.T) {
		testkit.AssertMessageInChannel(t, peerA.API, gamma.ID, gammaMsg)
	})
	t.Run("13-peerA-visible-all", func(t *testing.T) {
		testkit.AssertMessageInChannel(t, peerA.API, allChannel.ID, allMsg)
	})
	t.Run("14-peerA-cannot-read-beta", func(t *testing.T) {
		assertCannotReadChannel(t, ctx, peerA.API, beta.ID, "peerA-beta-read")
	})
	t.Run("15-peerA-cannot-send-beta", func(t *testing.T) {
		assertCannotSendChannel(t, ctx, peerA.API, beta.ID, "peerA-beta-send")
	})

	t.Run("16-peerB-visible-beta", func(t *testing.T) {
		testkit.AssertMessageInChannel(t, peerB.API, beta.ID, betaMsg)
	})
	t.Run("17-peerB-visible-gamma", func(t *testing.T) {
		testkit.AssertMessageInChannel(t, peerB.API, gamma.ID, gammaMsg)
	})
	t.Run("18-peerB-cannot-read-alpha", func(t *testing.T) {
		assertCannotReadChannel(t, ctx, peerB.API, alpha.ID, "peerB-alpha-read")
	})
	t.Run("19-peerB-cannot-send-alpha", func(t *testing.T) {
		assertCannotSendChannel(t, ctx, peerB.API, alpha.ID, "peerB-alpha-send")
	})
	t.Run("20-gamma-bidirectional-peerA-to-peerB", func(t *testing.T) {
		peerAGamma := testkit.UniqueMessageText("matrix-gamma-peerA")
		_, s, r, e := peerA.API.CreateMessage(ctx, gamma.ID, peerAGamma)
		if e != nil {
			t.Fatalf("peerA send gamma: %v", e)
		}
		assertions.RequireStatus(t, s, 200, r)
		testkit.AssertMessageInChannel(t, peerB.API, gamma.ID, peerAGamma)
		testkit.AssertMessageInChannel(t, hostAPI, gamma.ID, peerAGamma)
	})
}

func assertCannotReadChannel(t *testing.T, ctx context.Context, api *client.Client, channelID, label string) {
	t.Helper()
	_, status, raw, err := api.ListChannelMessages(ctx, channelID, testkit.MessageHistoryLimit())
	if err != nil {
		t.Fatalf("read forbidden channel (%s): %v", label, err)
	}
	if status == 200 {
		t.Fatalf("expected non-200 when reading forbidden channel (%s), raw=%s", label, string(raw))
	}
}

func assertCannotSendChannel(t *testing.T, ctx context.Context, api *client.Client, channelID, label string) {
	t.Helper()
	_, status, raw, err := api.CreateMessage(ctx, channelID, testkit.UniqueMessageText(label))
	if err != nil {
		t.Fatalf("send forbidden channel (%s): %v", label, err)
	}
	if status == 200 {
		t.Fatalf("expected non-200 when sending forbidden channel (%s), raw=%s", label, string(raw))
	}
}

func channelListContains(channels []client.Channel, channelID string) bool {
	for _, channel := range channels {
		if channel.ID == channelID {
			return true
		}
	}
	return false
}
