package agent

import (
	"context"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/api/dto/converter"
	"github.com/tidbcloud/AgentTeam/internal/store/attachment"
	"github.com/tidbcloud/AgentTeam/internal/store/taskboard"
)

type taskBoardBackend interface {
	ListTasks(ctx context.Context, request taskboard.ListTasksRequest) (taskboard.ListTasksResponse, error)
	BrowseChannelTasks(ctx context.Context, request taskboard.BrowseChannelTasksRequest) (taskboard.TaskRowBrowsePage, error)
	ListTaskOptions(ctx context.Context, request taskboard.ListTaskOptionsRequest) (taskboard.ListTaskOptionsResponse, error)
	CreateTasks(ctx context.Context, request taskboard.CreateTasksRequest) (taskboard.CreateTasksResponse, error)
	CreateSubtasks(ctx context.Context, request taskboard.CreateSubtasksRequest) (taskboard.CreateSubtasksResponse, error)
	ClaimTasks(ctx context.Context, request taskboard.ClaimTasksRequest) (taskboard.ClaimTasksResponse, error)
	UnclaimTask(ctx context.Context, request taskboard.UnclaimTaskRequest) (taskboard.UnclaimTaskResponse, error)
	BlockTask(ctx context.Context, request taskboard.BlockTaskRequest) (taskboard.BlockTaskResponse, error)
	UnblockTask(ctx context.Context, request taskboard.UnblockTaskRequest) (taskboard.UnblockTaskResponse, error)
	GetTaskDetail(ctx context.Context, request taskboard.GetTaskDetailRequest) (taskboard.GetTaskDetailResponse, error)
	UpdateTask(ctx context.Context, request taskboard.UpdateTaskRequest) (taskboard.UpdateTaskResponse, error)
	UpdateTaskStatus(ctx context.Context, request taskboard.UpdateTaskStatusRequest) (taskboard.UpdateTaskStatusResponse, error)
	AddTaskCheckpoint(ctx context.Context, request taskboard.AddTaskCheckpointRequest) (taskboard.AddTaskCheckpointResponse, error)
	DeleteTask(ctx context.Context, request taskboard.DeleteTaskRequest) (taskboard.DeleteTaskResponse, error)
}

type taskBoardServiceAdapter struct {
	base taskBoardBackend
}

// NewTaskBoardServiceAdapter adapts the in-process taskboard store to the
// agent-owned task API contract.
func NewTaskBoardServiceAdapter(base *taskboard.Store) TaskBoardService {
	return newTaskBoardServiceAdapter(base)
}

func newTaskBoardServiceAdapter(base taskBoardBackend) TaskBoardService {
	if base == nil {
		return nil
	}
	return taskBoardServiceAdapter{base: base}
}

func (adapter taskBoardServiceAdapter) ListTasks(ctx context.Context, request ListTasksRequest) (ListTasksResponse, error) {
	if request.Limit > 0 || request.Cursor != "" || request.SearchQuery != "" || request.SortMode != "" || len(request.QuickFilters) > 0 || request.Status != "" {
		cursor, err := converter.FromTaskBrowseCursorToken(request.Cursor, request.SortMode)
		if err != nil {
			return ListTasksResponse{}, err
		}
		response, err := adapter.base.BrowseChannelTasks(ctx, taskboard.BrowseChannelTasksRequest{
			Context: toTaskboardServiceContext(request.Context),
			Board:   toTaskboardBoardRef(request.Board),
			Query: taskboard.TaskBrowseQuery{
				Limit:           request.Limit,
				SearchQuery:     request.SearchQuery,
				SortMode:        request.SortMode,
				StatusFilter:    request.Status,
				QuickFilters:    request.QuickFilters,
				IncludeSubtasks: request.IncludeSubtasks,
				Cursor:          cursor,
			},
		})
		if err != nil {
			return ListTasksResponse{}, err
		}
		tasks := fromTaskboardTasks(response.Rows)
		return ListTasksResponse{
			ChannelID:  firstNonEmptyTaskboardRef(request.Board.ChannelID, request.Board.Channel),
			Tasks:      tasks,
			TaskViews:  taskViewsFromTasks(tasks),
			Limit:      response.QueryEcho.Limit,
			TotalCount: response.TotalCount,
			HasMore:    response.HasMore,
			NextCursor: converter.ToTaskBrowseCursorToken(response.NextCursor),
		}, nil
	}
	response, err := adapter.base.ListTasks(ctx, taskboard.ListTasksRequest{
		Context:         toTaskboardServiceContext(request.Context),
		Board:           toTaskboardBoardRef(request.Board),
		Status:          request.Status,
		IncludeSubtasks: request.IncludeSubtasks,
	})
	if err != nil {
		return ListTasksResponse{}, err
	}
	return fromTaskboardListTasksResponse(response), nil
}

