package taskboard

import (
	"context"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/internal/store/testfixture"
)

type taskboardViewStateFixture struct {
	service            *Service
	workspaceID        string
	ownerUserID        string
	ownerDisplayName   string
	memberUserID       string
	memberDisplayName  string
	primaryChannelID   string
	secondaryChannelID string
}

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

	getResponse, err := fixture.service.GetTaskBoardViewState(ctx, GetTaskBoardViewStateRequest{
		Context: NewUserServiceContext(fixture.workspaceID, fixture.ownerUserID, fixture.ownerDisplayName),
		Board:   BoardRef{ChannelID: fixture.primaryChannelID},
	})
	require.NoError(t, err)
	require.Equal(t, "board", getResponse.ViewState.View)
	require.Equal(t, "manual", getResponse.ViewState.SortMode)
	require.Empty(t, getResponse.ViewState.SearchQuery)
	require.Empty(t, getResponse.ViewState.QuickFilters)
	require.Empty(t, getResponse.ViewState.StructuredFilters)
	require.False(t, getResponse.ViewState.DoneCollapsed)

	ownerPrimaryState := TaskBoardViewState{
		View:         "list",
		SearchQuery:  "release board",
		QuickFilters: []string{"blocked", "created_by_me"},
		StructuredFilters: map[string][]string{
			"kind":     []string{"feature", "legacy/custom"},
			"priority": []string{"none", "p1"},
		},
		SortMode:      "manual",
		DoneCollapsed: true,
	}
	putResponse, err := fixture.service.PutTaskBoardViewState(ctx, PutTaskBoardViewStateRequest{
		Context:   NewUserServiceContext(fixture.workspaceID, fixture.ownerUserID, fixture.ownerDisplayName),
		Board:     BoardRef{ChannelID: fixture.primaryChannelID},
		ViewState: ownerPrimaryState,
	})
	require.NoError(t, err)
	require.Equal(t, ownerPrimaryState, putResponse.ViewState)

	getResponse, err = fixture.service.GetTaskBoardViewState(ctx, GetTaskBoardViewStateRequest{
		Context: NewUserServiceContext(fixture.workspaceID, fixture.ownerUserID, fixture.ownerDisplayName),
		Board:   BoardRef{ChannelID: fixture.primaryChannelID},
	})
	require.NoError(t, err)
	require.Equal(t, ownerPrimaryState, getResponse.ViewState)

	ownerSecondaryState := TaskBoardViewState{
		View:              "list",
		SearchQuery:       "review",
		QuickFilters:      []string{"blocked"},
		StructuredFilters: map[string][]string{"assignee": {"none"}},
		SortMode:          "manual",
		DoneCollapsed:     false,
	}
	_, err = fixture.service.PutTaskBoardViewState(ctx, PutTaskBoardViewStateRequest{
		Context:   NewUserServiceContext(fixture.workspaceID, fixture.ownerUserID, fixture.ownerDisplayName),
		Board:     BoardRef{ChannelID: fixture.secondaryChannelID},
		ViewState: ownerSecondaryState,
	})
	require.NoError(t, err)

	getResponse, err = fixture.service.GetTaskBoardViewState(ctx, GetTaskBoardViewStateRequest{
		Context: NewUserServiceContext(fixture.workspaceID, fixture.ownerUserID, fixture.ownerDisplayName),
		Board:   BoardRef{ChannelID: fixture.secondaryChannelID},
	})
	require.NoError(t, err)
	require.Equal(t, ownerSecondaryState, getResponse.ViewState)

	memberState := TaskBoardViewState{
		View:              "board",
		SearchQuery:       "builder",
		QuickFilters:      []string{"assigned_to_agents"},
		StructuredFilters: map[string][]string{"assignee": {"agt_1"}},
		SortMode:          "manual",
		DoneCollapsed:     true,
	}
	_, err = fixture.service.PutTaskBoardViewState(ctx, PutTaskBoardViewStateRequest{
		Context:   NewUserServiceContext(fixture.workspaceID, fixture.memberUserID, fixture.memberDisplayName),
		Board:     BoardRef{ChannelID: fixture.primaryChannelID},
		ViewState: memberState,
	})
	require.NoError(t, err)

	getResponse, err = fixture.service.GetTaskBoardViewState(ctx, GetTaskBoardViewStateRequest{
		Context: NewUserServiceContext(fixture.workspaceID, fixture.ownerUserID, fixture.ownerDisplayName),
		Board:   BoardRef{ChannelID: fixture.primaryChannelID},
	})
	require.NoError(t, err)
	require.Equal(t, ownerPrimaryState, getResponse.ViewState)

	memberResponse, err := fixture.service.GetTaskBoardViewState(ctx, GetTaskBoardViewStateRequest{
		Context: NewUserServiceContext(fixture.workspaceID, fixture.memberUserID, fixture.memberDisplayName),
		Board:   BoardRef{ChannelID: fixture.primaryChannelID},
	})
	require.NoError(t, err)
	require.Equal(t, memberState, memberResponse.ViewState)
}

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

	response, err := fixture.service.PutTaskBoardViewState(ctx, PutTaskBoardViewStateRequest{
		Context: NewUserServiceContext(fixture.workspaceID, fixture.ownerUserID, fixture.ownerDisplayName),
		Board:   BoardRef{ChannelID: fixture.primaryChannelID},
		ViewState: TaskBoardViewState{
			View:         "my_work",
			SearchQuery:  "  release   train  ",
			QuickFilters: []string{"mine", "blocked", "created_by_me", "stale"},
			StructuredFilters: map[string][]string{
				"status":            {"todo"},
				"channel":           {fixture.primaryChannelID},
				"assignee":          {"agt_builder"},
				"currentAgent":      {"agt_builder"},
				"suggestedAssignee": {"agt_builder"},
				"priority":          {"p1"},
			},
			SortMode:      "priority",
			DoneCollapsed: true,
		},
	})
	require.NoError(t, err)
	require.Equal(t, TaskBoardViewState{
		View:         "board",
		SearchQuery:  "release train",
		QuickFilters: []string{"blocked", "created_by_me"},
		StructuredFilters: map[string][]string{
			"status":   {"todo"},
			"channel":  {fixture.primaryChannelID},
			"assignee": {"agt_builder"},
			"priority": {"p1"},
		},
		SortMode:      "priority",
		DoneCollapsed: true,
	}, response.ViewState)

	getResponse, err := fixture.service.GetTaskBoardViewState(ctx, GetTaskBoardViewStateRequest{
		Context: NewUserServiceContext(fixture.workspaceID, fixture.ownerUserID, fixture.ownerDisplayName),
		Board:   BoardRef{ChannelID: fixture.primaryChannelID},
	})
	require.NoError(t, err)
	require.Equal(t, response.ViewState, getResponse.ViewState)
}

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

	ownerContext := NewUserServiceContext(fixture.workspaceID, fixture.ownerUserID, fixture.ownerDisplayName)
	memberContext := NewUserServiceContext(fixture.workspaceID, fixture.memberUserID, fixture.memberDisplayName)
	ownerState := TaskBoardViewState{
		View:         "list",
		SearchQuery:  "  release   train  ",
		QuickFilters: []string{"blocked", "created_by_me", "unknown"},
		StructuredFilters: map[string][]string{
			"status":   {"in_progress", "in_progress"},
			"priority": {"p1", "p1"},
			"channel":  {fixture.primaryChannelID},
		},
		SortMode:      "recently_updated",
		DoneCollapsed: true,
	}

	emptyList, err := fixture.service.ListTaskBoardSavedViews(ctx, ListTaskBoardSavedViewsRequest{Context: ownerContext})
	require.NoError(t, err)
	require.Empty(t, emptyList.SavedViews)

	created, err := fixture.service.CreateTaskBoardSavedView(ctx, CreateTaskBoardSavedViewRequest{
		Context:   ownerContext,
		Name:      "  Release   Review  ",
		ViewState: ownerState,
	})
	require.NoError(t, err)
	require.NotEmpty(t, created.SavedView.ID)
	require.Equal(t, "Release Review", created.SavedView.Name)
	require.Equal(t, TaskBoardViewState{
		View:         "list",
		SearchQuery:  "release train",
		QuickFilters: []string{"blocked", "created_by_me"},
		StructuredFilters: map[string][]string{
			"status":   {"in_progress"},
			"priority": {"p1"},
			"channel":  {fixture.primaryChannelID},
		},
		SortMode:      "recently_updated",
		DoneCollapsed: true,
	}, created.SavedView.ViewState)

	_, err = fixture.service.CreateTaskBoardSavedView(ctx, CreateTaskBoardSavedViewRequest{
		Context:   ownerContext,
		Name:      "Release Review",
		ViewState: ownerState,
	})
	require.ErrorIs(t, err, ErrConflict)

	memberCreated, err := fixture.service.CreateTaskBoardSavedView(ctx, CreateTaskBoardSavedViewRequest{
		Context: memberContext,
		Name:    "Release Review",
		ViewState: TaskBoardViewState{
			View:         "board",
			QuickFilters: []string{"assigned_to_agents"},
			SortMode:     "priority",
		},
	})
	require.NoError(t, err)
	require.NotEqual(t, created.SavedView.ID, memberCreated.SavedView.ID)

	ownerList, err := fixture.service.ListTaskBoardSavedViews(ctx, ListTaskBoardSavedViewsRequest{Context: ownerContext})
	require.NoError(t, err)
	require.Len(t, ownerList.SavedViews, 1)
	require.Equal(t, created.SavedView.ID, ownerList.SavedViews[0].ID)

	updated, err := fixture.service.UpdateTaskBoardSavedView(ctx, UpdateTaskBoardSavedViewRequest{
		Context:     ownerContext,
		SavedViewID: created.SavedView.ID,
		Name:        "Blocked Bugs",
		ViewState: TaskBoardViewState{
			View:         "board",
			QuickFilters: []string{"blocked"},
			SortMode:     "priority",
		},
	})
	require.NoError(t, err)
	require.Equal(t, created.SavedView.ID, updated.SavedView.ID)
	require.Equal(t, "Blocked Bugs", updated.SavedView.Name)
	require.Equal(t, []string{"blocked"}, updated.SavedView.ViewState.QuickFilters)
	require.True(t, updated.SavedView.UpdatedAt.After(updated.SavedView.CreatedAt) || updated.SavedView.UpdatedAt.Equal(updated.SavedView.CreatedAt))

	memberList, err := fixture.service.ListTaskBoardSavedViews(ctx, ListTaskBoardSavedViewsRequest{Context: memberContext})
	require.NoError(t, err)
	require.Len(t, memberList.SavedViews, 1)
	require.Equal(t, memberCreated.SavedView.ID, memberList.SavedViews[0].ID)

	_, err = fixture.service.DeleteTaskBoardSavedView(ctx, DeleteTaskBoardSavedViewRequest{
		Context:     memberContext,
		SavedViewID: created.SavedView.ID,
	})
	require.ErrorIs(t, err, ErrNotFound)

	_, err = fixture.service.DeleteTaskBoardSavedView(ctx, DeleteTaskBoardSavedViewRequest{
		Context:     ownerContext,
		SavedViewID: created.SavedView.ID,
	})
	require.NoError(t, err)

	ownerList, err = fixture.service.ListTaskBoardSavedViews(ctx, ListTaskBoardSavedViewsRequest{Context: ownerContext})
	require.NoError(t, err)
	require.Empty(t, ownerList.SavedViews)
}

