package task

import (
	"strings"

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

const (
	taskActivityCompatSourceKindActivity = "activity"
	taskActivityCompatSourceKindMessage  = "message"
)

func taskAnnouncementEntryFromAppMessage(taskID string, appMessage message.AppMessage) dto.TaskDiscussionEntry {
	var presentation *domain.MessagePresentation
	if decoded, err := domain.MessagePresentationFromMetadata(appMessage.Metadata); err == nil {
		presentation = decoded
	}
	return converter.ToTaskDiscussionEntry(taskboard.TaskDiscussionEntry{
		ID:                  appMessage.ID,
		TaskID:              strings.TrimSpace(taskID),
		Body:                appMessage.Content,
		AuthorProfile:       appMessage.AuthorProfile,
		Source:              appMessage.Source,
		ResolvedMentions:    append([]message.ResolvedMention(nil), appMessage.ResolvedMentions...),
		Reactions:           append([]message.MessageReactionAggregate(nil), appMessage.Reactions...),
		Attachments:         appMessage.Attachments,
		CreatedAt:           appMessage.CreatedAt,
		UpdatedAt:           appMessage.EditedAt,
		EditedAt:            appMessage.EditedAt,
		EditedByMemberID:    appMessage.EditedByMemberID,
		EditRevision:        appMessage.EditRevision,
		MessagePresentation: presentation,
	})
}

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

func taskBrowseQueryForPort(query taskboard.TaskBrowseQuery) TaskBrowseQuery {
	return fromTaskboardTaskBrowseQuery(query)
}

func taskBoardViewStateForPort(state taskboard.TaskBoardViewState) TaskBoardViewState {
	return fromTaskboardViewState(state)
}

func taskChecklistItemsForPort(items []taskboard.TaskChecklistItem) []TaskChecklistItem {
	if items == nil {
		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 taskboardTaskForDTO(task Task) taskboard.Task {
	createdByProfile := taskboardAuthorProfileFromDTO(task.CreatedByProfile)
	createdByType, createdByID, createdByName := taskBodyLegacyCreatedByFromAuthorProfile(createdByProfile, task.CreatedByType, task.CreatedByID, task.CreatedByName)
	return taskboard.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:    taskboardTaskPrincipalProfileForDTO(task.AssigneeProfile),
		KindField:          taskboardTaskFieldValueForDTO(task.KindField),
		PriorityField:      taskboardTaskFieldValueForDTO(task.PriorityField),
		CheckpointProgress: taskboard.TaskProgressSummary{Total: task.CheckpointProgress.Total, Done: task.CheckpointProgress.Done},
		SubtaskProgress:    taskboard.TaskProgressSummary{Total: task.SubtaskProgress.Total, Done: task.SubtaskProgress.Done},
		CreatedAt:          task.CreatedAt,
		CreatedByProfile:   taskboardTaskPrincipalProfileFromProfile(createdByProfile),
		CreatedByType:      createdByType,
		CreatedByID:        createdByID,
		CreatedByName:      createdByName,
		Source:             taskboardContributionSourceFromDTO(task.Source),
		SourceThread:       taskboardTaskSourceThreadForDTO(task.SourceThread),
		ReadState:          taskboardTaskReadStateForDTO(task.ReadState),
		AssignedAt:         task.AssignedAt,
		CompletedAt:        task.CompletedAt,
	}
}

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

func taskboardTaskPrincipalProfileForDTO(profile *AuthorProfile) *taskboard.TaskPrincipalProfile {
	converted := taskboardAuthorProfileFromDTO(profile)
	return taskboardTaskPrincipalProfileFromProfile(converted)
}

func taskboardTaskPrincipalProfileFromProfile(converted message.AuthorProfile) *taskboard.TaskPrincipalProfile {
	if !converted.Valid() {
		return nil
	}
	return &converted
}

func taskBodyLegacyCreatedByFromAuthorProfile(profile message.AuthorProfile, fallbackType string, fallbackID string, fallbackName string) (string, string, string) {
	profile = profile.Normalize()
	if !profile.Valid() {
		return fallbackType, fallbackID, fallbackName
	}
	legacyType := profile.ActorType
	switch legacyType {
	case message.AuthorActorTypeHuman:
		legacyType = "user"
	case message.AuthorActorTypeService:
		legacyType = "app"
	}
	return legacyType, firstNonEmptyTaskString(profile.PrincipalID, profile.TargetID, profile.MemberID, fallbackID), firstNonEmptyTaskString(profile.DisplayName, fallbackName)
}

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

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

func taskboardTaskReadStateForDTO(readState *TaskReadState) *taskboard.TaskReadState {
	if readState == nil {
		return nil
	}
	return &taskboard.TaskReadState{
		UnreadCount:            readState.UnreadCount,
		ManualUnread:           readState.ManualUnread,
		LatestEventID:          readState.LatestEventID,
		LatestEventCreatedAt:   readState.LatestEventCreatedAt,
		LastReadEventID:        readState.LastReadEventID,
		LastReadEventCreatedAt: readState.LastReadEventCreatedAt,
		LastMessageReadSeq:     readState.LastMessageReadSeq,
		LastActivityReadSeq:    readState.LastActivityReadSeq,
		ManualUnreadGeneration: readState.ManualUnreadGeneration,
	}
}

func taskboardTaskFieldOptionForDTO(option TaskFieldOption) taskboard.TaskFieldOption {
	return taskboard.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}
}

func taskboardTaskFieldOptionsForDTO(options []TaskFieldOption) []taskboard.TaskFieldOption {
	if len(options) == 0 {
		return nil
	}
	out := make([]taskboard.TaskFieldOption, 0, len(options))
	for _, option := range options {
		out = append(out, taskboardTaskFieldOptionForDTO(option))
	}
	return out
}

func taskboardListTaskFieldOptionsResponseForDTO(response ListTaskFieldOptionsResponse) taskboard.ListTaskFieldOptionsResponse {
	return taskboard.ListTaskFieldOptionsResponse{CanManage: response.CanManage, PriorityOptions: taskboardTaskFieldOptionsForDTO(response.PriorityOptions), KindOptions: taskboardTaskFieldOptionsForDTO(response.KindOptions)}
}

func taskboardSetTaskFieldOptionActiveResponseForDTO(response SetTaskFieldOptionActiveResponse) taskboard.SetTaskFieldOptionActiveResponse {
	return taskboard.SetTaskFieldOptionActiveResponse{Option: taskboardTaskFieldOptionForDTO(response.Option), AffectedTasks: taskboardTasksForDTO(response.AffectedTasks)}
}

func taskboardGetTaskBoardViewStateResponseForDTO(response GetTaskBoardViewStateResponse) taskboard.GetTaskBoardViewStateResponse {
	return taskboard.GetTaskBoardViewStateResponse{ViewState: taskboardTaskBoardViewStateForDTO(response.ViewState)}
}

func taskboardPutTaskBoardViewStateResponseForDTO(response PutTaskBoardViewStateResponse) taskboard.PutTaskBoardViewStateResponse {
	return taskboard.PutTaskBoardViewStateResponse{ViewState: taskboardTaskBoardViewStateForDTO(response.ViewState)}
}

func taskboardListTaskBoardSavedViewsResponseForDTO(response ListTaskBoardSavedViewsResponse) taskboard.ListTaskBoardSavedViewsResponse {
	return taskboard.ListTaskBoardSavedViewsResponse{SavedViews: taskboardTaskBoardSavedViewsForDTO(response.SavedViews)}
}

func taskboardCreateTaskBoardSavedViewResponseForDTO(response CreateTaskBoardSavedViewResponse) taskboard.CreateTaskBoardSavedViewResponse {
	return taskboard.CreateTaskBoardSavedViewResponse{SavedView: taskboardTaskBoardSavedViewForDTO(response.SavedView)}
}

func taskboardUpdateTaskBoardSavedViewResponseForDTO(response UpdateTaskBoardSavedViewResponse) taskboard.UpdateTaskBoardSavedViewResponse {
	return taskboard.UpdateTaskBoardSavedViewResponse{SavedView: taskboardTaskBoardSavedViewForDTO(response.SavedView)}
}

func taskboardGetTaskDetailResponseForDTO(response GetTaskDetailResponse) taskboard.GetTaskDetailResponse {
	return taskboard.GetTaskDetailResponse{Detail: taskboardTaskDetailForDTO(response.Detail)}
}

func taskboardGetTaskTimelineResponseForDTO(response GetTaskTimelineResponse) taskboard.GetTaskTimelineResponse {
	return taskboard.GetTaskTimelineResponse{Entries: taskboardTaskTimelineEntriesForDTO(response.Entries), HasMore: response.HasMore, NextCursor: taskboardTaskTimelineCursorForDTO(response.NextCursor)}
}

func markTaskReadResponseForDTO(response MarkTaskReadResponse) dto.MarkTaskReadResponse {
	return dto.MarkTaskReadResponse{
		Task: converter.ToTask(taskboardTaskForDTO(response.Task)),
		ReadState: dto.TaskReadState{
			UnreadCount:            response.ReadState.UnreadCount,
			ManualUnread:           response.ReadState.ManualUnread,
			LatestEventID:          response.ReadState.LatestEventID,
			LatestEventCreatedAt:   response.ReadState.LatestEventCreatedAt,
			LastReadEventID:        response.ReadState.LastReadEventID,
			LastReadEventCreatedAt: response.ReadState.LastReadEventCreatedAt,
		},
	}
}

func taskboardTaskBoardViewStateForDTO(state TaskBoardViewState) taskboard.TaskBoardViewState {
	return taskboard.TaskBoardViewState{View: state.View, SearchQuery: state.SearchQuery, QuickFilters: state.QuickFilters, StructuredFilters: state.StructuredFilters, SortMode: state.SortMode, CardPropertyToggles: state.CardPropertyToggles, DoneCollapsed: state.DoneCollapsed}
}

func taskboardTaskBoardSavedViewForDTO(savedView TaskBoardSavedView) taskboard.TaskBoardSavedView {
	return taskboard.TaskBoardSavedView{ID: savedView.ID, WorkspaceID: savedView.WorkspaceID, MemberID: savedView.MemberID, Name: savedView.Name, ViewState: taskboardTaskBoardViewStateForDTO(savedView.ViewState), CreatedAt: savedView.CreatedAt, UpdatedAt: savedView.UpdatedAt}
}

func taskboardTaskBoardSavedViewsForDTO(savedViews []TaskBoardSavedView) []taskboard.TaskBoardSavedView {
	if len(savedViews) == 0 {
		return nil
	}
	out := make([]taskboard.TaskBoardSavedView, 0, len(savedViews))
	for _, savedView := range savedViews {
		out = append(out, taskboardTaskBoardSavedViewForDTO(savedView))
	}
	return out
}

func taskboardTaskBrowseCursorForDTO(cursor TaskBrowseCursor) taskboard.TaskBrowseCursor {
	return taskboard.TaskBrowseCursor{SortMode: cursor.SortMode, TaskID: cursor.TaskID, CreatedAt: cursor.CreatedAt, ActivityAt: cursor.ActivityAt, Title: cursor.Title, PriorityMissing: cursor.PriorityMissing, PriorityLabel: cursor.PriorityLabel, TaskNumber: cursor.TaskNumber}
}

func taskboardTaskBrowseCursorsForDTO(cursors map[string]TaskBrowseCursor) map[string]taskboard.TaskBrowseCursor {
	if len(cursors) == 0 {
		return nil
	}
	out := make(map[string]taskboard.TaskBrowseCursor, len(cursors))
	for key, cursor := range cursors {
		out[key] = taskboardTaskBrowseCursorForDTO(cursor)
	}
	return out
}

func taskboardTaskTimelineCursorForDTO(cursor TaskTimelineCursor) taskboard.TaskTimelineCursor {
	return taskboard.TaskTimelineCursor{Seq: cursor.Seq, CreatedAt: cursor.CreatedAt, ID: cursor.ID}
}

func taskboardSubtaskCursorForDTO(cursor TaskSubtaskCursor) taskboard.SubtaskPageCursor {
	return taskboard.SubtaskPageCursor{CreatedAt: cursor.CreatedAt, ID: cursor.ID}
}

func taskboardListTaskSubtasksResponseForDTO(response ListTaskSubtasksResponse) taskboard.ListTaskSubtasksResponse {
	return taskboard.ListTaskSubtasksResponse{
		Tasks:      taskboardTasksForDTO(response.Tasks),
		HasMore:    response.HasMore,
		NextCursor: taskboardSubtaskCursorForDTO(response.NextCursor),
	}
}

func taskTimelineCursorForDTO(cursor taskboard.TaskTimelineCursor) TaskTimelineCursor {
	return TaskTimelineCursor{Seq: cursor.Seq, CreatedAt: cursor.CreatedAt, ID: cursor.ID}
}

func taskSubtaskCursorForDTO(cursor taskboard.SubtaskPageCursor) TaskSubtaskCursor {
	return TaskSubtaskCursor{CreatedAt: cursor.CreatedAt, ID: cursor.ID}
}

func taskboardTaskBrowseQueryForDTO(query TaskBrowseQuery) taskboard.TaskBrowseQuery {
	return taskboard.TaskBrowseQuery{Limit: query.Limit, SearchQuery: query.SearchQuery, SortMode: query.SortMode, StatusFilter: query.StatusFilter, QuickFilters: query.QuickFilters, StructuredFilters: query.StructuredFilters, IncludeSubtasks: query.IncludeSubtasks, Cursor: taskboardTaskBrowseCursorForDTO(query.Cursor), ColumnCursors: taskboardTaskBrowseCursorsForDTO(query.ColumnCursors)}
}

func taskboardTaskRowBrowsePageForDTO(page TaskRowBrowsePage) taskboard.TaskRowBrowsePage {
	return taskboard.TaskRowBrowsePage{QueryEcho: taskboardTaskBrowseQueryForDTO(page.QueryEcho), Rows: taskboardTasksForDTO(page.Rows), TotalCount: page.TotalCount, HasMore: page.HasMore, NextCursor: taskboardTaskBrowseCursorForDTO(page.NextCursor), ColumnCounts: page.ColumnCounts}
}

func taskboardRelatedTaskSignalForDTO(signal *RelatedTaskSignal) *taskboard.RelatedTaskSignal {
	if signal == nil {
		return nil
	}
	return &taskboard.RelatedTaskSignal{Type: signal.Type, Payload: signal.Payload, CreatedAt: signal.CreatedAt}
}

func taskboardRelatedTaskUnreadForDTO(unread RelatedTaskUnread) taskboard.RelatedTaskUnread {
	return taskboard.RelatedTaskUnread{Count: unread.Count, ManualUnread: unread.ManualUnread}
}

func taskboardRelatedTaskRowForDTO(row RelatedTaskRow) taskboard.RelatedTaskRow {
	return taskboard.RelatedTaskRow{
		TaskID:          row.TaskID,
		TaskScopeID:     row.TaskScopeID,
		ChannelID:       row.ChannelID,
		TaskNumber:      row.TaskNumber,
		Title:           row.Title,
		Status:          row.Status,
		ParentTaskID:    row.ParentTaskID,
		ParentTaskRef:   row.ParentTaskRef,
		Source:          taskboardContributionSourceFromDTO(row.Source),
		SourceThread:    taskboardTaskSourceThreadForDTO(row.SourceThread),
		AssigneeProfile: taskboardTaskPrincipalProfileForDTO(row.AssigneeProfile),
		LastSignal:      taskboardRelatedTaskSignalForDTO(row.LastSignal),
		Unread:          taskboardRelatedTaskUnreadForDTO(row.Unread),
		Route:           row.Route,
	}
}

func taskboardRelatedTaskRowsForDTO(rows []RelatedTaskRow) []taskboard.RelatedTaskRow {
	if len(rows) == 0 {
		return nil
	}
	out := make([]taskboard.RelatedTaskRow, 0, len(rows))
	for _, row := range rows {
		out = append(out, taskboardRelatedTaskRowForDTO(row))
	}
	return out
}

func taskboardRelatedTasksResponseForDTO(response RelatedTasksResponse) taskboard.RelatedTasksResponse {
	return taskboard.RelatedTasksResponse{Rows: taskboardRelatedTaskRowsForDTO(response.Tasks), HasMore: response.HasMore, NextCursor: taskboard.RelatedTaskCursor{TaskNumber: response.NextCursor.TaskNumber, TaskID: response.NextCursor.TaskID}}
}

func relatedTasksSummaryResponseForDTO(response RelatedTasksSummaryResponse) dto.RelatedTasksSummaryResponse {
	return dto.RelatedTasksSummaryResponse{
		TaskCount: response.TaskCount,
		Unread: dto.RelatedTaskUnread{
			Count:        response.Unread.Count,
			ManualUnread: response.Unread.ManualUnread,
		},
		StatusCounts: response.StatusCounts,
	}
}

func taskboardTaskBoardColumnPageForDTO(page TaskBoardColumnPage) taskboard.TaskBoardColumnPage {
	return taskboard.TaskBoardColumnPage{Tasks: taskboardTasksForDTO(page.Tasks), Count: page.Count, HasMore: page.HasMore, NextCursor: taskboardTaskBrowseCursorForDTO(page.NextCursor)}
}

func taskboardTaskBoardColumnsForDTO(columns map[string]TaskBoardColumnPage) map[string]taskboard.TaskBoardColumnPage {
	if len(columns) == 0 {
		return nil
	}
	out := make(map[string]taskboard.TaskBoardColumnPage, len(columns))
	for key, page := range columns {
		out[key] = taskboardTaskBoardColumnPageForDTO(page)
	}
	return out
}

func taskboardTaskBoardColumnDefinitionsForDTO(definitions []TaskBoardColumnDefinition) []taskboard.TaskBoardColumnDefinition {
	if len(definitions) == 0 {
		return nil
	}
	out := make([]taskboard.TaskBoardColumnDefinition, 0, len(definitions))
	for _, definition := range definitions {
		out = append(out, taskboard.TaskBoardColumnDefinition{
			Key:    definition.Key,
			Name:   definition.Name,
			Source: definition.Source,
		})
	}
	return out
}

func taskboardTaskBoardBrowsePageForDTO(page TaskBoardBrowsePage) taskboard.TaskBoardBrowsePage {
	return taskboard.TaskBoardBrowsePage{
		QueryEcho:         taskboardTaskBrowseQueryForDTO(page.QueryEcho),
		TotalCount:        page.TotalCount,
		ColumnOrder:       append([]string(nil), page.ColumnOrder...),
		ColumnDefinitions: taskboardTaskBoardColumnDefinitionsForDTO(page.ColumnDefinitions),
		ColumnCounts:      page.ColumnCounts,
		Columns:           taskboardTaskBoardColumnsForDTO(page.Columns),
	}
}

func taskboardTaskDiscussionEntryForDTO(entry TaskDiscussionEntry) taskboard.TaskDiscussionEntry {
	authorProfile := taskboardAuthorProfileFromDTO(entry.AuthorProfile)
	return taskboard.TaskDiscussionEntry{
		ID:                  entry.ID,
		TaskID:              entry.TaskID,
		Body:                entry.Body,
		AuthorProfile:       authorProfile,
		Source:              taskboardContributionSourceFromDTO(entry.Source),
		CreatedBy:           taskboardCompatActorFromAuthorProfile(authorProfile, entry.CreatedBy),
		ResolvedMentions:    taskboardResolvedMentionsForDTO(entry.ResolvedMentions),
		Reactions:           taskboardMessageReactionsForDTO(entry.Reactions),
		MessagePresentation: converter.ToMessagePresentation(entry.MessagePresentation),
		Attachments:         taskboardTaskDiscussionAttachmentsForDTO(entry.Attachments),
		CreatedAt:           entry.CreatedAt,
		UpdatedAt:           entry.UpdatedAt,
		EditedAt:            entry.EditedAt,
		EditedByMemberID:    entry.EditedByMemberID,
		EditRevision:        entry.EditRevision,
	}
}

func taskboardResolvedMentionsForDTO(mentions []ResolvedMention) []message.ResolvedMention {
	if len(mentions) == 0 {
		return nil
	}
	out := make([]message.ResolvedMention, 0, len(mentions))
	for _, mention := range mentions {
		out = append(out, message.ResolvedMention{
			Token: mention.Token, Type: mention.Type, ID: mention.ID,
			DisplayName: mention.DisplayName, ResolvedToken: mention.ResolvedToken,
		})
	}
	return out
}

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

func taskboardMessageReactionReactorForDTO(reactor MessageReactionReactor) message.MessageReactionReactor {
	return message.MessageReactionReactor{AuthorProfile: taskboardAuthorProfileFromDTO(reactor.AuthorProfile)}
}

func taskboardMessageReactionReactorsForDTO(reactors []MessageReactionReactor) []message.MessageReactionReactor {
	if len(reactors) == 0 {
		return nil
	}
	out := make([]message.MessageReactionReactor, 0, len(reactors))
	for _, reactor := range reactors {
		out = append(out, taskboardMessageReactionReactorForDTO(reactor))
	}
	return out
}

func taskboardMessageReactionsForDTO(reactions []MessageReactionAggregate) []message.MessageReactionAggregate {
	if len(reactions) == 0 {
		return nil
	}
	out := make([]message.MessageReactionAggregate, 0, len(reactions))
	for _, reaction := range reactions {
		out = append(out, message.MessageReactionAggregate{Emoji: reaction.Emoji, Count: reaction.Count, Reactors: taskboardMessageReactionReactorsForDTO(reaction.Reactors)})
	}
	return out
}

func taskboardTaskArtifactForDTO(artifact TaskArtifact) taskboard.TaskArtifact {
	createdByProfile := taskboardAuthorProfileFromDTO(artifact.CreatedByProfile)
	return taskboard.TaskArtifact{ID: artifact.ID, Kind: artifact.Kind, Title: artifact.Title, Payload: artifact.Payload, CreatedByProfile: taskboardTaskPrincipalProfileFromProfile(createdByProfile), CreatedByRef: taskboardTaskFactCompatRefFromAuthorProfile(createdByProfile, artifact.CreatedByRef), CreatedAt: artifact.CreatedAt}
}

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

func taskboardTaskReviewForDTO(review TaskReviewRequest) taskboard.TaskReviewRequest {
	requestedByProfile := taskboardAuthorProfileFromDTO(review.RequestedByProfile)
	reviewerProfile := taskboardAuthorProfileFromDTO(review.ReviewerProfile)
	decidedByProfile := taskboardAuthorProfileFromDTO(review.DecidedByProfile)
	return taskboard.TaskReviewRequest{ID: review.ID, Status: review.Status, RequestedByProfile: taskboardTaskPrincipalProfileFromProfile(requestedByProfile), ReviewerProfile: taskboardTaskPrincipalProfileFromProfile(reviewerProfile), DecidedByProfile: taskboardTaskPrincipalProfileFromProfile(decidedByProfile), RequestedByRef: taskboardTaskFactCompatRefFromAuthorProfile(requestedByProfile, review.RequestedByRef), ReviewerRef: taskboardTaskFactCompatRefFromAuthorProfile(reviewerProfile, review.ReviewerRef), Summary: review.Summary, Questions: review.Questions, Decision: review.Decision, DecisionSummary: review.DecisionSummary, DecisionQuestions: review.DecisionQuestions, DecidedByRef: taskboardTaskFactCompatRefFromAuthorProfile(decidedByProfile, review.DecidedByRef), ReviewTargetType: review.ReviewTargetType, ReviewTargetPayload: review.ReviewTargetPayload, RequestedAt: review.RequestedAt, DecidedAt: review.DecidedAt}
}

func taskboardTaskReviewsForDTO(reviews []TaskReviewRequest) []taskboard.TaskReviewRequest {
	if len(reviews) == 0 {
		return nil
	}
	out := make([]taskboard.TaskReviewRequest, 0, len(reviews))
	for _, review := range reviews {
		out = append(out, taskboardTaskReviewForDTO(review))
	}
	return out
}

func taskboardTaskActivityEventForDTO(event TaskActivityEvent) taskboard.TaskActivityEvent {
	authorProfile := taskboardAuthorProfileFromDTO(event.AuthorProfile)
	source := taskboardContributionSourceFromDTO(event.Source)
	return taskboard.TaskActivityEvent{ID: event.ID, Type: event.Type, AuthorProfile: authorProfile, Source: source, ActorRef: taskboardTaskActivityCompatActorRefFromAuthorProfile(authorProfile, event.ActorRef), ActorDisplayName: taskboardTaskActivityCompatActorDisplayNameFromAuthorProfile(authorProfile, event.ActorDisplayName), Payload: event.Payload, SourceKind: taskboardTaskActivityCompatSourceKindFromContributionSource(source, event.SourceKind), SourceMessageID: taskboardTaskActivityCompatSourceMessageIDFromContributionSource(source, event.SourceMessageID), SourceActivityID: taskboardTaskActivityCompatSourceActivityIDFromContributionSource(source, event.SourceActivityID), CreatedAt: event.CreatedAt}
}

func taskboardTaskActivityCompatActorRefFromAuthorProfile(profile message.AuthorProfile, fallback string) string {
	profile = profile.Normalize()
	if !profile.Valid() {
		return fallback
	}
	return firstNonEmptyTaskString(profile.MemberID, profile.PrincipalID, profile.TargetID, fallback)
}

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

func taskboardTaskFactCompatRefFromAuthorProfile(profile message.AuthorProfile, fallback string) string {
	profile = profile.Normalize()
	if !profile.Valid() {
		return fallback
	}
	return firstNonEmptyTaskString(profile.MemberID, profile.PrincipalID, profile.TargetID, fallback)
}

func taskboardTaskActivityCompatActorDisplayNameFromAuthorProfile(profile message.AuthorProfile, fallback string) string {
	profile = profile.Normalize()
	if !profile.Valid() {
		return fallback
	}
	return firstNonEmptyTaskString(profile.DisplayName, fallback)
}

func taskActivityCompatSourceKindFromContributionSource(source *ContributionSource, fallback string) string {
	if source == nil {
		return fallback
	}
	switch source.RefType {
	case message.ContributionSourceRefTypeMessage:
		return taskActivityCompatSourceKindMessage
	case message.ContributionSourceRefTypeTaskActivity:
		return taskActivityCompatSourceKindActivity
	default:
		return fallback
	}
}

func taskActivityCompatSourceMessageIDFromContributionSource(source *ContributionSource, fallback string) string {
	if source == nil {
		return fallback
	}
	switch source.RefType {
	case message.ContributionSourceRefTypeMessage:
		return firstNonEmptyTaskString(source.RefID, fallback)
	case message.ContributionSourceRefTypeTaskActivity:
		return ""
	default:
		return fallback
	}
}

func taskActivityCompatSourceActivityIDFromContributionSource(source *ContributionSource, fallback string) string {
	if source == nil {
		return fallback
	}
	switch source.RefType {
	case message.ContributionSourceRefTypeTaskActivity:
		return firstNonEmptyTaskString(source.RefID, source.EventID, fallback)
	case message.ContributionSourceRefTypeMessage:
		return ""
	default:
		return fallback
	}
}

func taskboardTaskActivityCompatSourceKindFromContributionSource(source message.ContributionSource, fallback string) string {
	source = source.Normalize()
	switch source.RefType {
	case message.ContributionSourceRefTypeMessage:
		return taskActivityCompatSourceKindMessage
	case message.ContributionSourceRefTypeTaskActivity:
		return taskActivityCompatSourceKindActivity
	default:
		return fallback
	}
}

func taskboardTaskActivityCompatSourceMessageIDFromContributionSource(source message.ContributionSource, fallback string) string {
	source = source.Normalize()
	switch source.RefType {
	case message.ContributionSourceRefTypeMessage:
		return firstNonEmptyTaskString(source.RefID, fallback)
	case message.ContributionSourceRefTypeTaskActivity:
		return ""
	default:
		return fallback
	}
}

func taskboardTaskActivityCompatSourceActivityIDFromContributionSource(source message.ContributionSource, fallback string) string {
	source = source.Normalize()
	switch source.RefType {
	case message.ContributionSourceRefTypeTaskActivity:
		return firstNonEmptyTaskString(source.RefID, source.EventID, fallback)
	case message.ContributionSourceRefTypeMessage:
		return ""
	default:
		return fallback
	}
}

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

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

func taskboardTaskDetailForDTO(detail TaskDetail) taskboard.TaskDetail {
	var parentTask *taskboard.Task
	if detail.ParentTask != nil {
		convertedParentTask := taskboardTaskForDTO(*detail.ParentTask)
		parentTask = &convertedParentTask
	}
	return taskboard.TaskDetail{Task: taskboardTaskForDTO(detail.Task), ParentTask: parentTask, Checkpoints: toTaskboardChecklistItems(detail.Checkpoints), Timeline: taskboardTaskTimelineEntriesForDTO(detail.Timeline), TimelineHasMore: detail.TimelineHasMore, TimelineNextCursor: taskboardTaskTimelineCursorForDTO(detail.TimelineNextCursor), AcceptanceCriteria: toTaskboardChecklistItems(detail.AcceptanceCriteria), ReviewChecklist: toTaskboardChecklistItems(detail.ReviewChecklist), Subtasks: taskboardTasksForDTO(detail.Subtasks), SubtasksHasMore: detail.SubtasksHasMore, SubtasksNextCursor: taskboardSubtaskCursorForDTO(detail.SubtasksNextCursor), PinnedMessageRefs: detail.PinnedMessageRefs, RelevantURLs: detail.RelevantURLs, WorkspaceRefs: detail.WorkspaceRefs, ReviewHistory: taskboardTaskReviewsForDTO(detail.ReviewHistory), Artifacts: taskboardTaskArtifactsForDTO(detail.Artifacts)}
}

func taskboardTaskViewsForDTO(views []TaskView) []taskboard.TaskView {
	if len(views) == 0 {
		return nil
	}
	out := make([]taskboard.TaskView, 0, len(views))
	for _, view := range views {
		createdByProfile := taskboardAuthorProfileFromDTO(view.CreatedByProfile)
		_, _, createdByName := taskBodyLegacyCreatedByFromAuthorProfile(createdByProfile, "", "", view.CreatedByName)
		out = append(out, taskboard.TaskView{
			TaskScopeID:      view.TaskScopeID,
			TaskNumber:       view.TaskNumber,
			Title:            view.Title,
			Status:           view.Status,
			AssigneeProfile:  taskboardTaskPrincipalProfileForDTO(view.AssigneeProfile),
			CreatedByProfile: taskboardTaskPrincipalProfileFromProfile(createdByProfile),
			CreatedByName:    createdByName,
		})
	}
	return out
}

func authorProfileFromTaskboard(profile message.AuthorProfile) *AuthorProfile {
	profile = profile.Normalize()
	if !profile.Valid() {
		return nil
	}
	return &AuthorProfile{
		ActorType:      profile.ActorType,
		PrincipalID:    profile.PrincipalID,
		MemberID:       profile.MemberID,
		TargetID:       profile.TargetID,
		DisplayName:    profile.DisplayName,
		AvatarURL:      profile.AvatarURL,
		ServiceKind:    profile.ServiceKind,
		ServiceSlug:    profile.ServiceSlug,
		ServiceBuiltin: profile.ServiceBuiltin,
		Badge:          profile.Badge,
	}
}

func taskboardAuthorProfileFromDTO(profile *AuthorProfile) message.AuthorProfile {
	if profile == nil {
		return message.AuthorProfile{}
	}
	return message.AuthorProfile{
		ActorType:      profile.ActorType,
		PrincipalID:    profile.PrincipalID,
		MemberID:       profile.MemberID,
		TargetID:       profile.TargetID,
		DisplayName:    profile.DisplayName,
		AvatarURL:      profile.AvatarURL,
		ServiceKind:    profile.ServiceKind,
		ServiceSlug:    profile.ServiceSlug,
		ServiceBuiltin: profile.ServiceBuiltin,
		Badge:          profile.Badge,
	}.Normalize()
}

// taskDiscussionCompatActorFromAuthorProfile derives the retained CreatedBy
// compatibility field from the resolved message AuthorProfile when adapting
// taskboard read models into the internal task API contract.
func taskDiscussionCompatActorFromAuthorProfile(profile *AuthorProfile, fallback taskboard.TaskActor) TaskActor {
	if profile == nil {
		return TaskActor{Type: fallback.Type, ID: fallback.ID, Name: fallback.Name}
	}
	actorType := profile.ActorType
	if actorType == message.AuthorActorTypeService {
		actorType = "app"
	}
	if actorType == "" {
		actorType = fallback.Type
	}
	return TaskActor{
		Type: actorType,
		ID:   firstNonEmptyTaskString(profile.PrincipalID, profile.TargetID, profile.MemberID, fallback.ID),
		Name: firstNonEmptyTaskString(profile.DisplayName, fallback.Name),
	}
}

// taskboardCompatActorFromAuthorProfile keeps the compatibility alias direction
// old -> new on the reverse adapter as well: CreatedBy is derived from
// AuthorProfile, not the other way around.
func taskboardCompatActorFromAuthorProfile(profile message.AuthorProfile, fallback TaskActor) taskboard.TaskActor {
	profile = profile.Normalize()
	if !profile.Valid() {
		return taskboard.TaskActor{Type: fallback.Type, ID: fallback.ID, Name: fallback.Name}
	}
	actorType := profile.ActorType
	if actorType == message.AuthorActorTypeService {
		actorType = "app"
	}
	return taskboard.TaskActor{
		Type: actorType,
		ID:   firstNonEmptyTaskString(profile.PrincipalID, profile.TargetID, profile.MemberID, fallback.ID),
		Name: firstNonEmptyTaskString(profile.DisplayName, fallback.Name),
	}
}

func firstNonEmptyTaskString(values ...string) string {
	for _, value := range values {
		if value != "" {
			return value
		}
	}
	return ""
}

func contributionSourceFromTaskboard(source message.ContributionSource) *ContributionSource {
	source = message.ResolveContributionSource("", source).Source.Normalize()
	if source.IsZero() {
		return nil
	}
	return &ContributionSource{
		Kind:      source.Kind,
		RefType:   source.RefType,
		RefID:     source.RefID,
		RunID:     source.RunID,
		EventID:   source.EventID,
		EventType: source.EventType,
		Label:     source.Label,
	}
}

func taskboardContributionSourceFromDTO(source *ContributionSource) message.ContributionSource {
	if source == nil {
		return message.ContributionSource{}
	}
	return message.ContributionSource{
		Kind:      source.Kind,
		RefType:   source.RefType,
		RefID:     source.RefID,
		RunID:     source.RunID,
		EventID:   source.EventID,
		EventType: source.EventType,
		Label:     source.Label,
	}.Normalize()
}
