package task

import (
	"encoding/json"
	"testing"

	"github.com/stretchr/testify/require"

	"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/message"
	"github.com/tidbcloud/AgentTeam/internal/store/taskboard"
)

func TestTaskChecklistItemsFromDTOPreservesNilVsEmpty(t *testing.T) {
	convert := func(items []dto.TaskChecklistItem) []TaskChecklistItem {
		return taskChecklistItemsForPort(converter.FromTaskChecklistItems(items))
	}

	require.Nil(t, convert(nil))

	cleared := convert([]dto.TaskChecklistItem{})
	require.NotNil(t, cleared)
	require.Empty(t, cleared)

	converted := convert([]dto.TaskChecklistItem{{ID: "item-1", Text: "Verify", Checked: true}})
	require.Len(t, converted, 1)
	require.Equal(t, "item-1", converted[0].ID)
	require.Equal(t, "Verify", converted[0].Text)
	require.True(t, converted[0].Checked)
}

func TestTaskAdapterPreservesReadState(t *testing.T) {
	t.Parallel()

	source := taskboard.Task{
		ID:          "tsk_1",
		ChannelID:   "chn_1",
		TaskNumber:  7,
		Title:       "Preserve read state",
		Status:      "in_review",
		ReadState:   &taskboard.TaskReadState{UnreadCount: 2, ManualUnread: true, LatestEventID: "tev_1"},
		CreatedByID: "usr_1",
	}

	apiTask := fromTaskboardTask(source)
	payload, err := json.Marshal(apiTask)
	require.NoError(t, err)
	require.Contains(t, string(payload), `"readState"`)

	roundTripped := taskboardTaskForDTO(apiTask)
	require.NotNil(t, roundTripped.ReadState)
	require.Equal(t, int64(2), roundTripped.ReadState.UnreadCount)
	require.True(t, roundTripped.ReadState.ManualUnread)
	require.Equal(t, "tev_1", roundTripped.ReadState.LatestEventID)

	dtoTask := converter.ToTask(roundTripped)
	require.NotNil(t, dtoTask.ReadState)
	require.Equal(t, int64(2), dtoTask.ReadState.UnreadCount)
	require.True(t, dtoTask.ReadState.ManualUnread)
	require.Equal(t, "tev_1", dtoTask.ReadState.LatestEventID)

	markRead := markTaskReadResponseForDTO(MarkTaskReadResponse{
		Task:      apiTask,
		ReadState: TaskReadState{UnreadCount: 0, LatestEventID: "tev_2", LastReadEventID: "tev_2"},
	})
	require.Equal(t, "tsk_1", markRead.Task.ID)
	require.Equal(t, int64(0), markRead.ReadState.UnreadCount)
	require.Equal(t, "tev_2", markRead.ReadState.LatestEventID)
	require.Equal(t, "tev_2", markRead.ReadState.LastReadEventID)
}

func TestTaskAdaptersPreservePrincipalProfiles(t *testing.T) {
	source := taskboard.Task{
		ID: "tsk_profile",
		CreatedByProfile: &message.AuthorProfile{
			ActorType:   message.AuthorActorTypeHuman,
			PrincipalID: "usr_creator",
			MemberID:    "mem_creator",
			DisplayName: "Creator",
		},
		CreatedByType: "agent",
		CreatedByID:   "legacy_creator",
		CreatedByName: "Legacy Creator",
		AssigneeProfile: &message.AuthorProfile{
			ActorType:   message.AuthorActorTypeAgent,
			PrincipalID: "agt_claimed",
			MemberID:    "mem_claimed",
			DisplayName: "Claimed Agent",
		},
	}

	apiTask := fromTaskboardTask(source)
	require.NotNil(t, apiTask.CreatedByProfile)
	require.Equal(t, "usr_creator", apiTask.CreatedByProfile.PrincipalID)
	require.NotNil(t, apiTask.AssigneeProfile)
	require.Equal(t, "agt_claimed", apiTask.AssigneeProfile.PrincipalID)

	roundTripped := taskboardTaskForDTO(Task{
		ID:               apiTask.ID,
		CreatedByProfile: apiTask.CreatedByProfile,
		CreatedByType:    "agent",
		CreatedByID:      "legacy_creator_roundtrip",
		CreatedByName:    "Legacy Creator Roundtrip",
		AssigneeProfile:  apiTask.AssigneeProfile,
	})
	require.NotNil(t, roundTripped.CreatedByProfile)
	require.Equal(t, "usr_creator", roundTripped.CreatedByProfile.PrincipalID)
	require.Equal(t, "usr_creator", roundTripped.CreatedByID)
	require.Equal(t, "Creator", roundTripped.CreatedByName)
	require.NotNil(t, roundTripped.AssigneeProfile)
	require.Equal(t, "agt_claimed", roundTripped.AssigneeProfile.PrincipalID)
}