func firstNonEmptyTaskboardRef(values ...string) string {
	for _, value := range values {
		if trimmed := strings.TrimSpace(value); trimmed != "" {
			return trimmed
		}
	}
	return ""
}

func (adapter taskBoardServiceAdapter) ListTaskOptions(ctx context.Context, request ListTaskOptionsRequest) (ListTaskOptionsResponse, error) {
	response, err := adapter.base.ListTaskOptions(ctx, taskboard.ListTaskOptionsRequest{
		Context: toTaskboardServiceContext(request.Context),
		Board:   toTaskboardBoardRef(request.Board),
		Role:    request.Role,
		Query:   request.Query,
		Limit:   request.Limit,
		Cursor:  request.Cursor,
	})
	if err != nil {
		return ListTaskOptionsResponse{}, err
	}
	return fromTaskboardListTaskOptionsResponse(response), nil
}

func (adapter taskBoardServiceAdapter) CreateTasks(ctx context.Context, request CreateTasksRequest) (CreateTasksResponse, error) {
	response, err := adapter.base.CreateTasks(ctx, taskboard.CreateTasksRequest{
		Context:  toTaskboardServiceContext(request.Context),
		Board:    toTaskboardBoardRef(request.Board),
		Titles:   request.Titles,
		Task:     toTaskboardTaskCreateInput(request.Task),
		Metadata: toTaskboardCreateTaskMetadata(request.Metadata),
	})
	if err != nil {
		return CreateTasksResponse{}, err
	}
	return fromTaskboardCreateTasksResponse(response), nil
}

func (adapter taskBoardServiceAdapter) CreateSubtasks(ctx context.Context, request CreateSubtasksRequest) (CreateSubtasksResponse, error) {
	response, err := adapter.base.CreateSubtasks(ctx, taskboard.CreateSubtasksRequest{
		Context:         toTaskboardServiceContext(request.Context),
		Board:           toTaskboardBoardRef(request.Board),
		Task:            toTaskboardTaskRef(request.Task),
		ParentTaskScope: request.ParentTaskScope,
		SourceChannelID: request.SourceChannelID,
		Metadata:        toTaskboardCreateTaskMetadata(request.Metadata),
		Titles:          request.Titles,
		Input:           toTaskboardTaskCreateInput(request.Input),
	})
	if err != nil {
		return CreateSubtasksResponse{}, err
	}
	return fromTaskboardCreateSubtasksResponse(response), nil
}

func (adapter taskBoardServiceAdapter) ClaimTasks(ctx context.Context, request ClaimTasksRequest) (ClaimTasksResponse, error) {
	response, err := adapter.base.ClaimTasks(ctx, taskboard.ClaimTasksRequest{
		Context: toTaskboardServiceContext(request.Context),
		Board:   toTaskboardBoardRef(request.Board),
		Tasks:   toTaskboardTaskRefs(request.Tasks),
	})
	if err != nil {
		return ClaimTasksResponse{}, err
	}
	return fromTaskboardClaimTasksResponse(response), nil
}

