package kb_test

import (
	"bytes"
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/stretchr/testify/require"
	kbservice "github.com/tidbcloud/AgentTeam/internal/service/kb"
	"github.com/tidbcloud/AgentTeam/internal/store/kb"
)

type apiWikiGenerator struct{}

func (apiWikiGenerator) Generate(context.Context, kbservice.KBWikiGenerationInput) (kbservice.KBWikiGenerationOutput, error) {
	return kbservice.KBWikiGenerationOutput{}, nil
}

type apiWorkspaceGovernanceAuthorizer struct{}

func (apiWorkspaceGovernanceAuthorizer) CanAdministerWorkspace(_ context.Context, _, principalID string) (bool, error) {
	return principalID == "owner", nil
}

func TestKBHandlerQueuesRevisionFencedDirectWikiGeneration(t *testing.T) {
	mux, svc, store := newKBTestMuxServiceStoreWithOptions(t, nil, func(options *kbservice.KBServiceOptions) {
		options.WikiGenerator = apiWikiGenerator{}
		options.WorkspaceGovernanceAuthorizer = apiWorkspaceGovernanceAuthorizer{}
	})
	base, _, err := svc.CreateKnowledgeBaseSetup(t.Context(), "wsp_1", "owner", kbservice.KBMutation{Name: "Product docs"})
	require.NoError(t, err)
	now := time.Date(2026, 8, 9, 2, 3, 4, 0, time.UTC)
	_, err = store.CreateSource(t.Context(), kb.Source{
		ID: "src_1", WorkspaceID: "wsp_1", KnowledgeBaseID: base.ID,
		Name: "Repository", SourceType: "file_repository", Status: "ready",
		SyncStatus: "ready", IndexStatus: "ready", Config: "{}",
		CreatedBy: "owner", UpdatedBy: "owner", CreatedAt: now, UpdatedAt: now,
	})
	require.NoError(t, err)
	_, err = store.CreateSourceRevision(t.Context(), kb.SourceRevision{
		ID: "rev_1", WorkspaceID: "wsp_1", KnowledgeBaseID: base.ID,
		SourceID: "src_1", SourceCommitSHA: "source-commit", IsCurrent: true,
		FreshnessStatus: "current", CreatedAt: now, UpdatedAt: now,
	})
	require.NoError(t, err)

	request := httptest.NewRequest(http.MethodPost, "/api/v1/kb/"+base.ID+"/setup-session/sources/src_1/wiki/direct-generation", bytes.NewBufferString(`{
		"sourceRevision":"rev_1",
		"scope":"full",
		"language":"zh",
		"reason":"Create onboarding docs",
		"idempotencyKey":"api-request-1"
	}`))
	request.Header.Set("Authorization", "Bearer "+jwt(map[string]any{"sub": "owner"}))
	request.Header.Set("X-Workspace-Id", "wsp_1")
	request.Header.Set("Content-Type", "application/json")
	recorder := httptest.NewRecorder()
	mux.ServeHTTP(recorder, request)
	require.Equal(t, http.StatusAccepted, recorder.Code, recorder.Body.String())
	var response struct {
		CanDirectWikiGeneration bool `json:"canDirectWikiGeneration"`
		Sources                 []struct {
			SourceRevision string `json:"sourceRevision"`
			WikiState      string `json:"wikiState"`
			WikiRequest    struct {
				Status string `json:"status"`
				Reason string `json:"reason"`
			} `json:"wikiRequest"`
		} `json:"sources"`
	}
	require.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &response))
	require.True(t, response.CanDirectWikiGeneration)
	require.Len(t, response.Sources, 1)
	require.Equal(t, "rev_1", response.Sources[0].SourceRevision)
	require.Equal(t, kb.GenerationRequestStatusQueued, response.Sources[0].WikiState)
	require.Equal(t, kb.GenerationRequestStatusQueued, response.Sources[0].WikiRequest.Status)
	require.Equal(t, "Create onboarding docs", response.Sources[0].WikiRequest.Reason)

	_, err = svc.UpsertKnowledgeBasePermission(t.Context(), "wsp_1", "owner", base.ID, kbservice.KBPermissionGrantMutation{
		SubjectType: "user", SubjectID: "member", Role: "edit", RoleSet: true,
	})
	require.NoError(t, err)
	denied := httptest.NewRequest(http.MethodPost, "/api/v1/kb/"+base.ID+"/setup-session/sources/src_1/wiki/direct-generation", bytes.NewBufferString(`{
		"sourceRevision":"rev_1","idempotencyKey":"member-request"
	}`))
	denied.Header.Set("Authorization", "Bearer "+jwt(map[string]any{"sub": "member"}))
	denied.Header.Set("X-Workspace-Id", "wsp_1")
	denied.Header.Set("Content-Type", "application/json")
	deniedRecorder := httptest.NewRecorder()
	mux.ServeHTTP(deniedRecorder, denied)
	require.Equal(t, http.StatusForbidden, deniedRecorder.Code, deniedRecorder.Body.String())
}
