package channeladmin

import (
	"context"
	"fmt"
	"sync"
	"testing"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/coredb"
	"github.com/tidbcloud/AgentTeam/internal/store/agent"
	"github.com/tidbcloud/AgentTeam/internal/store/channel"
	"github.com/tidbcloud/AgentTeam/internal/store/message"
)

type paginationChannelStore struct {
	mu      sync.Mutex
	pages   map[string]channel.ManagedIMChannelPage
	options []channel.ManagedIMChannelListOptions
}

func (store *paginationChannelStore) CreateChannelInTx(context.Context, coredb.Tx, string, string, string, string, string) (message.ChannelSummary, error) {
	panic("unused")
}

func (store *paginationChannelStore) ListManagedIMChannelsForUserPage(
	_ context.Context,
	_ string,
	_ string,
	options channel.ManagedIMChannelListOptions,
) (channel.ManagedIMChannelPage, error) {
	store.mu.Lock()
	defer store.mu.Unlock()
	store.options = append(store.options, options)
	page := store.pages[options.Cursor]
	page.Channels = append([]message.ChannelSummary(nil), page.Channels...)
	return page, nil
}

func TestNormalizeChannelPageInput(t *testing.T) {
	cases := []struct {
		name       string
		input      PageInput
		wantLimit  int
		wantOffset int
		wantErr    bool
	}{
		{name: "defaults", input: PageInput{}, wantLimit: 50},
		{name: "explicit", input: PageInput{Limit: 25, Cursor: " 75 "}, wantLimit: 25, wantOffset: 75},
		{name: "negative limit", input: PageInput{Limit: -1}, wantErr: true},
		{name: "over max limit", input: PageInput{Limit: 101}, wantErr: true},
		{name: "invalid cursor", input: PageInput{Cursor: "opaque"}, wantErr: true},
		{name: "negative cursor", input: PageInput{Cursor: "-1"}, wantErr: true},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			limit, offset, err := normalizeChannelPageInput(tc.input)
			if tc.wantErr {
				require.ErrorIs(t, err, ErrValidation)
				return
			}
			require.NoError(t, err)
			require.Equal(t, tc.wantLimit, limit)
			require.Equal(t, tc.wantOffset, offset)
		})
	}
}

func TestListReturnsContinuationAfterCarrierFiltering(t *testing.T) {
	channelStore := &paginationChannelStore{pages: map[string]channel.ManagedIMChannelPage{
		"": {
			Channels: []message.ChannelSummary{
				{ChannelID: "chn_all", Slug: "all", Subsystem: "im"},
				{ChannelID: "chn_one", Slug: "one", Subsystem: "im"},
			},
			HasMore:    true,
			NextCursor: "2",
			TotalCount: 3,
		},
		"2": {
			Channels: []message.ChannelSummary{
				{ChannelID: "chn_two", Slug: "two", Subsystem: "im"},
			},
			TotalCount: 3,
		},
	}}
	service := New(Dependencies{
		Channels:   channelStore,
		Owners:     fakeOwnerResolver{userID: "usr_owner"},
		Workspaces: fakeWorkspaceResolver{workspaceID: "ws", memberID: "mem_owner", userID: "usr_owner"},
	})
	agent := agent.AuthenticatedAgent{AgentID: "agt_1", WorkspaceID: "ws"}

	first, err := service.List(context.Background(), agent, PageInput{Limit: 2})
	require.NoError(t, err)
	require.Equal(t, []string{"chn_all", "chn_one"}, channelViewIDs(first.Channels))
	require.True(t, first.HasMore)
	require.Equal(t, "2", first.NextCursor)
	require.Equal(t, 3, first.TotalCount)

	second, err := service.List(context.Background(), agent, PageInput{Limit: 2, Cursor: first.NextCursor})
	require.NoError(t, err)
	require.Equal(t, []string{"chn_two"}, channelViewIDs(second.Channels))
	require.False(t, second.HasMore)
	require.Empty(t, second.NextCursor)
	require.Equal(t, 3, second.TotalCount)

	channelStore.mu.Lock()
	require.Equal(t, []channel.ManagedIMChannelListOptions{
		{Limit: 2},
		{Limit: 2, Cursor: "2"},
	}, channelStore.options)
	channelStore.mu.Unlock()
}

func TestListContinuationExposesChannelOneHundredAndOne(t *testing.T) {
	summaries := make([]message.ChannelSummary, 0, 101)
	for index := 0; index < 101; index++ {
		summaries = append(summaries, message.ChannelSummary{
			ChannelID: fmt.Sprintf("chn_%03d", index),
			Slug:      fmt.Sprintf("channel-%03d", index),
			Subsystem: "im",
		})
	}
	channelStore := &paginationChannelStore{pages: map[string]channel.ManagedIMChannelPage{
		"": {
			Channels:   summaries[:100],
			HasMore:    true,
			NextCursor: "100",
			TotalCount: 101,
		},
		"100": {
			Channels:   summaries[100:],
			TotalCount: 101,
		},
	}}
	service := New(Dependencies{
		Channels:   channelStore,
		Owners:     fakeOwnerResolver{userID: "usr_owner"},
		Workspaces: fakeWorkspaceResolver{workspaceID: "ws", memberID: "mem_owner", userID: "usr_owner"},
	})
	agent := agent.AuthenticatedAgent{AgentID: "agt_1", WorkspaceID: "ws"}

	first, err := service.List(context.Background(), agent, PageInput{Limit: 100})
	require.NoError(t, err)
	require.Len(t, first.Channels, 100)
	require.True(t, first.HasMore)
	require.Equal(t, "100", first.NextCursor)
	require.Equal(t, 101, first.TotalCount)

	second, err := service.List(context.Background(), agent, PageInput{Limit: 100, Cursor: first.NextCursor})
	require.NoError(t, err)
	require.Equal(t, []string{"chn_100"}, channelViewIDs(second.Channels))
	require.False(t, second.HasMore)
	require.Equal(t, 101, second.TotalCount)
}

func channelViewIDs(views []ChannelView) []string {
	ids := make([]string, 0, len(views))
	for _, view := range views {
		ids = append(ids, view.ChannelID)
	}
	return ids
}