func (adapter taskBoardServiceAdapter) UnclaimTask(ctx context.Context, request UnclaimTaskRequest) (UnclaimTaskResponse, error) {
	response, err := adapter.base.UnclaimTask(ctx, taskboard.UnclaimTaskRequest{
		Context: toTaskboardServiceContext(request.Context),
		Board:   toTaskboardBoardRef(request.Board),
		Task:    toTaskboardTaskRef(request.Task),
	})
	if err != nil {
		return UnclaimTaskResponse{}, err
	}
	return UnclaimTaskResponse{Task: fromTaskboardTask(response.Task)}, nil
}

func (adapter taskBoardServiceAdapter) BlockTask(ctx context.Context, request BlockTaskRequest) (BlockTaskResponse, error) {
	response, err := adapter.base.BlockTask(ctx, taskboard.BlockTaskRequest{
		Context: toTaskboardServiceContext(request.Context),
		Board:   toTaskboardBoardRef(request.Board),
		Task:    toTaskboardTaskRef(request.Task),
		Block:   taskboard.TaskBlockInput{Reason: request.Block.Reason},
	})
	if err != nil {
		return BlockTaskResponse{}, err
	}
	return BlockTaskResponse{Task: fromTaskboardTask(response.Task), Changed: response.Changed}, nil
}

func (adapter taskBoardServiceAdapter) UnblockTask(ctx context.Context, request UnblockTaskRequest) (UnblockTaskResponse, error) {
	response, err := adapter.base.UnblockTask(ctx, taskboard.UnblockTaskRequest{
		Context: toTaskboardServiceContext(request.Context),
		Board:   toTaskboardBoardRef(request.Board),
		Task:    toTaskboardTaskRef(request.Task),
	})
	if err != nil {
		return UnblockTaskResponse{}, err
	}
	return UnblockTaskResponse{Task: fromTaskboardTask(response.Task), Changed: response.Changed}, nil
}

func (adapter taskBoardServiceAdapter) GetTaskDetail(ctx context.Context, request GetTaskDetailRequest) (GetTaskDetailResponse, error) {
	response, err := adapter.base.GetTaskDetail(ctx, taskboard.GetTaskDetailRequest{
		Context:                 toTaskboardServiceContext(request.Context),
		Board:                   toTaskboardBoardRef(request.Board),
		Task:                    toTaskboardTaskRef(request.Task),
		SkipDiscussionReactions: true,
	})
	if err != nil {
		return GetTaskDetailResponse{}, err
	}
	return GetTaskDetailResponse{Detail: fromTaskboardTaskDetail(response.Detail)}, nil
}

func (adapter taskBoardServiceAdapter) UpdateTask(ctx context.Context, request UpdateTaskRequest) (UpdateTaskResponse, error) {
	response, err := adapter.base.UpdateTask(ctx, taskboard.UpdateTaskRequest{
		Context: toTaskboardServiceContext(request.Context),
		Board:   toTaskboardBoardRef(request.Board),
		Task:    toTaskboardTaskRef(request.Task),
		Patch:   toTaskboardTaskUpdatePatch(request.Patch),
	})
	if err != nil {
		return UpdateTaskResponse{}, err
	}
	return UpdateTaskResponse{Task: fromTaskboardTask(response.Task)}, nil
}

func (adapter taskBoardServiceAdapter) UpdateTaskStatus(ctx context.Context, request UpdateTaskStatusRequest) (UpdateTaskStatusResponse, error) {
	response, err := adapter.base.UpdateTaskStatus(ctx, taskboard.UpdateTaskStatusRequest{
		Context: toTaskboardServiceContext(request.Context),
		Board:   toTaskboardBoardRef(request.Board),
		Task:    toTaskboardTaskRef(request.Task),
		Status:  request.Status,
	})
	if err != nil {
		return UpdateTaskStatusResponse{}, err
	}
	return UpdateTaskStatusResponse{Task: fromTaskboardTask(response.Task), Changed: response.Changed}, nil
}

