package agent

import (
	"github.com/tidbcloud/AgentTeam/internal/api/dto/converter"
	"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 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 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),
		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 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),
		Attachments:   taskboardTaskDiscussionAttachmentsForDTO(entry.Attachments),
		CreatedAt:     entry.CreatedAt,
	}
}

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 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 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),
		PinnedMessageRefs:        detail.PinnedMessageRefs,
		PinnedMessageRefsHasMore: detail.PinnedMessageRefsHasMore,
		RelevantURLs:             detail.RelevantURLs,
		RelevantURLsHasMore:      detail.RelevantURLsHasMore,
		WorkspaceRefs:            detail.WorkspaceRefs,
		WorkspaceRefsHasMore:     detail.WorkspaceRefsHasMore,
		Artifacts:                taskboardTaskArtifactsForDTO(detail.Artifacts),
	}
}

func taskboardTaskTimelineCursorForDTO(token string) taskboard.TaskTimelineCursor {
	cursor, err := converter.FromTaskTimelineCursorToken(token)
	if err != nil {
		return taskboard.TaskTimelineCursor{}
	}
	return cursor
}

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 canonical message AuthorProfile when adapting
// taskboard read models into the agent 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()
}

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 taskboardCreatedTasksForDTO(tasks []CreatedTask) []taskboard.CreatedTask {
	if len(tasks) == 0 {
		return nil
	}
	out := make([]taskboard.CreatedTask, 0, len(tasks))
	for _, task := range tasks {
		out = append(out, taskboard.CreatedTask{TaskScopeID: task.TaskScopeID, TaskNumber: task.TaskNumber, Title: task.Title, Reused: task.Reused})
	}
	return out
}

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