package daemon

import (
	"context"
	"encoding/json"
	"errors"
	"testing"
	"time"

	"github.com/stretchr/testify/require"
)

func TestAgentMemorySyncStatusUsesAdvertisedDaemonEndpoint(t *testing.T) {
	serverConn, desktopConn := newDaemonProtocolTestPair(t)
	client := newWorkspaceListRemoteReadyClient(serverConn)
	now := time.Date(2026, time.August, 21, 9, 0, 0, 0, time.UTC)
	hub := &Hub{
		remoteEndpoints:     defaultRemoteEndpointRegistry(),
		elasticRequestLinks: newTestElasticRequestLinkManager(t, &now, ElasticRequestLinkManagerConfig{}),
		clients:             map[string]*clientState{client.machineID: client},
	}
	retryAt := now.Add(time.Minute)
	responsePayload := mustMarshalRemoteWorkspaceRawTest(t, memorySyncStatusRemoteResponse{
		Available: true,
		Status: &AgentMemorySyncStatus{
			State:   "pending_sync",
			Reason:  "push_failed",
			RetryAt: retryAt,
		},
	})
	type assignmentResult struct {
		endpointID string
		agentID    string
		err        error
	}
	assignmentCh := make(chan assignmentResult, 1)

	go func() {
		var assignment RemoteRequestAssignment[json.RawMessage]
		if err := desktopConn.ReadJSON(&assignment); err != nil {
			assignmentCh <- assignmentResult{err: err}
			return
		}
		var payload memorySyncRemoteRequestPayload
		if err := json.Unmarshal(assignment.Payload, &payload); err != nil {
			assignmentCh <- assignmentResult{endpointID: assignment.EndpointID, err: err}
			return
		}
		result, err := json.Marshal(RemoteRequestResult[json.RawMessage]{
			Type:      sessionLinkRemoteRequestResultType,
			RequestID: assignment.RequestID,
			Success:   true,
			Outcome:   "ok",
			Payload:   responsePayload,
		})
		if err == nil {
			err = handleSessionLinkRemoteRequestResultCommand(hub, context.Background(), client, result)
		}
		assignmentCh <- assignmentResult{endpointID: assignment.EndpointID, agentID: payload.AgentID, err: err}
	}()

	ctx, cancel := context.WithTimeout(t.Context(), time.Second)
	defer cancel()
	status, available, err := hub.GetAgentMemorySyncStatus(ctx, client.machineID, "agt_1")
	require.NoError(t, err)
	require.True(t, available)
	require.Equal(t, "pending_sync", status.State)
	require.Equal(t, "push_failed", status.Reason)
	require.Equal(t, retryAt, status.RetryAt)
	assignment := <-assignmentCh
	require.NoError(t, assignment.err)
	require.Equal(t, remoteEndpointRuntimeMemorySyncStatus, assignment.endpointID)
	require.Equal(t, "agt_1", assignment.agentID)
}

func TestAgentMemorySyncStatusDoesNotFallBackForOlderDaemon(t *testing.T) {
	client := newElasticReadyClient()
	delete(client.supportedEndpointVersions, remoteEndpointRuntimeMemorySyncStatus)
	now := time.Date(2026, time.August, 21, 9, 0, 0, 0, time.UTC)
	hub := &Hub{
		remoteEndpoints:     defaultRemoteEndpointRegistry(),
		elasticRequestLinks: newTestElasticRequestLinkManager(t, &now, ElasticRequestLinkManagerConfig{}),
		clients:             map[string]*clientState{client.machineID: client},
	}

	_, _, err := hub.GetAgentMemorySyncStatus(t.Context(), client.machineID, "agt_1")
	require.True(t, errors.Is(err, ErrMemorySyncEndpointUnsupported), "got %v", err)
}