func (adapter taskBoardServiceAdapter) AddTaskCheckpoint(ctx context.Context, request AddTaskCheckpointRequest) (AddTaskCheckpointResponse, error) {
	response, err := adapter.base.AddTaskCheckpoint(ctx, taskboard.AddTaskCheckpointRequest{
		Context:    toTaskboardServiceContext(request.Context),
		Board:      toTaskboardBoardRef(request.Board),
		Task:       toTaskboardTaskRef(request.Task),
		Checkpoint: taskboard.TaskCheckpointInput{Text: request.Checkpoint.Text},
	})
	if err != nil {
		return AddTaskCheckpointResponse{}, err
	}
	return AddTaskCheckpointResponse{Task: fromTaskboardTask(response.Task)}, nil
}

func (adapter taskBoardServiceAdapter) DeleteTask(ctx context.Context, request DeleteTaskRequest) (DeleteTaskResponse, error) {
	response, err := adapter.base.DeleteTask(ctx, taskboard.DeleteTaskRequest{
		Context:     toTaskboardServiceContext(request.Context),
		Board:       toTaskboardBoardRef(request.Board),
		Task:        toTaskboardTaskRef(request.Task),
		SubtaskMode: taskboard.DeleteTaskSubtaskMode(request.SubtaskMode),
	})
	if err != nil {
		return DeleteTaskResponse{}, err
	}
	return DeleteTaskResponse{
		DeletedTaskNumbers:  response.DeletedTaskNumbers,
		PromotedTaskNumbers: response.PromotedTaskNumbers,
	}, nil
}

func toTaskboardCreateTaskMetadata(metadata *CreateTaskMetadata) *taskboard.CreateTaskMetadata {
	if metadata == nil {
		return nil
	}
	return &taskboard.CreateTaskMetadata{
		SourceThreadChannelID:       metadata.SourceThreadChannelID,
		SourceThreadParentChannelID: metadata.SourceThreadParentChannelID,
		SourceThreadParentMessageID: metadata.SourceThreadParentMessageID,
		SourceThreadRef:             metadata.SourceThreadRef,
	}
}

func toTaskboardServiceContext(ctx ServiceContext) taskboard.ServiceContext {
	return taskboard.ServiceContext{
		RequestID:      ctx.RequestID,
		WorkspaceID:    ctx.WorkspaceID,
		Subject:        taskboard.SubjectContext{PrincipalID: ctx.Subject.PrincipalID, PrincipalType: taskboard.PrincipalType(ctx.Subject.PrincipalType), MemberID: ctx.Subject.MemberID, DisplayName: ctx.Subject.DisplayName},
		InstallationID: ctx.InstallationID,
		Scopes:         ctx.Scopes,
	}
}

func toTaskboardBoardRef(ref BoardRef) taskboard.BoardRef {
	return taskboard.BoardRef{ChannelID: ref.ChannelID, Channel: ref.Channel}
}

func toTaskboardTaskRef(ref TaskRef) taskboard.TaskRef {
	return taskboard.TaskRef{TaskID: ref.TaskID, TaskNumber: ref.TaskNumber}
}

func toTaskboardTaskRefs(refs []TaskRef) []taskboard.TaskRef {
	if len(refs) == 0 {
		return nil
	}
	out := make([]taskboard.TaskRef, 0, len(refs))
	for _, ref := range refs {
		out = append(out, toTaskboardTaskRef(ref))
	}
	return out
}

func toTaskboardTaskCreateInput(input *TaskCreateInput) *taskboard.TaskCreateInput {
	if input == nil {
		return nil
	}
	return &taskboard.TaskCreateInput{
		Title:            input.Title,
		Description:      input.Description,
		Kind:             input.Kind,
		Priority:         input.Priority,
		KindOptionID:     input.KindOptionID,
		PriorityOptionID: input.PriorityOptionID,
		AssigneeRef:      input.AssigneeRef,
	}
}