func seedTaskboardViewStateFixture(t *testing.T, ctx context.Context) taskboardViewStateFixture {
	t.Helper()

	pool := startTaskboardTestPool(t, ctx)
	now := time.Date(2026, 4, 15, 10, 0, 0, 0, time.UTC)

	fixture := taskboardViewStateFixture{
		service:            New(pool),
		workspaceID:        "wsp_task_view_state",
		ownerUserID:        "usr_task_view_state_owner",
		ownerDisplayName:   "Task View State Owner",
		memberUserID:       "usr_task_view_state_member",
		memberDisplayName:  "Task View State Member",
		primaryChannelID:   "chn_task_view_state_primary",
		secondaryChannelID: "chn_task_view_state_secondary",
	}

	mustExecTaskboard(t, ctx, pool, `
INSERT INTO users (id, email, name, display_name, avatar_url, is_admin, password_hash, email_verified_at, created_at, updated_at)
VALUES
    ($1, 'task-view-state-owner@example.com', 'task-view-state-owner', $2, '', FALSE, 'hash', $5, $5, $5),
    ($3, 'task-view-state-member@example.com', 'task-view-state-member', $4, '', FALSE, 'hash', $5, $5, $5)
`, fixture.ownerUserID, fixture.ownerDisplayName, fixture.memberUserID, fixture.memberDisplayName, now)

	mustExecTaskboard(t, ctx, pool, `
INSERT INTO workspaces (id, organization_id, slug, name, latest_message_seq, created_at, updated_at)
VALUES ($1, $2, 'task-view-state', 'Task View State Workspace', 0, $3, $3)
`, fixture.workspaceID, testfixture.SeedWorkspaceOrganization(t, ctx, pool, fixture.workspaceID, now), now)

	mustExecTaskboard(t, ctx, pool, `
INSERT INTO members (id, workspace_id, type, display_name, user_id, workspace_role, status, created_at, updated_at)
VALUES
    ('mem_task_view_state_owner', $1, 'human', $2, $3, 'owner', 'active', $5, $5),
    ('mem_task_view_state_member', $1, 'human', $4, $6, 'member', 'active', $5, $5)
`, fixture.workspaceID, fixture.ownerDisplayName, fixture.ownerUserID, fixture.memberDisplayName, now, fixture.memberUserID)

	seedTaskboardChannelScope(t, ctx, pool, fixture.workspaceID, fixture.primaryChannelID, "all", "", "", "public", "", now)
	seedTaskboardChannelScope(t, ctx, pool, fixture.workspaceID, fixture.secondaryChannelID, "build", "", "", "private", "mem_task_view_state_owner", now)

	mustExecTaskboard(t, ctx, pool, `
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, 'mem_task_view_state_owner', 'public', NULL, 0, 0, 0, $3, $3, NULL, NULL),
    ($1, 'mem_task_view_state_member', 'public', NULL, 0, 0, 0, $3, $3, NULL, NULL),
    ($2, 'mem_task_view_state_owner', 'public', NULL, 0, 0, 0, $3, $3, NULL, NULL),
    ($2, 'mem_task_view_state_member', 'public', NULL, 0, 0, 0, $3, $3, NULL, NULL)
`, fixture.primaryChannelID, fixture.secondaryChannelID, now)

	seedTaskboardChannelScopeFacts(t, ctx, pool, fixture.workspaceID, now)

	return fixture
}
