package activity

import (
	"context"
	"testing"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/gormtest"

	"github.com/stretchr/testify/require"
	activitystore "github.com/tidbcloud/AgentTeam/internal/store/resource/activity"
)

func TestListResourceActivityAppliesDescriptorVisibilityNamesAndTombstones(t *testing.T) {
	ctx := context.Background()
	db, err := gormtest.OpenSQLiteDB(t)
	require.NoError(t, err)
	require.NoError(t, db.AutoMigrate(&activitystore.ResourceActivityEvent{}, &activitystore.ResourceActivityLatestCommit{}))
	activityStore := activitystore.New(db)
	now := time.Date(2026, 6, 24, 10, 0, 0, 0, time.UTC)

	_, err = activityStore.UpsertEvent(ctx, activitystore.ResourceActivityEvent{ID: "act_private", WorkspaceID: "ws_1", AssetType: activitystore.AssetTypeMemory, AssetID: "mem_private", EventType: "updated", OccurredAt: now.Add(4 * time.Minute)})
	require.NoError(t, err)
	_, err = activityStore.UpsertEvent(ctx, activitystore.ResourceActivityEvent{ID: "act_other_tombstone", WorkspaceID: "ws_1", AssetType: activitystore.AssetTypeMemory, AssetID: "mem_other", EventType: "deleted", ActorID: "other", DetailsJSON: `{"name":"Other memory"}`, OccurredAt: now.Add(3 * time.Minute)})
	require.NoError(t, err)
	_, err = activityStore.UpsertEvent(ctx, activitystore.ResourceActivityEvent{ID: "act_actor_wrong_type", WorkspaceID: "ws_1", AssetType: activitystore.AssetTypeMemory, AssetID: "mem_actor_wrong_type", EventType: "updated", ActorID: "viewer", DetailsJSON: `{"name":"Wrong type"}`, OccurredAt: now.Add(2 * time.Minute)})
	require.NoError(t, err)
	_, err = activityStore.UpsertEvent(ctx, activitystore.ResourceActivityEvent{ID: "act_actor_tombstone", WorkspaceID: "ws_1", AssetType: activitystore.AssetTypeMemory, AssetID: "mem_deleted", EventType: "deleted", ActorID: "viewer", DetailsJSON: `{"name":"Removed memory"}`, OccurredAt: now.Add(time.Minute)})
	require.NoError(t, err)
	_, err = activityStore.UpsertEvent(ctx, activitystore.ResourceActivityEvent{ID: "act_visible", WorkspaceID: "ws_1", AssetType: activitystore.AssetTypeMemory, AssetID: "mem_visible", EventType: "updated", DetailsJSON: `{"name":"Store name should not win"}`, OccurredAt: now})
	require.NoError(t, err)

	page, err := ListResourceActivity(ctx, activityStore, "ws_1", ResourceActivityDescriptor{
		AssetType: activitystore.AssetTypeMemory,
		VisibleAssets: []ResourceActivityVisibleAsset{
			{ID: " mem_visible ", Name: "Visible from descriptor"},
			{ID: "  ", Name: "Ignored blank asset"},
		},
		TombstoneActorID:    " viewer ",
		TombstoneEventTypes: []string{"deleted"},
	}, ResourceActivityListOptions{Limit: 10})

	require.NoError(t, err)
	require.Len(t, page.Items, 2)
	require.Equal(t, "act_actor_tombstone", page.Items[0].ID)
	require.Equal(t, "Removed memory", page.Items[0].AssetName)
	require.Equal(t, "act_visible", page.Items[1].ID)
	require.Equal(t, "Visible from descriptor", page.Items[1].AssetName)
}

func TestWriteResourceActivityEventStoresDisplaySnapshot(t *testing.T) {
	ctx := context.Background()
	db, err := gormtest.OpenSQLiteDB(t)
	require.NoError(t, err)
	require.NoError(t, db.AutoMigrate(&activitystore.ResourceActivityEvent{}, &activitystore.ResourceActivityLatestCommit{}))
	activityStore := activitystore.New(db)
	now := time.Date(2026, 6, 30, 1, 2, 3, 0, time.UTC)

	err = WriteResourceActivityEvent(ctx, activityStore, ResourceActivityEventInput{
		WorkspaceID:  "ws_1",
		AssetType:    activitystore.AssetTypeMemory,
		AssetID:      "mem_1",
		RepoFullName: "owner/mem_1.mem",
		EventType:    "commit_pushed",
		ActorType:    ActivityActorTypeUser,
		ActorID:      "alice@example.com",
		ActorName:    "Alice Git",
		Details:      map[string]any{"sha": "1234567890abcdef", "message": "update"},
		Display: GitActorActivityDisplay(
			"alice@example.com",
			"Alice Git",
			"alice@example.com",
			"alice",
		),
		TargetKind: "commit",
		TargetID:   "1234567890abcdef",
		OccurredAt: now,
	})
	require.NoError(t, err)

	page, err := ListResourceActivity(ctx, activityStore, "ws_1", ResourceActivityDescriptor{
		AssetType:     activitystore.AssetTypeMemory,
		VisibleAssets: []ResourceActivityVisibleAsset{{ID: "mem_1", Name: "Product Memory"}},
	}, ResourceActivityListOptions{Limit: 10})
	require.NoError(t, err)
	require.Len(t, page.Items, 1)
	item := page.Items[0]
	require.Equal(t, "Alice Git", item.Display.Actor.Name)
	require.Equal(t, "git", item.Display.Actor.Kind)
	require.Equal(t, "alice@example.com", item.Display.Actor.Email)
	require.Equal(t, "Product Memory", item.Display.Resource.Name)
	require.Equal(t, "owner/mem_1.mem", item.Display.Resource.RepoFullName)
	require.Equal(t, "1234567890ab", item.Display.Target.Label)
	require.NotEmpty(t, item.Display.Meta)
	require.Equal(t, "1234567890ab", item.Display.Meta[0].Value)
	for _, meta := range item.Display.Meta {
		require.NotEqual(t, "repo", meta.Key)
		require.NotEqual(t, "owner/mem_1.mem", meta.Value)
	}
}