func toTaskboardTaskUpdatePatch(patch TaskUpdatePatch) taskboard.TaskUpdatePatch {
	return taskboard.TaskUpdatePatch{
		Title:              patch.Title,
		Description:        patch.Description,
		Kind:               patch.Kind,
		Priority:           patch.Priority,
		KindOptionID:       patch.KindOptionID,
		PriorityOptionID:   patch.PriorityOptionID,
		AssigneeRef:        patch.AssigneeRef,
		AcceptanceCriteria: toTaskboardChecklistItems(patch.AcceptanceCriteria),
		ReviewChecklist:    toTaskboardChecklistItems(patch.ReviewChecklist),
		PinnedMessageRefs:  patch.PinnedMessageRefs,
		RelevantURLs:       patch.RelevantURLs,
		WorkspaceRefs:      patch.WorkspaceRefs,
		BlockedReason:      patch.BlockedReason,
		ParentTaskRef:      patch.ParentTaskRef,
		AttachmentIDs:      patch.AttachmentIDs,
	}
}

func toTaskboardChecklistItems(items []TaskChecklistItem) []taskboard.TaskChecklistItem {
	if len(items) == 0 {
		return nil
	}
	out := make([]taskboard.TaskChecklistItem, 0, len(items))
	for _, item := range items {
		out = append(out, taskboard.TaskChecklistItem{ID: item.ID, Text: item.Text, Checked: item.Checked})
	}
	return out
}

func fromTaskboardListTasksResponse(response taskboard.ListTasksResponse) ListTasksResponse {
	return ListTasksResponse{
		ChannelID:  response.ChannelID,
		Tasks:      fromTaskboardTasks(response.Tasks),
		TaskViews:  fromTaskboardTaskViews(response.TaskViews),
		Limit:      response.Limit,
		TotalCount: response.TotalCount,
		HasMore:    response.HasMore,
		NextCursor: converter.ToTaskBrowseCursorToken(response.NextCursor),
	}
}

func fromTaskboardCreateTasksResponse(response taskboard.CreateTasksResponse) CreateTasksResponse {
	return CreateTasksResponse{ChannelID: response.ChannelID, Tasks: fromTaskboardTasks(response.Tasks), CreatedTasks: fromTaskboardCreatedTasks(response.CreatedTasks)}
}

func fromTaskboardCreateSubtasksResponse(response taskboard.CreateSubtasksResponse) CreateSubtasksResponse {
	return CreateSubtasksResponse{ChannelID: response.ChannelID, Tasks: fromTaskboardTasks(response.Tasks), CreatedTasks: fromTaskboardCreatedTasks(response.CreatedTasks)}
}

func fromTaskboardClaimTasksResponse(response taskboard.ClaimTasksResponse) ClaimTasksResponse {
	return ClaimTasksResponse{
		ChannelID:          response.ChannelID,
		Task:               fromTaskboardTask(response.Task),
		Tasks:              fromTaskboardTasks(response.Tasks),
		Results:            fromTaskboardClaimResults(response.Results),
		ChangedTaskNumbers: response.ChangedTaskNumbers,
	}
}

func fromTaskboardTask(task taskboard.Task) Task {
	return Task{
		ID:                 task.ID,
		TaskScopeID:        task.TaskScopeID,
		ChannelID:          task.ChannelID,
		TaskNumber:         task.TaskNumber,
		Title:              task.Title,
		Status:             task.Status,
		Description:        task.Description,
		Kind:               task.Kind,
		Priority:           task.Priority,
		BlockedReason:      task.BlockedReason,
		LastActivityAt:     task.LastActivityAt,
		ArtifactCount:      task.ArtifactCount,
		ParentTaskRef:      task.ParentTaskRef,
		DiscussionCount:    task.DiscussionCount,
		AssigneeProfile:    taskPrincipalProfileFromTaskboard(task.AssigneeProfile),
		KindField:          fromTaskboardFieldValue(task.KindField),
		PriorityField:      fromTaskboardFieldValue(task.PriorityField),
		CheckpointProgress: TaskProgressSummary{Total: task.CheckpointProgress.Total, Done: task.CheckpointProgress.Done},
		SubtaskProgress:    TaskProgressSummary{Total: task.SubtaskProgress.Total, Done: task.SubtaskProgress.Done},
		CreatedAt:          task.CreatedAt,
		CreatedByProfile:   taskPrincipalProfileFromTaskboard(task.CreatedByProfile),
		CreatedByType:      task.CreatedByType,
		CreatedByID:        task.CreatedByID,
		CreatedByName:      task.CreatedByName,
		Source:             contributionSourceFromTaskboard(task.Source),
		SourceThread:       fromTaskboardSourceThread(task.SourceThread),
		AssignedAt:         task.AssignedAt,
		CompletedAt:        task.CompletedAt,
	}
}