func TestTaskViewReverseAdapterPreservesPrincipalProfiles(t *testing.T) {
	views := taskboardTaskViewsForDTO([]TaskView{{
		TaskScopeID: "tsk_scope",
		TaskNumber:  42,
		Title:       "Canonical list identity",
		Status:      "todo",
		CreatedByProfile: &AuthorProfile{
			ActorType:   message.AuthorActorTypeHuman,
			PrincipalID: "usr_profile_creator",
			DisplayName: "Profile Creator",
		},
		CreatedByName: "Legacy Creator",
		AssigneeProfile: &AuthorProfile{
			ActorType:   message.AuthorActorTypeAgent,
			PrincipalID: "agt_profile_claimed",
			DisplayName: "Profile Claimed",
		},
	}})

	require.Len(t, views, 1)
	require.NotNil(t, views[0].CreatedByProfile)
	require.Equal(t, "usr_profile_creator", views[0].CreatedByProfile.PrincipalID)
	require.Equal(t, "Profile Creator", views[0].CreatedByName)
	require.NotNil(t, views[0].AssigneeProfile)
	require.Equal(t, "agt_profile_claimed", views[0].AssigneeProfile.PrincipalID)
}

func TestTaskAdaptersPreserveSourceProvenance(t *testing.T) {
	source := message.ContributionSource{
		Kind:    message.ContributionSourceKindAgentRuntime,
		RefType: message.ContributionSourceRefTypeMessage,
		RefID:   "msg_task_origin",
		Label:   "Creator Agent",
	}
	taskSourceThread := &taskboard.TaskSourceThread{
		ThreadChannelID: "thr_task_origin",
		ParentChannelID: "chn_task_origin",
		ParentMessageID: "msg_task_origin",
		Ref:             "#task-origin",
	}

	apiTask := fromTaskboardTask(taskboard.Task{
		ID:           "tsk_source",
		Source:       source,
		SourceThread: taskSourceThread,
	})
	require.NotNil(t, apiTask.Source)
	require.Equal(t, message.ContributionSourceKindAgentRuntime, apiTask.Source.Kind)
	require.Equal(t, message.ContributionSourceRefTypeMessage, apiTask.Source.RefType)
	require.Equal(t, "msg_task_origin", apiTask.Source.RefID)
	require.NotNil(t, apiTask.SourceThread)
	require.Equal(t, "thr_task_origin", apiTask.SourceThread.ThreadChannelID)

	roundTripped := taskboardTaskForDTO(Task{
		ID:           apiTask.ID,
		Source:       apiTask.Source,
		SourceThread: apiTask.SourceThread,
	})
	require.Equal(t, message.ContributionSourceKindAgentRuntime, roundTripped.Source.Kind)
	require.Equal(t, message.ContributionSourceRefTypeMessage, roundTripped.Source.RefType)
	require.Equal(t, "msg_task_origin", roundTripped.Source.RefID)
	require.NotNil(t, roundTripped.SourceThread)
	require.Equal(t, "thr_task_origin", roundTripped.SourceThread.ThreadChannelID)
}

func TestTaskDiscussionAdaptersDeriveCompatibilityActorFromAuthorProfile(t *testing.T) {
	source := taskboard.TaskDiscussionEntry{
		ID:   "msg_service",
		Body: "service event discussion",
		AuthorProfile: message.AuthorProfile{
			ActorType:   message.AuthorActorTypeService,
			PrincipalID: "svc_install_1",
			MemberID:    "mem_service",
			TargetID:    "gitea",
			DisplayName: "Gitea",
			ServiceKind: "app",
			ServiceSlug: "gitea",
		},
		CreatedBy:           taskboard.TaskActor{Type: "human", ID: "usr_legacy", Name: "Legacy User"},
		MessagePresentation: domain.NewWorkflowRecoveryPresentation("wri_1", "wfx_1", "wfx_1", 1, 2),
	}

	apiEntry := fromTaskboardDiscussionEntry(source)
	require.Equal(t, TaskActor{Type: "app", ID: "svc_install_1", Name: "Gitea"}, apiEntry.CreatedBy)
	require.NotNil(t, apiEntry.MessagePresentation)
	require.Equal(t, domain.MessagePresentationKeyWorkflowRecovery, apiEntry.MessagePresentation.Key)

	roundTripped := taskboardTaskDiscussionEntryForDTO(TaskDiscussionEntry{
		ID:                  apiEntry.ID,
		Body:                apiEntry.Body,
		AuthorProfile:       apiEntry.AuthorProfile,
		CreatedBy:           TaskActor{Type: "human", ID: "usr_legacy_roundtrip", Name: "Legacy Roundtrip"},
		MessagePresentation: apiEntry.MessagePresentation,
	})
	require.Equal(t, taskboard.TaskActor{Type: "app", ID: "svc_install_1", Name: "Gitea"}, roundTripped.CreatedBy)
	require.NotNil(t, roundTripped.MessagePresentation)
	require.Equal(t, domain.MessagePresentationKeyWorkflowRecovery, roundTripped.MessagePresentation.Key)
}

