package kb

import (
	"context"
	"testing"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/service/memory"
	"github.com/tidbcloud/AgentTeam/internal/service/resource/activity"

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

func newKBActivityServiceForTest(t *testing.T) (*KBService, *activitystore.Store) {
	t.Helper()
	db, err := openServiceTestSQLiteDB(t)
	require.NoError(t, err)
	require.NoError(t, db.AutoMigrate(
		&kb.KnowledgeBase{},
		&kb.KnowledgeBaseGroup{},
		&kb.KnowledgeBaseTag{},
		&kb.KnowledgeBaseTagAssignment{},
		&kb.KnowledgeBasePin{},
		&kb.KnowledgeBaseGrant{},
		&kb.KnowledgeBaseSourceRepositoryAccessOverride{},
		&kb.Source{},
		&kb.SourceMirror{},
		&kb.AnalysisRun{},
		&kb.SourceRevision{},
		&kb.Job{},
		&kb.AuditEvent{},
		&repos.ResourceRepository{},
		&repos.ResourceRepositoryLifecycleEvent{},
		&repos.ResourceRepositoryAccessGrant{},
		&activitystore.ResourceActivityEvent{},
		&activitystore.ResourceActivityLatestCommit{},
		&activitystore.ResourceActivityProjectorCheckpoint{},
	))
	now := func() time.Time { return time.Date(2026, 6, 26, 1, 2, 3, 0, time.UTC) }
	activityStore := activitystore.New(db)
	svc := NewKBServiceWithOptions(kb.New(db), now, KBServiceOptions{
		WikiStore:            &fakeAGSRepositoryStore{},
		RepositoryStore:      repos.New(db),
		AgentOwnershipSource: fakeAgentOwnershipSource{agents: []memory.AgentOwnership{{ID: "agt_reader"}}},
		ActivityStore:        activityStore,
	})
	return svc, activityStore
}

func TestListKnowledgeBaseActivityMapsKBFilterToVisibleSourceEvents(t *testing.T) {
	ctx := context.Background()
	svc, activityStore := newKBActivityServiceForTest(t)
	base, err := svc.CreateKnowledgeBase(ctx, "wsp_1", "owner", KBMutation{Name: "Runbooks"})
	require.NoError(t, err)
	first, err := svc.CreateSource(ctx, "wsp_1", "owner", base.ID, KBSourceMutation{SourceType: "codebase", Name: "AgentTeam"})
	require.NoError(t, err)
	second, err := svc.CreateSource(ctx, "wsp_1", "owner", base.ID, KBSourceMutation{SourceType: "file_repository", Name: "Docs"})
	require.NoError(t, err)
	otherBase, err := svc.CreateKnowledgeBase(ctx, "wsp_1", "owner", KBMutation{Name: "Private"})
	require.NoError(t, err)
	otherSource, err := svc.CreateSource(ctx, "wsp_1", "owner", otherBase.ID, KBSourceMutation{SourceType: "codebase", Name: "Other"})
	require.NoError(t, err)
	now := svc.now()
	for _, event := range []activitystore.ResourceActivityEvent{
		{ID: "act_first", WorkspaceID: "wsp_1", AssetType: activitystore.AssetTypeKB, AssetID: first.ID, EventType: "commit_pushed", DetailsJSON: `{"message":"first"}`, OccurredAt: now.Add(2 * time.Minute)},
		{ID: "act_second", WorkspaceID: "wsp_1", AssetType: activitystore.AssetTypeKB, AssetID: second.ID, EventType: "wiki_page_updated", DetailsJSON: `{"title":"Docs"}`, OccurredAt: now.Add(time.Minute)},
		{ID: "act_other", WorkspaceID: "wsp_1", AssetType: activitystore.AssetTypeKB, AssetID: otherSource.ID, EventType: "commit_pushed", OccurredAt: now},
	} {
		_, err := activityStore.UpsertEvent(ctx, event)
		require.NoError(t, err)
	}

	page, err := svc.ListKnowledgeBaseActivity(ctx, "wsp_1", "owner", activity.ResourceActivityListOptions{AssetID: base.ID, Limit: 20})

	require.NoError(t, err)
	require.GreaterOrEqual(t, len(page.Items), 2)
	require.Equal(t, "act_first", page.Items[0].ID)
	require.Equal(t, "AgentTeam", page.Items[0].AssetName)
	require.Equal(t, base.ID, page.Items[0].Details["knowledgeBaseId"])
	require.Equal(t, first.ID, page.Items[0].Details["sourceId"])
	require.Equal(t, "AgentTeam", page.Items[0].Details["sourceName"])
	require.Equal(t, "act_second", page.Items[1].ID)
	require.Equal(t, "Docs", page.Items[1].AssetName)
}

func TestListKnowledgeBaseActivityAppliesExplicitUserDenial(t *testing.T) {
	ctx := context.Background()
	svc, activityStore := newKBActivityServiceForTest(t)
	publicRole := "view"
	publicKB, err := svc.CreateKnowledgeBase(ctx, "wsp_1", "owner", KBMutation{Name: "Shared", DefaultWorkspaceMemberRole: &publicRole})
	require.NoError(t, err)
	publicSource, err := svc.CreateSource(ctx, "wsp_1", "owner", publicKB.ID, KBSourceMutation{SourceType: "codebase", Name: "Shared source"})
	require.NoError(t, err)
	privateKB, err := svc.CreateKnowledgeBase(ctx, "wsp_1", "owner", KBMutation{Name: "Denied"})
	require.NoError(t, err)
	privateSource, err := svc.CreateSource(ctx, "wsp_1", "owner", privateKB.ID, KBSourceMutation{SourceType: "codebase", Name: "Private source"})
	require.NoError(t, err)
	_, err = svc.UpsertKnowledgeBasePermission(ctx, "wsp_1", "owner", privateKB.ID, KBPermissionGrantMutation{SubjectType: "user", SubjectID: "viewer", Role: "none", RoleSet: true})
	require.NoError(t, err)
	now := svc.now()
	for _, event := range []activitystore.ResourceActivityEvent{
		{ID: "act_shared", WorkspaceID: "wsp_1", AssetType: activitystore.AssetTypeKB, AssetID: publicSource.ID, EventType: "commit_pushed", OccurredAt: now.Add(time.Minute)},
		{ID: "act_private", WorkspaceID: "wsp_1", AssetType: activitystore.AssetTypeKB, AssetID: privateSource.ID, EventType: "commit_pushed", OccurredAt: now},
	} {
		_, err := activityStore.UpsertEvent(ctx, event)
		require.NoError(t, err)
	}

	page, err := svc.ListKnowledgeBaseActivity(ctx, "wsp_1", "viewer", activity.ResourceActivityListOptions{EventType: "commit_pushed", Limit: 10})

	require.NoError(t, err)
	require.Len(t, page.Items, 1)
	require.Equal(t, "act_shared", page.Items[0].ID)
}

func TestKnowledgeBaseAuditWritesResourceActivityWithNavigationDetails(t *testing.T) {
	ctx := context.Background()
	svc, _ := newKBActivityServiceForTest(t)
	base, err := svc.CreateKnowledgeBase(ctx, "wsp_1", "owner", KBMutation{Name: "Runbooks"})
	require.NoError(t, err)
	source, err := svc.CreateSource(ctx, "wsp_1", "owner", base.ID, KBSourceMutation{SourceType: "codebase", Name: "AgentTeam"})
	require.NoError(t, err)

	page, err := svc.ListKnowledgeBaseActivity(ctx, "wsp_1", "owner", activity.ResourceActivityListOptions{Limit: 10})

	require.NoError(t, err)
	byType := map[string]activity.ResourceActivityItem{}
	for _, item := range page.Items {
		byType[item.EventType] = item
	}
	require.Equal(t, base.ID, byType["created"].AssetID)
	require.Equal(t, "resource", byType["created"].Target.Kind)
	require.Equal(t, source.ID, byType["source_created"].AssetID)
	require.Equal(t, "source", byType["source_created"].Target.Kind)
	require.Equal(t, base.ID, byType["source_created"].Details["knowledgeBaseId"])
	require.Equal(t, source.ID, byType["source_created"].Details["sourceId"])
	require.Equal(t, "AgentTeam", byType["source_created"].Details["sourceName"])
}

func TestKBServiceWritesSourceRepositoryLifecycleActivity(t *testing.T) {
	ctx := context.Background()
	svc, activityStore := newKBActivityServiceForTest(t)
	base, err := svc.CreateKnowledgeBase(ctx, "wsp_1", "owner", KBMutation{Name: "Runbooks"})
	require.NoError(t, err)

	source, err := svc.CreateSource(ctx, "wsp_1", "owner", base.ID, KBSourceMutation{SourceType: "codebase", Name: "AgentTeam"})
	require.NoError(t, err)
	repos, err := svc.repositoryStore.ListForResource(ctx, "wsp_1", repos.ResourceTypeKBSource, source.ID)
	require.NoError(t, err)
	require.Len(t, repos, 2)
	for _, repo := range repos {
		created, err := activityStore.GetBySourceEventID(ctx, "wsp_1", activity.ActivitySourceControl, activity.ResourceActivityControlSourceEventID(repo.ResourceType, repo.ResourceID, repo.RepoRole, "created"))
		require.NoError(t, err)
		require.Equal(t, activity.ResourceRepositoryActivityCreated, created.EventType)
		require.Equal(t, "repository", created.TargetKind)
		require.Equal(t, repo.RepoFullName, created.TargetID)
		require.Equal(t, repo.RepoFullName, created.RepoFullName)
		require.Equal(t, "owner", created.ActorID)
	}

	require.NoError(t, svc.DeleteSource(ctx, "wsp_1", "owner", base.ID, source.ID))

	for _, repo := range repos {
		deleted, err := activityStore.GetBySourceEventID(ctx, "wsp_1", activity.ActivitySourceControl, activity.ResourceActivityControlSourceEventID(repo.ResourceType, repo.ResourceID, repo.RepoRole, "deleted"))
		require.NoError(t, err)
		require.Equal(t, activity.ResourceRepositoryActivityDeleted, deleted.EventType)
		require.Equal(t, "repository", deleted.TargetKind)
		require.Equal(t, repo.RepoFullName, deleted.TargetID)
		require.Equal(t, "owner", deleted.ActorID)
	}
}

func TestKnowledgeBaseActivityCanFilterSingleSource(t *testing.T) {
	ctx := context.Background()
	svc, activityStore := newKBActivityServiceForTest(t)
	base, err := svc.CreateKnowledgeBase(ctx, "wsp_1", "owner", KBMutation{Name: "Runbooks"})
	require.NoError(t, err)
	first, err := svc.CreateSource(ctx, "wsp_1", "owner", base.ID, KBSourceMutation{SourceType: "codebase", Name: "AgentTeam"})
	require.NoError(t, err)
	second, err := svc.CreateSource(ctx, "wsp_1", "owner", base.ID, KBSourceMutation{SourceType: "codebase", Name: "Docs"})
	require.NoError(t, err)
	_, err = activityStore.UpsertEvent(ctx, activitystore.ResourceActivityEvent{ID: "act_first", WorkspaceID: "wsp_1", AssetType: activitystore.AssetTypeKB, AssetID: first.ID, EventType: "commit_pushed", DetailsJSON: `{}`, OccurredAt: svc.now().Add(time.Minute)})
	require.NoError(t, err)
	_, err = activityStore.UpsertEvent(ctx, activitystore.ResourceActivityEvent{ID: "act_second", WorkspaceID: "wsp_1", AssetType: activitystore.AssetTypeKB, AssetID: second.ID, EventType: "commit_pushed", DetailsJSON: `{}`, OccurredAt: svc.now()})
	require.NoError(t, err)

	page, err := svc.ListKnowledgeBaseActivity(ctx, "wsp_1", "owner", activity.ResourceActivityListOptions{AssetID: second.ID, EventType: "commit_pushed", Limit: 10})

	require.NoError(t, err)
	require.Len(t, page.Items, 1)
	require.Equal(t, "act_second", page.Items[0].ID)
	require.Equal(t, second.ID, page.Items[0].Details["sourceId"])
}
