package memory

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

	"github.com/stretchr/testify/require"
	"github.com/tidbcloud/AgentTeam/internal/api/meshxapi"
	"github.com/tidbcloud/AgentTeam/internal/identity/trusted"
	memoryservice "github.com/tidbcloud/AgentTeam/internal/service/memory"
)

type fakeMemoryContentService struct {
	fakeMemoryService
	checkpointMemoryID string
	checkpointInput    memoryservice.MemoryCheckpointMutation
	checkpointResult   memoryservice.MemoryContentVersion
	contentVersions    memoryservice.MemoryContentVersionPage
	contentCompare     []memoryservice.MemoryContentCompareEntry
	contentBackups     []memoryservice.MemoryContentBackup
	backupMemoryID     string
	backupInput        memoryservice.MemoryBackupMutation
	contentBackup      memoryservice.MemoryContentBackup
	restoreMemoryID    string
	restoreInput       memoryservice.MemoryRestoreMutation
	contentRestore     memoryservice.MemoryContentVersion
	contentArchive     []byte
}

func (s *fakeMemoryContentService) PublishMemoryCheckpoint(_ context.Context, _, _, memoryID string, input memoryservice.MemoryCheckpointMutation) (memoryservice.MemoryContentVersion, error) {
	s.checkpointMemoryID = memoryID
	s.checkpointInput = input
	return s.checkpointResult, nil
}

func (s *fakeMemoryContentService) ListMemoryContentVersions(context.Context, string, string, string, int, int) (memoryservice.MemoryContentVersionPage, error) {
	return s.contentVersions, nil
}

func (s *fakeMemoryContentService) CompareMemoryContentVersions(context.Context, string, string, string, string, string) ([]memoryservice.MemoryContentCompareEntry, error) {
	return s.contentCompare, nil
}

func (s *fakeMemoryContentService) CreateMemoryContentBackup(_ context.Context, _, _ string, memoryID string, input memoryservice.MemoryBackupMutation) (memoryservice.MemoryContentBackup, error) {
	s.backupMemoryID = memoryID
	s.backupInput = input
	return s.contentBackup, nil
}

func (s *fakeMemoryContentService) ListMemoryContentBackups(context.Context, string, string, string, int) ([]memoryservice.MemoryContentBackup, error) {
	return s.contentBackups, nil
}

func (s *fakeMemoryContentService) RestoreMemoryContentVersion(_ context.Context, _, _ string, memoryID string, input memoryservice.MemoryRestoreMutation) (memoryservice.MemoryContentVersion, error) {
	s.restoreMemoryID = memoryID
	s.restoreInput = input
	return s.contentRestore, nil
}

func (s *fakeMemoryContentService) OpenMemoryContentArchive(context.Context, string, string, string, string) (memoryservice.MemoryContentArchive, error) {
	return memoryservice.MemoryContentArchive{Body: io.NopCloser(bytes.NewReader(s.contentArchive))}, nil
}