func fromTaskboardTasks(tasks []taskboard.Task) []Task {
	if len(tasks) == 0 {
		return nil
	}
	out := make([]Task, 0, len(tasks))
	for _, task := range tasks {
		out = append(out, fromTaskboardTask(task))
	}
	return out
}

func fromTaskboardListTaskOptionsResponse(response taskboard.ListTaskOptionsResponse) ListTaskOptionsResponse {
	return ListTaskOptionsResponse{
		PriorityOptions: fromTaskboardFieldOptions(response.PriorityOptions),
		KindOptions:     fromTaskboardFieldOptions(response.KindOptions),
		AssigneeOptions: fromTaskboardMemberOptions(response.AssigneeOptions),
		Limit:           response.Limit,
		HasMore:         response.HasMore,
		NextCursor:      response.NextCursor,
	}
}

func fromTaskboardFieldOptions(options []taskboard.TaskFieldOption) []TaskFieldOption {
	if len(options) == 0 {
		return nil
	}
	out := make([]TaskFieldOption, 0, len(options))
	for _, option := range options {
		out = append(out, TaskFieldOption{
			ID:         option.ID,
			FieldKind:  option.FieldKind,
			Key:        option.Key,
			Label:      option.Label,
			Aliases:    option.Aliases,
			Position:   option.Position,
			IsActive:   option.IsActive,
			UsageCount: option.UsageCount,
		})
	}
	return out
}

func fromTaskboardMemberOptions(options []taskboard.TaskMemberOption) []TaskMemberOption {
	if len(options) == 0 {
		return nil
	}
	out := make([]TaskMemberOption, 0, len(options))
	for _, option := range options {
		out = append(out, TaskMemberOption{
			MemberID: option.MemberID,
			Type:     option.Type,
			UserID:   option.UserID,
			AgentID:  option.AgentID,
			Name:     option.Name,
			Label:    option.Label,
		})
	}
	return out
}

func fromTaskboardTaskViews(views []taskboard.TaskView) []TaskView {
	if len(views) == 0 {
		return nil
	}
	out := make([]TaskView, 0, len(views))
	for _, view := range views {
		out = append(out, TaskView{
			TaskScopeID:      view.TaskScopeID,
			TaskNumber:       view.TaskNumber,
			Title:            view.Title,
			Status:           view.Status,
			AssigneeProfile:  taskPrincipalProfileFromTaskboard(view.AssigneeProfile),
			CreatedByProfile: taskPrincipalProfileFromTaskboard(view.CreatedByProfile),
			CreatedByName:    view.CreatedByName,
		})
	}
	return out
}

func taskViewsFromTasks(tasks []Task) []TaskView {
	if len(tasks) == 0 {
		return nil
	}
	out := make([]TaskView, 0, len(tasks))
	for _, task := range tasks {
		out = append(out, TaskView{
			TaskScopeID:      task.TaskScopeID,
			TaskNumber:       task.TaskNumber,
			Title:            task.Title,
			Status:           task.Status,
			AssigneeProfile:  task.AssigneeProfile,
			CreatedByProfile: task.CreatedByProfile,
			CreatedByName:    task.CreatedByName,
		})
	}
	return out
}

func fromTaskboardCreatedTasks(tasks []taskboard.CreatedTask) []CreatedTask {
	if len(tasks) == 0 {
		return nil
	}
	out := make([]CreatedTask, 0, len(tasks))
	for _, task := range tasks {
		out = append(out, CreatedTask{TaskScopeID: task.TaskScopeID, TaskNumber: task.TaskNumber, Title: task.Title, Reused: task.Reused})
	}
	return out
}

