package taskboard

import (
	"context"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

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

type taskboardTaxonomyFixture struct {
	service           *Service
	workspaceID       string
	channelID         string
	ownerUserID       string
	ownerDisplayName  string
	adminUserID       string
	adminDisplayName  string
	memberUserID      string
	memberDisplayName string
}

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

	listResponse, err := fixture.service.ListTaskFieldOptions(ctx, ListTaskFieldOptionsRequest{
		Context: NewUserServiceContext(fixture.workspaceID, fixture.memberUserID, fixture.memberDisplayName),
	})
	require.NoError(t, err)
	require.False(t, listResponse.CanManage)
	require.Equal(t, []string{"p0", "p1", "p2", "p3"}, taskFieldKeys(listResponse.PriorityOptions))
	require.Equal(t, []string{"feature", "bug", "design", "product", "refactor", "docs", "test", "chore", "research", "spike", "release", "ops"}, taskFieldKeys(listResponse.KindOptions))
	require.NotContains(t, taskFieldKeys(listResponse.KindOptions), "data")
	require.Contains(t, findTaskFieldOption(t, listResponse.PriorityOptions, "p0").Aliases, "critical")
	require.Contains(t, findTaskFieldOption(t, listResponse.KindOptions, "feature").Aliases, "feat")

	createdFeatureTask, err := fixture.service.CreateTasks(ctx, CreateTasksRequest{
		Context: NewUserServiceContext(fixture.workspaceID, fixture.ownerUserID, fixture.ownerDisplayName),
		Board:   BoardRef{ChannelID: fixture.channelID},
		Titles:  []string{"Normalize aliases"},
	})
	require.NoError(t, err)
	require.Len(t, createdFeatureTask.Tasks, 1)

	normalizedTask, err := fixture.service.UpdateTask(ctx, UpdateTaskRequest{
		Context: NewUserServiceContext(fixture.workspaceID, fixture.ownerUserID, fixture.ownerDisplayName),
		Task:    TaskRef{TaskID: createdFeatureTask.Tasks[0].ID},
		Patch: TaskUpdatePatch{
			Kind:     stringPointer("feat"),
			Priority: stringPointer("critical"),
		},
	})
	require.NoError(t, err)
	require.Equal(t, "feature", normalizedTask.Task.KindField.Key)
	require.Equal(t, "Feature", normalizedTask.Task.Kind)
	require.Equal(t, "p0", normalizedTask.Task.PriorityField.Key)
	require.Equal(t, "Critical", normalizedTask.Task.Priority)

	createdLegacyTask, err := fixture.service.CreateTasks(ctx, CreateTasksRequest{
		Context: NewUserServiceContext(fixture.workspaceID, fixture.ownerUserID, fixture.ownerDisplayName),
		Board:   BoardRef{ChannelID: fixture.channelID},
		Titles:  []string{"Keep legacy values"},
	})
	require.NoError(t, err)
	require.Len(t, createdLegacyTask.Tasks, 1)

	legacyTask, err := fixture.service.UpdateTask(ctx, UpdateTaskRequest{
		Context: NewUserServiceContext(fixture.workspaceID, fixture.ownerUserID, fixture.ownerDisplayName),
		Task:    TaskRef{TaskID: createdLegacyTask.Tasks[0].ID},
		Patch: TaskUpdatePatch{
			Kind: stringPointer("legacy-custom"),
		},
	})
	require.NoError(t, err)
	require.True(t, legacyTask.Task.KindField.IsLegacy)
	require.Equal(t, "legacy-custom", legacyTask.Task.KindField.RawValue)
	require.Equal(t, "legacy-custom", legacyTask.Task.Kind)

	listResponse, err = fixture.service.ListTaskFieldOptions(ctx, ListTaskFieldOptionsRequest{
		Context: NewUserServiceContext(fixture.workspaceID, fixture.ownerUserID, fixture.ownerDisplayName),
	})
	require.NoError(t, err)
	require.Equal(t, int64(1), findTaskFieldOption(t, listResponse.PriorityOptions, "p0").UsageCount)
	require.Equal(t, int64(1), findTaskFieldOption(t, listResponse.KindOptions, "feature").UsageCount)
}

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

	_, err := fixture.service.UpsertTaskFieldOption(ctx, UpsertTaskFieldOptionRequest{
		Context: NewUserServiceContext(fixture.workspaceID, fixture.memberUserID, fixture.memberDisplayName),
		Option: TaskFieldOptionInput{
			FieldKind: "kind",
			Key:       "enhancement",
			Label:     "Enhancement",
		},
	})
	require.ErrorIs(t, err, ErrForbidden)

	created, err := fixture.service.UpsertTaskFieldOption(ctx, UpsertTaskFieldOptionRequest{
		Context: NewUserServiceContext(fixture.workspaceID, fixture.ownerUserID, fixture.ownerDisplayName),
		Option: TaskFieldOptionInput{
			FieldKind: "kind",
			Key:       "enhancement",
			Label:     "Enhancement",
			Aliases:   []string{"enh", "improvement"},
		},
	})
	require.NoError(t, err)
	require.Equal(t, "enhancement", created.Option.Key)
	require.Equal(t, "Enhancement", created.Option.Label)
	require.ElementsMatch(t, []string{"enh", "improvement"}, created.Option.Aliases)

	renamed, err := fixture.service.UpsertTaskFieldOption(ctx, UpsertTaskFieldOptionRequest{
		Context:  NewUserServiceContext(fixture.workspaceID, fixture.adminUserID, fixture.adminDisplayName),
		OptionID: created.Option.ID,
		Option: TaskFieldOptionInput{
			Label:   "Enhancement Work",
			Aliases: []string{"enh"},
		},
	})
	require.NoError(t, err)
	require.Equal(t, "Enhancement Work", renamed.Option.Label)

	second, err := fixture.service.UpsertTaskFieldOption(ctx, UpsertTaskFieldOptionRequest{
		Context: NewUserServiceContext(fixture.workspaceID, fixture.ownerUserID, fixture.ownerDisplayName),
		Option: TaskFieldOptionInput{
			FieldKind: "kind",
			Key:       "insight",
			Label:     "Insight",
		},
	})
	require.NoError(t, err)

	_, err = fixture.service.ReorderTaskFieldOption(ctx, ReorderTaskFieldOptionRequest{
		Context:  NewUserServiceContext(fixture.workspaceID, fixture.adminUserID, fixture.adminDisplayName),
		OptionID: second.Option.ID,
		Position: 0,
	})
	require.NoError(t, err)

	listResponse, err := fixture.service.ListTaskFieldOptions(ctx, ListTaskFieldOptionsRequest{
		Context: NewUserServiceContext(fixture.workspaceID, fixture.ownerUserID, fixture.ownerDisplayName),
	})
	require.NoError(t, err)
	require.Equal(t, "insight", listResponse.KindOptions[0].Key)

	deactivated, err := fixture.service.SetTaskFieldOptionActive(ctx, SetTaskFieldOptionActiveRequest{
		Context:  NewUserServiceContext(fixture.workspaceID, fixture.adminUserID, fixture.adminDisplayName),
		OptionID: created.Option.ID,
		Active:   false,
	})
	require.NoError(t, err)
	require.False(t, deactivated.Option.IsActive)

	reactivated, err := fixture.service.SetTaskFieldOptionActive(ctx, SetTaskFieldOptionActiveRequest{
		Context:  NewUserServiceContext(fixture.workspaceID, fixture.adminUserID, fixture.adminDisplayName),
		OptionID: created.Option.ID,
		Active:   true,
	})
	require.NoError(t, err)
	require.True(t, reactivated.Option.IsActive)
}

