package kb_test

import (
	"bytes"
	"encoding/json"
	"fmt"
	"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"
)

func TestKBHandlerEditsOfficialThroughRevisionFencedDirectSave(t *testing.T) {
	mux, svc, store := newKBTestMuxServiceStoreWithOptions(t, nil, func(options *kbservice.KBServiceOptions) {
		options.WorkspaceGovernanceAuthorizer = apiWorkspaceGovernanceAuthorizer{}
	})
	base, err := svc.CreateKnowledgeBase(t.Context(), "wsp_1", "owner", kbservice.KBMutation{Name: "Published name"})
	require.NoError(t, err)
	publishedRevision := base.PublishedRevision
	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: "codebase", Status: "ready", SyncStatus: "ready", IndexStatus: "ready",
		CreatedBy: "owner", UpdatedBy: "owner", CreatedAt: now, UpdatedAt: now,
	})
	require.NoError(t, err)
	bypass := officialEditRequest(t, http.MethodPatch, "/api/v1/kb/"+base.ID, `{"name":"Bypassed name","description":""}`)
	bypassRecorder := httptest.NewRecorder()
	mux.ServeHTTP(bypassRecorder, bypass)
	require.Equal(t, http.StatusConflict, bypassRecorder.Code, bypassRecorder.Body.String())

	start := officialEditRequest(t, http.MethodPost, "/api/v1/kb/"+base.ID+"/official-edit-session", "")
	startRecorder := httptest.NewRecorder()
	mux.ServeHTTP(startRecorder, start)
	require.Equal(t, http.StatusOK, startRecorder.Code, startRecorder.Body.String())
	var setup struct {
		Mode              string `json:"mode"`
		WorkingRevision   string `json:"workingRevision"`
		PublishedRevision string `json:"publishedRevision"`
		Version           int64  `json:"version"`
		BasicInfo         struct {
			Name    string `json:"name"`
			GroupID string `json:"groupId"`
		} `json:"basicInfo"`
	}
	require.NoError(t, json.Unmarshal(startRecorder.Body.Bytes(), &setup))
	require.Equal(t, kb.SetupModeEditOfficial, setup.Mode)
	require.Equal(t, "Published name", setup.BasicInfo.Name)
	require.Equal(t, publishedRevision, setup.PublishedRevision)
	require.NotEqual(t, publishedRevision, setup.WorkingRevision)

	_, err = svc.SetKnowledgeBaseSetupSourceArtifact(t.Context(), "wsp_1", "owner", base.ID, "src_1", "wiki", "skipped")
	require.NoError(t, err)
	ready, err := svc.SetKnowledgeBaseSetupSourceArtifact(t.Context(), "wsp_1", "owner", base.ID, "src_1", "dashboard", "skipped")
	require.NoError(t, err)

	basicBody := fmt.Sprintf(`{"name":"Working name","description":"Isolated metadata","groupId":%q,"tagIds":[],"expectedVersion":%d}`, setup.BasicInfo.GroupID, ready.Session.Version)
	update := officialEditRequest(t, http.MethodPatch, "/api/v1/kb/"+base.ID+"/setup-session/official-basic", basicBody)
	updateRecorder := httptest.NewRecorder()
	mux.ServeHTTP(updateRecorder, update)
	require.Equal(t, http.StatusOK, updateRecorder.Code, updateRecorder.Body.String())
	var updated struct {
		WorkingRevision       string   `json:"workingRevision"`
		PublishedRevision     string   `json:"publishedRevision"`
		OfficialContentDigest string   `json:"officialContentDigest"`
		Version               int64    `json:"version"`
		OfficialChangeFields  []string `json:"officialChangeFields"`
		CanSaveOfficial       bool     `json:"canSaveOfficial"`
	}
	require.NoError(t, json.Unmarshal(updateRecorder.Body.Bytes(), &updated))
	require.Contains(t, updated.OfficialChangeFields, "name")
	require.Len(t, updated.OfficialContentDigest, 64)
	require.True(t, updated.CanSaveOfficial)
	readerView, err := svc.GetKnowledgeBase(t.Context(), "wsp_1", "owner", base.ID)
	require.NoError(t, err)
	require.Equal(t, "Published name", readerView.Name)
	require.Equal(t, publishedRevision, readerView.PublishedRevision)
	read := officialEditRequest(t, http.MethodGet, "/api/v1/kb/"+base.ID, "")
	readRecorder := httptest.NewRecorder()
	mux.ServeHTTP(readRecorder, read)
	require.Equal(t, http.StatusOK, readRecorder.Code, readRecorder.Body.String())
	var readerPayload struct {
		Name              string `json:"name"`
		PublishedRevision string `json:"publishedRevision"`
		WorkingRevision   string `json:"workingRevision"`
	}
	require.NoError(t, json.Unmarshal(readRecorder.Body.Bytes(), &readerPayload))
	require.Equal(t, "Published name", readerPayload.Name)
	require.Equal(t, publishedRevision, readerPayload.PublishedRevision)
	require.Empty(t, readerPayload.WorkingRevision)

	saveBody := fmt.Sprintf(`{"workingRevision":%q,"publishedRevision":%q,"contentDigest":%q,"expectedVersion":%d,"clientRequestId":"api-save-1"}`, updated.WorkingRevision, updated.PublishedRevision, updated.OfficialContentDigest, updated.Version)
	save := officialEditRequest(t, http.MethodPost, "/api/v1/kb/"+base.ID+"/setup-session/official-save", saveBody)
	saveRecorder := httptest.NewRecorder()
	mux.ServeHTTP(saveRecorder, save)
	require.Equal(t, http.StatusOK, saveRecorder.Code, saveRecorder.Body.String())
	var saved struct {
		Name              string `json:"name"`
		PublishedRevision string `json:"publishedRevision"`
		WorkingRevision   string `json:"workingRevision"`
	}
	require.NoError(t, json.Unmarshal(saveRecorder.Body.Bytes(), &saved))
	require.Equal(t, "Working name", saved.Name)
	require.Equal(t, updated.WorkingRevision, saved.PublishedRevision)
	require.Empty(t, saved.WorkingRevision)
}

