package kb

import (
	"context"
	"errors"
	"strings"
	"testing"
	"time"

	"github.com/stretchr/testify/require"
	contentservice "github.com/tidbcloud/AgentTeam/internal/service/content"
	contentstore "github.com/tidbcloud/AgentTeam/internal/store/content"
	storekb "github.com/tidbcloud/AgentTeam/internal/store/kb"
)

func TestNativeUADashboardRepositoryPublishesAndReplaysExactVersions(t *testing.T) {
	fixture := newKBSourceContentMigrationFixture(t, true)
	fixture.workspace.allowed["wsp_1\x00viewer"] = true
	repository := NativeUADashboardRepository{Content: fixture.authorized}
	firstFiles := testNativeUADashboardFiles(`{"version":1}`)
	request := UADashboardRepositoryPublishRequest{
		WorkspaceID: "wsp_1", SourceID: "src_1", SourceRevisionID: "rev_1", Actor: "owner",
		Files: firstFiles, Limits: DefaultUADashboardImportLimits(),
	}

	first, err := repository.PublishUADashboard(t.Context(), request)
	require.NoError(t, err)
	require.NotEmpty(t, first.CommitSHA)
	require.Len(t, first.FileDigests, 5)
	replayed, err := repository.PublishUADashboard(t.Context(), request)
	require.NoError(t, err)
	require.Equal(t, first.CommitSHA, replayed.CommitSHA)
	request.PreviousPublicationID = first.CommitSHA
	secondIdentical, err := repository.PublishUADashboard(t.Context(), request)
	require.NoError(t, err)
	require.NotEqual(t, first.CommitSHA, secondIdentical.CommitSHA)
	replayedSecond, err := repository.PublishUADashboard(t.Context(), request)
	require.NoError(t, err)
	require.Equal(t, secondIdentical.CommitSHA, replayedSecond.CommitSHA)

	read := func(subject, version, digest string) (UADashboardRepositoryFile, error) {
		return repository.ReadUADashboardFile(t.Context(), UADashboardRepositoryFileRequest{
			WorkspaceID: "wsp_1", SourceID: "src_1", Subject: subject,
			DashboardCommitSHA: version, Path: UADashboardKnowledgeGraphFile,
			ExpectedBlobSHA: digest, MaxBytes: DefaultUADashboardImportLimits().FileBytes,
		})
	}
	file, err := read("viewer", first.CommitSHA, first.FileDigests[UADashboardKnowledgeGraphFile])
	require.NoError(t, err)
	require.Equal(t, firstFiles[UADashboardKnowledgeGraphFile], file.Content)

	request.SourceRevisionID = "rev_2"
	request.PreviousPublicationID = secondIdentical.CommitSHA
	request.Files = testNativeUADashboardFiles(`{"version":2}`)
	second, err := repository.PublishUADashboard(t.Context(), request)
	require.NoError(t, err)
	require.NotEqual(t, first.CommitSHA, second.CommitSHA)
	oldFile, err := read("viewer", first.CommitSHA, first.FileDigests[UADashboardKnowledgeGraphFile])
	require.NoError(t, err)
	require.Equal(t, firstFiles[UADashboardKnowledgeGraphFile], oldFile.Content)
	newFile, err := read("viewer", second.CommitSHA, second.FileDigests[UADashboardKnowledgeGraphFile])
	require.NoError(t, err)
	require.Equal(t, request.Files[UADashboardKnowledgeGraphFile], newFile.Content)

	fixture.workspace.allowed["wsp_1\x00viewer"] = false
	_, err = read("viewer", first.CommitSHA, first.FileDigests[UADashboardKnowledgeGraphFile])
	require.ErrorIs(t, err, contentservice.ErrAccessDenied)
}

func TestKBServiceRoutesNativeUADashboardWithoutInsightsRepository(t *testing.T) {
	fixture := newKBSourceContentMigrationFixture(t, true)
	require.NoError(t, fixture.db.AutoMigrate(&storekb.AuditEvent{}))
	fixture.workspace.admins["wsp_1\x00owner"] = true
	authority := contentstore.New(fixture.db)
	_, err := authority.ActivateNativeAuthority(t.Context(), contentstore.ActivateNativeAuthorityParams{
		WorkspaceID: "wsp_1", Domain: kbSourceContentDomain, BindingKey: "src_1",
		Checkpoint: "native-group", Actor: "owner", Now: time.Date(2026, 8, 22, 2, 0, 0, 0, time.UTC),
	})
	require.NoError(t, err)
	legacy := &fakeUADashboardRepository{commitSHA: strings.Repeat("a", 40)}
	service := NewKBServiceWithOptions(fixture.store, func() time.Time { return time.Date(2026, 8, 22, 2, 1, 0, 0, time.UTC) }, KBServiceOptions{
		ContentAuthority: authority, RepositoryStore: fixture.repos,
		UADashboardRepository: legacy, NativeUADashboardRepository: NativeUADashboardRepository{Content: fixture.authorized},
		PrincipalWorkspaceAccessVerifier: fixture.workspace, WorkspaceGovernanceAuthorizer: fixture.workspace,
	})
	archive := writeUATestArchive(t, "zip", testUAArchiveEntries(t, "", testUAGraph(t)))
	status, err := service.ImportUADashboard(context.Background(), "wsp_1", "owner", "kb_1", "src_1", UADashboardImport{
		ArchivePath: archive, Filename: "dashboard.zip",
	})
	require.NoError(t, err)
	require.Equal(t, "ready", status.State)
	require.True(t, strings.HasPrefix(status.DashboardCommitSHA, "ctv_"))
	require.Zero(t, legacy.publishCalls)

	file, err := service.ReadUADashboardFile(context.Background(), "wsp_1", "owner", "kb_1", "src_1", UADashboardKnowledgeGraphFile)
	require.NoError(t, err)
	require.Equal(t, testUAGraph(t), file.Content)
	require.Contains(t, file.ETag, status.DashboardCommitSHA)
	require.Zero(t, legacy.readCalls)

	nativeFailure := &fakeUADashboardRepository{publishErr: errors.New("native publication unavailable")}
	service.nativeUADashboardRepository = nativeFailure
	_, err = service.ImportUADashboard(context.Background(), "wsp_1", "owner", "kb_1", "src_1", UADashboardImport{
		ArchivePath: archive, Filename: "dashboard.zip",
	})
	require.ErrorContains(t, err, "native publication unavailable")
	require.Equal(t, 1, nativeFailure.publishCalls)
	require.Zero(t, legacy.publishCalls, "native failure must not fall back to AGS")
}

func testNativeUADashboardFiles(graph string) map[string][]byte {
	return map[string][]byte{
		UADashboardKnowledgeGraphFile: []byte(graph),
		UADashboardDomainGraphFile:    []byte(`{"kind":"domain"}`),
		UADashboardMetaFile:           []byte(`{"meta":true}`),
		UADashboardConfigFile:         []byte(`{"config":true}`),
		UADashboardFingerprintsFile:   []byte(`{"fingerprints":[]}`),
	}
}