func seedTaskboardTaxonomyFixture(t *testing.T, ctx context.Context) taskboardTaxonomyFixture {
	t.Helper()

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

	fixture := taskboardTaxonomyFixture{
		service:           New(pool),
		workspaceID:       "wsp_task_taxonomy",
		channelID:         "chn_task_taxonomy",
		ownerUserID:       "usr_task_taxonomy_owner",
		ownerDisplayName:  "Task Taxonomy Owner",
		adminUserID:       "usr_task_taxonomy_admin",
		adminDisplayName:  "Task Taxonomy Admin",
		memberUserID:      "usr_task_taxonomy_member",
		memberDisplayName: "Task Taxonomy Member",
	}

	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-taxonomy-owner@example.com', 'task-taxonomy-owner', $2, '', FALSE, 'hash', $7, $7, $7),
    ($3, 'task-taxonomy-admin@example.com', 'task-taxonomy-admin', $4, '', FALSE, 'hash', $7, $7, $7),
    ($5, 'task-taxonomy-member@example.com', 'task-taxonomy-member', $6, '', FALSE, 'hash', $7, $7, $7)
`, fixture.ownerUserID, fixture.ownerDisplayName, fixture.adminUserID, fixture.adminDisplayName, 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-taxonomy', 'Task Taxonomy 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_taxonomy_owner', $1, 'human', $2, $3, 'owner', 'active', $7, $7),
    ('mem_task_taxonomy_admin', $1, 'human', $4, $5, 'admin', 'active', $7, $7),
    ('mem_task_taxonomy_member', $1, 'human', $6, $8, 'member', 'active', $7, $7)
`, fixture.workspaceID, fixture.ownerDisplayName, fixture.ownerUserID, fixture.adminDisplayName, fixture.adminUserID, fixture.memberDisplayName, now, fixture.memberUserID)

	seedTaskboardChannelScope(t, ctx, pool, fixture.workspaceID, fixture.channelID, "all", "", "", "public", "", 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_taxonomy_owner', 'public', NULL, 0, 0, 0, $2, $2, NULL, NULL),
    ($1, 'mem_task_taxonomy_admin', 'public', NULL, 0, 0, 0, $2, $2, NULL, NULL),
    ($1, 'mem_task_taxonomy_member', 'public', NULL, 0, 0, 0, $2, $2, NULL, NULL)
`, fixture.channelID, now)

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

	return fixture
}

func taskFieldKeys(options []TaskFieldOption) []string {
	keys := make([]string, 0, len(options))
	for _, option := range options {
		keys = append(keys, option.Key)
	}
	return keys
}

func findTaskFieldOption(t *testing.T, options []TaskFieldOption, key string) TaskFieldOption {
	t.Helper()

	for _, option := range options {
		if option.Key == key {
			return option
		}
	}
	t.Fatalf("missing task field option %q", key)
	return TaskFieldOption{}
}