func TestNativeMemoryContentRoutesUseExactVersionDTOs(t *testing.T) {
	now := time.Date(2026, 8, 22, 8, 0, 0, 0, time.UTC)
	version := memoryservice.MemoryContentVersion{
		VersionID: "ctv_2", ParentVersionID: "ctv_1", OperationID: "checkpoint-1", OperationKind: "checkpoint",
		ReplicaID: "replica-1", CoveredFromSequence: 1, CoveredThroughSequence: 2, Generation: 3,
		RootDigest: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", FileCount: 1, SizeBytes: 9,
		SourceAuthorName: "Memory Author", SourceAuthorEmail: "author@example.test", SourceMessage: "historical checkpoint",
		CreatedBy: "usr_1", CreatedAt: now,
	}
	restoredVersion := version
	restoredVersion.OperationID = "restore-1"
	restoredVersion.OperationKind = "restore"
	restoredVersion.TargetVersionID = "ctv_1"
	svc := &fakeMemoryContentService{
		checkpointResult: version,
		contentVersions:  memoryservice.MemoryContentVersionPage{Items: []memoryservice.MemoryContentVersion{version}, Page: 1, PerPage: 10},
		contentCompare:   []memoryservice.MemoryContentCompareEntry{{Path: "MEMORY.md", Change: "modified", BeforeDigest: "old", AfterDigest: "new"}},
		contentBackup:    memoryservice.MemoryContentBackup{ID: "ctp_1", VersionID: "ctv_2", Label: "backup", CreatedBy: "usr_1", CreatedAt: now},
		contentBackups:   []memoryservice.MemoryContentBackup{{ID: "ctp_1", VersionID: "ctv_2", Label: "backup", CreatedBy: "usr_1", CreatedAt: now}},
		contentRestore:   restoredVersion,
		contentArchive:   []byte("zip-body"),
	}
	mux := http.NewServeMux()
	RegisterRoutes(mux, NewMemoryHandler(svc), nil)

	payload := map[string]any{
		"operationId": "checkpoint-1", "replicaId": "replica-1", "expectedVersionId": "ctv_1", "generation": 3,
		"coveredFromSequence": 1, "coveredThroughSequence": 2,
		"files": []map[string]any{{"path": "MEMORY.md", "contentBase64": base64.StdEncoding.EncodeToString([]byte("# memory\n"))}},
	}
	body, err := json.Marshal(payload)
	require.NoError(t, err)
	rec := serveAuthenticatedMemoryRequest(mux, http.MethodPost, "/api/v1/memory/mem_1/checkpoints", body)
	require.Equal(t, http.StatusCreated, rec.Code, rec.Body.String())
	require.Equal(t, "mem_1", svc.checkpointMemoryID)
	require.Equal(t, []byte("# memory\n"), svc.checkpointInput.Files[0].Content)
	require.Contains(t, rec.Body.String(), `"versionId":"ctv_2"`)
	require.NotContains(t, rec.Body.String(), "repository")
	require.NotContains(t, rec.Body.String(), "branch")
	require.NotContains(t, rec.Body.String(), "commitSha")

	rec = serveAuthenticatedMemoryRequest(mux, http.MethodGet, "/api/v1/memory/mem_1/versions?page=1&per_page=10", nil)
	require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
	require.Contains(t, rec.Body.String(), `"operationKind":"checkpoint"`)
	require.Contains(t, rec.Body.String(), `"sourceAuthorName":"Memory Author"`)
	require.Contains(t, rec.Body.String(), `"sourceAuthorEmail":"author@example.test"`)
	require.Contains(t, rec.Body.String(), `"sourceMessage":"historical checkpoint"`)
	rec = serveAuthenticatedMemoryRequest(mux, http.MethodGet, "/api/v1/memory/mem_1/versions:compare?beforeVersionId=ctv_1&afterVersionId=ctv_2", nil)
	require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
	require.Contains(t, rec.Body.String(), `"path":"MEMORY.md"`)
	rec = serveAuthenticatedMemoryRequest(mux, http.MethodGet, "/api/v1/memory/mem_1/backups", nil)
	require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
	require.Contains(t, rec.Body.String(), `"id":"ctp_1"`)
	rec = serveAuthenticatedMemoryRequest(mux, http.MethodPost, "/api/v1/memory/mem_1/backups", []byte(`{"expectedVersionId":"ctv_2","generation":3,"label":"backup"}`))
	require.Equal(t, http.StatusCreated, rec.Code, rec.Body.String())
	require.Equal(t, "mem_1", svc.backupMemoryID)
	require.Equal(t, memoryservice.MemoryBackupMutation{ExpectedVersionID: "ctv_2", Generation: 3, Label: "backup"}, svc.backupInput)
	require.Contains(t, rec.Body.String(), `"versionId":"ctv_2"`)
	rec = serveAuthenticatedMemoryRequest(mux, http.MethodPost, "/api/v1/memory/mem_1/versions:restore", []byte(`{"operationId":"restore-1","expectedVersionId":"ctv_2","targetVersionId":"ctv_1","generation":3}`))
	require.Equal(t, http.StatusCreated, rec.Code, rec.Body.String())
	require.Equal(t, "mem_1", svc.restoreMemoryID)
	require.Equal(t, memoryservice.MemoryRestoreMutation{OperationID: "restore-1", ExpectedVersionID: "ctv_2", TargetVersionID: "ctv_1", Generation: 3}, svc.restoreInput)
	require.Contains(t, rec.Body.String(), `"targetVersionId":"ctv_1"`)
	rec = serveAuthenticatedMemoryRequest(mux, http.MethodGet, "/api/v1/memory/mem_1/versions/ctv_2/archive", nil)
	require.Equal(t, http.StatusOK, rec.Code, rec.Body.String())
	require.Equal(t, "application/zip", rec.Header().Get("Content-Type"))
	require.Equal(t, "zip-body", rec.Body.String())
}

func serveAuthenticatedMemoryRequest(handler http.Handler, method, target string, body []byte) *httptest.ResponseRecorder {
	request := httptest.NewRequest(method, target, bytes.NewReader(body))
	request = request.WithContext(trusted.WithIdentity(request.Context(), trusted.Identity{Subject: "usr_1"}))
	request.Header.Set(meshxapi.HeaderWorkspaceID, "wsp_1")
	if body != nil {
		request.Header.Set("Content-Type", "application/json")
	}
	recorder := httptest.NewRecorder()
	handler.ServeHTTP(recorder, request)
	return recorder
}