func fromTaskboardClaimResults(results []taskboard.ClaimResult) []ClaimResult {
	if len(results) == 0 {
		return nil
	}
	out := make([]ClaimResult, 0, len(results))
	for _, result := range results {
		out = append(out, ClaimResult{TaskNumber: result.TaskNumber, Success: result.Success, Reason: result.Reason})
	}
	return out
}

func taskPrincipalProfileFromTaskboard(profile *taskboard.TaskPrincipalProfile) *AuthorProfile {
	if profile == nil {
		return nil
	}
	return authorProfileFromTaskboard(*profile)
}

func fromTaskboardFieldValue(value *taskboard.TaskFieldValue) *TaskFieldValue {
	if value == nil {
		return nil
	}
	return &TaskFieldValue{OptionID: value.OptionID, Key: value.Key, Label: value.Label, RawValue: value.RawValue, IsLegacy: value.IsLegacy, IsNone: value.IsNone}
}

func fromTaskboardSourceThread(thread *taskboard.TaskSourceThread) *TaskSourceThread {
	if thread == nil {
		return nil
	}
	return &TaskSourceThread{ThreadChannelID: thread.ThreadChannelID, ParentChannelID: thread.ParentChannelID, ParentMessageID: thread.ParentMessageID, Ref: thread.Ref}
}

func fromTaskboardChecklistItems(items []taskboard.TaskChecklistItem) []TaskChecklistItem {
	if len(items) == 0 {
		return nil
	}
	out := make([]TaskChecklistItem, 0, len(items))
	for _, item := range items {
		out = append(out, TaskChecklistItem{ID: item.ID, Text: item.Text, Checked: item.Checked})
	}
	return out
}

func fromTaskboardArtifact(artifact taskboard.TaskArtifact) TaskArtifact {
	createdByProfile := taskPrincipalProfileFromTaskboard(artifact.CreatedByProfile)
	return TaskArtifact{ID: artifact.ID, Kind: artifact.Kind, Title: artifact.Title, Payload: artifact.Payload, CreatedByProfile: createdByProfile, CreatedByRef: taskFactCompatRefFromAuthorProfile(createdByProfile, artifact.CreatedByRef), CreatedAt: artifact.CreatedAt}
}

func fromTaskboardArtifacts(artifacts []taskboard.TaskArtifact) []TaskArtifact {
	if len(artifacts) == 0 {
		return nil
	}
	out := make([]TaskArtifact, 0, len(artifacts))
	for _, artifact := range artifacts {
		out = append(out, fromTaskboardArtifact(artifact))
	}
	return out
}

func fromTaskboardActivityEvent(event taskboard.TaskActivityEvent) TaskActivityEvent {
	authorProfile := authorProfileFromTaskboard(event.AuthorProfile)
	source := contributionSourceFromTaskboard(event.Source)
	return TaskActivityEvent{ID: event.ID, Type: event.Type, AuthorProfile: authorProfile, Source: source, ActorRef: taskActivityCompatActorRefFromAuthorProfile(authorProfile, event.ActorRef), ActorDisplayName: taskActivityCompatActorDisplayNameFromAuthorProfile(authorProfile, event.ActorDisplayName), Payload: event.Payload, SourceKind: taskActivityCompatSourceKindFromContributionSource(source, event.SourceKind), SourceMessageID: taskActivityCompatSourceMessageIDFromContributionSource(source, event.SourceMessageID), SourceActivityID: taskActivityCompatSourceActivityIDFromContributionSource(source, event.SourceActivityID), CreatedAt: event.CreatedAt}
}

func taskActivityCompatActorRefFromAuthorProfile(profile *AuthorProfile, fallback string) string {
	if profile == nil {
		return fallback
	}
	return firstNonEmptyTaskString(profile.MemberID, profile.PrincipalID, profile.TargetID, fallback)
}