func TestTaskActivityAdaptersDeriveCompatibilityActorFromAuthorProfile(t *testing.T) {
	source := taskboard.TaskActivityEvent{
		ID:   "act_service",
		Type: "checkpoint_added",
		AuthorProfile: message.AuthorProfile{
			ActorType:   message.AuthorActorTypeService,
			PrincipalID: "svc_install_1",
			MemberID:    "mem_service",
			TargetID:    "gitea",
			DisplayName: "Gitea",
			ServiceKind: "app",
			ServiceSlug: "gitea",
		},
		ActorRef:         "legacy_member",
		ActorDisplayName: "Legacy User",
		SourceKind:       "message",
		SourceMessageID:  "legacy_msg",
		SourceActivityID: "legacy_activity",
	}

	apiEvent := fromTaskboardActivityEvent(source)
	require.NotNil(t, apiEvent.AuthorProfile)
	require.Equal(t, "svc_install_1", apiEvent.AuthorProfile.PrincipalID)
	require.Nil(t, apiEvent.Source)
	require.Equal(t, "mem_service", apiEvent.ActorRef)
	require.Equal(t, "Gitea", apiEvent.ActorDisplayName)
	require.Equal(t, "message", apiEvent.SourceKind)
	require.Equal(t, "legacy_msg", apiEvent.SourceMessageID)
	require.Equal(t, "legacy_activity", apiEvent.SourceActivityID)

	roundTripped := taskboardTaskActivityEventForDTO(TaskActivityEvent{
		ID:               apiEvent.ID,
		Type:             apiEvent.Type,
		AuthorProfile:    apiEvent.AuthorProfile,
		Source:           apiEvent.Source,
		ActorRef:         "legacy_roundtrip",
		ActorDisplayName: "Legacy Roundtrip",
		SourceKind:       "message",
		SourceMessageID:  "legacy_msg_roundtrip",
		SourceActivityID: "legacy_activity_roundtrip",
	})
	require.Equal(t, "mem_service", roundTripped.ActorRef)
	require.Equal(t, "Gitea", roundTripped.ActorDisplayName)
	require.Equal(t, message.AuthorActorTypeService, roundTripped.AuthorProfile.ActorType)
	require.True(t, roundTripped.Source.IsZero())
	require.Equal(t, "message", roundTripped.SourceKind)
	require.Equal(t, "legacy_msg_roundtrip", roundTripped.SourceMessageID)
	require.Equal(t, "legacy_activity_roundtrip", roundTripped.SourceActivityID)
}

func TestTaskFactAdaptersDeriveCompatibilityRefsFromPrincipalProfiles(t *testing.T) {
	artifact := fromTaskboardArtifact(taskboard.TaskArtifact{
		ID:   "art_1",
		Kind: "report",
		CreatedByProfile: &message.AuthorProfile{
			ActorType:   message.AuthorActorTypeService,
			PrincipalID: "svc_reporter",
			MemberID:    "mem_reporter",
			DisplayName: "Reporter",
		},
		CreatedByRef: "legacy_artifact_creator",
	})
	require.NotNil(t, artifact.CreatedByProfile)
	require.Equal(t, "svc_reporter", artifact.CreatedByProfile.PrincipalID)
	require.Equal(t, "mem_reporter", artifact.CreatedByRef)

	roundTrippedArtifact := taskboardTaskArtifactForDTO(TaskArtifact{
		ID:               artifact.ID,
		CreatedByProfile: artifact.CreatedByProfile,
		CreatedByRef:     "legacy_artifact_roundtrip",
	})
	require.NotNil(t, roundTrippedArtifact.CreatedByProfile)
	require.Equal(t, "svc_reporter", roundTrippedArtifact.CreatedByProfile.PrincipalID)
	require.Equal(t, "mem_reporter", roundTrippedArtifact.CreatedByRef)
}