func TestListResourceActivityBuildsReadableDisplayForLegacyEvents(t *testing.T) {
	ctx := context.Background()
	db, err := gormtest.OpenSQLiteDB(t)
	require.NoError(t, err)
	require.NoError(t, db.AutoMigrate(&activitystore.ResourceActivityEvent{}))
	activityStore := activitystore.New(db)
	now := time.Date(2026, 6, 30, 1, 2, 3, 0, time.UTC)

	_, err = activityStore.UpsertEvent(ctx, activitystore.ResourceActivityEvent{
		ID:           "act_legacy",
		WorkspaceID:  "ws_1",
		AssetType:    activitystore.AssetTypeSkill,
		AssetID:      "sk_1",
		RepoFullName: "owner/sk_1.sk",
		EventType:    "pr_opened",
		ActorType:    ActivityActorTypeUser,
		ActorID:      "git-author@example.com",
		ActorName:    "Git Author",
		DetailsJSON:  `{"number":42,"title":"Improve docs"}`,
		TargetKind:   "pull",
		TargetID:     "42",
		TargetURL:    "https://ags.local/owner/sk_1/pulls/42",
		OccurredAt:   now,
	})
	require.NoError(t, err)

	page, err := ListResourceActivity(ctx, activityStore, "ws_1", ResourceActivityDescriptor{
		AssetType:     activitystore.AssetTypeSkill,
		VisibleAssets: []ResourceActivityVisibleAsset{{ID: "sk_1", Name: "Reusable Skill"}},
	}, ResourceActivityListOptions{Limit: 10})
	require.NoError(t, err)
	require.Len(t, page.Items, 1)
	item := page.Items[0]
	require.Equal(t, "Git Author", item.Display.Actor.Name)
	require.Equal(t, "git-author@example.com", item.Display.Actor.Email)
	require.Equal(t, "Reusable Skill", item.Display.Resource.Name)
	require.Equal(t, "#42", item.Display.Target.Label)
	require.Equal(t, "https://ags.local/owner/sk_1/pulls/42", item.Display.Meta[0].ExternalURL)
}

func TestWithDataPlaneActivityDisplayPreservesActorAndEnrichesTarget(t *testing.T) {
	repo := ResourceActivityDataPlaneRepository{
		AssetType:    activitystore.AssetTypeKB,
		AssetID:      "src_1",
		RepoRole:     "primary",
		RepoFullName: "owner/source.kb",
	}
	event := WithDataPlaneActivityDisplay(repo, ResourceActivityDataPlaneEvent{
		ActorType: ActivityActorTypeUser,
		ActorID:   "alice@example.com",
		ActorName: "Alice Git",
		Details: map[string]any{
			"sourceName": "Design Docs",
			"sha":        "abcdef1234567890",
		},
		TargetKind: "commit",
		TargetID:   "abcdef1234567890",
		TargetURL:  "https://ags.local/owner/source.kb/commit/abcdef1234567890",
	}, GitActorActivityDisplay("alice@example.com", "Alice Git", "alice@example.com", "alice"))

	require.Equal(t, "Alice Git", event.Display.Actor.Name)
	require.Equal(t, "git", event.Display.Actor.Kind)
	require.Equal(t, "alice@example.com", event.Display.Actor.ID)
	require.Equal(t, "alice@example.com", event.Display.Actor.Email)
	require.Equal(t, "Design Docs", event.Display.Resource.Name)
	require.Equal(t, "abcdef123456", event.Display.Target.Label)
	require.Equal(t, "https://ags.local/owner/source.kb/commit/abcdef1234567890", event.Display.Meta[0].ExternalURL)
	for _, meta := range event.Display.Meta {
		require.NotEqual(t, "repo", meta.Key)
		require.NotEqual(t, "owner/source.kb", meta.Value)
	}
}

func TestDefaultResourceActivityDisplayMetaDoesNotRepeatRepoWhenResourceFallsBackToRepo(t *testing.T) {
	display := completedResourceActivityDisplay(ResourceActivityDisplay{}, activitystore.ResourceActivityEvent{
		AssetType:    activitystore.AssetTypeMemory,
		AssetID:      "mem_1",
		RepoFullName: "owner/mem_1.mem",
		TargetKind:   "settings",
		TargetID:     "settings",
	}, map[string]any{}, "")

	require.Equal(t, "owner/mem_1.mem", display.Resource.Name)
	for _, meta := range display.Meta {
		require.NotEqual(t, "repo", meta.Key)
		require.NotEqual(t, "owner/mem_1.mem", meta.Value)
	}
}