func taskActivityCompatActorDisplayNameFromAuthorProfile(profile *AuthorProfile, fallback string) string {
	if profile == nil {
		return fallback
	}
	return firstNonEmptyTaskString(profile.DisplayName, fallback)
}

func fromTaskboardTimelineEntry(entry taskboard.TaskTimelineEntry) TaskTimelineEntry {
	converted := TaskTimelineEntry{ID: entry.ID, Kind: entry.Kind, CreatedAt: entry.CreatedAt}
	if entry.Activity != nil {
		activity := fromTaskboardActivityEvent(*entry.Activity)
		converted.Activity = &activity
	}
	if entry.Discussion != nil {
		discussion := fromTaskboardDiscussionEntry(*entry.Discussion)
		converted.Discussion = &discussion
	}
	return converted
}

func fromTaskboardTimelineEntries(entries []taskboard.TaskTimelineEntry) []TaskTimelineEntry {
	if len(entries) == 0 {
		return nil
	}
	out := make([]TaskTimelineEntry, 0, len(entries))
	for _, entry := range entries {
		out = append(out, fromTaskboardTimelineEntry(entry))
	}
	return out
}

func fromTaskboardDiscussionEntry(entry taskboard.TaskDiscussionEntry) TaskDiscussionEntry {
	authorProfile := authorProfileFromTaskboard(entry.AuthorProfile)
	return TaskDiscussionEntry{
		ID:            entry.ID,
		TaskID:        entry.TaskID,
		Body:          entry.Body,
		AuthorProfile: authorProfile,
		Source:        contributionSourceFromTaskboard(entry.Source),
		CreatedBy:     taskDiscussionCompatActorFromAuthorProfile(authorProfile, entry.CreatedBy),
		Attachments:   fromTaskboardDiscussionAttachments(entry.Attachments),
		CreatedAt:     entry.CreatedAt,
	}
}

func fromTaskboardDiscussionAttachments(attachments []attachment.AppAttachment) []TaskDiscussionAttachment {
	if len(attachments) == 0 {
		return nil
	}
	out := make([]TaskDiscussionAttachment, 0, len(attachments))
	for _, attachment := range attachments {
		out = append(out, TaskDiscussionAttachment{
			ID:           attachment.ID,
			Filename:     attachment.Filename,
			ContentType:  attachment.ContentType,
			SizeBytes:    attachment.SizeBytes,
			ThumbnailURL: attachment.ThumbnailURL,
		})
	}
	return out
}

func fromTaskboardTaskDetail(detail taskboard.TaskDetail) TaskDetail {
	var parentTask *Task
	if detail.ParentTask != nil {
		convertedParentTask := fromTaskboardTask(*detail.ParentTask)
		parentTask = &convertedParentTask
	}
	return TaskDetail{
		Task:                     fromTaskboardTask(detail.Task),
		ParentTask:               parentTask,
		Checkpoints:              fromTaskboardChecklistItems(detail.Checkpoints),
		Timeline:                 fromTaskboardTimelineEntries(detail.Timeline),
		TimelineHasMore:          detail.TimelineHasMore,
		TimelineNextCursor:       converter.ToTaskTimelineCursorToken(detail.TimelineNextCursor),
		AcceptanceCriteria:       fromTaskboardChecklistItems(detail.AcceptanceCriteria),
		ReviewChecklist:          fromTaskboardChecklistItems(detail.ReviewChecklist),
		Subtasks:                 fromTaskboardTasks(detail.Subtasks),
		PinnedMessageRefs:        detail.PinnedMessageRefs,
		PinnedMessageRefsHasMore: detail.PinnedMessageRefsHasMore,
		RelevantURLs:             detail.RelevantURLs,
		RelevantURLsHasMore:      detail.RelevantURLsHasMore,
		WorkspaceRefs:            detail.WorkspaceRefs,
		WorkspaceRefsHasMore:     detail.WorkspaceRefsHasMore,
		Artifacts:                fromTaskboardArtifacts(detail.Artifacts),
	}
}