func TestKBHandlerOrganizationAdminConvertsAnotherOwnersOfficialToDraft(t *testing.T) {
	mux, svc, _ := newKBTestMuxServiceStoreWithOptions(t, nil, func(options *kbservice.KBServiceOptions) {
		options.WorkspaceGovernanceAuthorizer = apiWorkspaceGovernanceAuthorizer{}
	})
	base, err := svc.CreateKnowledgeBase(t.Context(), "wsp_1", "other-owner", kbservice.KBMutation{Name: "Published runbooks"})
	require.NoError(t, err)

	request := officialEditRequest(t, http.MethodPost, "/api/v1/kb/"+base.ID+"/draft-conversion", "")
	recorder := httptest.NewRecorder()
	mux.ServeHTTP(recorder, request)
	require.Equal(t, http.StatusOK, recorder.Code, recorder.Body.String())
	var setup struct {
		Mode              string `json:"mode"`
		WorkingRevision   string `json:"workingRevision"`
		PublishedRevision string `json:"publishedRevision"`
		BasicInfo         struct {
			Name string `json:"name"`
		} `json:"basicInfo"`
	}
	require.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &setup))
	require.Equal(t, kbservice.KBSetupModeCreateDraft, setup.Mode)
	require.NotEmpty(t, setup.WorkingRevision)
	require.Empty(t, setup.PublishedRevision)
	require.Equal(t, "Published runbooks", setup.BasicInfo.Name)

	converted, err := svc.GetKnowledgeBase(t.Context(), "wsp_1", "owner", base.ID)
	require.NoError(t, err)
	require.Equal(t, kbservice.KBPublicationDraft, converted.PublicationState)
	require.Empty(t, converted.PublishedRevision)
}

func officialEditRequest(t *testing.T, method, path, body string) *http.Request {
	t.Helper()
	request := httptest.NewRequest(method, path, bytes.NewBufferString(body))
	request.Header.Set("Authorization", "Bearer "+jwt(map[string]any{"sub": "owner"}))
	request.Header.Set("X-Workspace-Id", "wsp_1")
	if body != "" {
		request.Header.Set("Content-Type", "application/json")
	}
	return request
}
