package com.infoepoch.pms.agent.domain.care.orchestrator;

import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.RunnableConfig;
import com.infoepoch.pms.agent.domain.care.model.ActivitySummary;
import com.infoepoch.pms.agent.domain.care.model.CareConversationState;
import com.infoepoch.pms.agent.domain.care.model.CareQuery;
import com.infoepoch.pms.agent.domain.care.model.CareTaskType;
import com.infoepoch.pms.agent.domain.care.log.CareTraceLogSupport;
import com.infoepoch.pms.agent.domain.care.model.UserProfileSummary;
import com.infoepoch.pms.agent.domain.care.state.CareConversationStateService;
import com.infoepoch.pms.agent.domain.care.stream.CareStreamingResponseAssembler;
import com.infoepoch.pms.agent.observability.ai.AiObservationLogger;
import com.infoepoch.pms.agent.observability.ai.AiObservationSupport;
import com.infoepoch.pms.agent.properties.AiObservabilityProperties;
import com.infoepoch.pms.agent.properties.CareAgentProperties;
import com.infoepoch.pms.agent.tool.union.care.CurrentUserProfileTool;
import com.infoepoch.pms.agent.tool.union.care.EligibleActivitiesTool;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import reactor.core.publisher.Flux;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Set;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class ActivityRecommendationExecutorTest {

    @Mock
    private CurrentUserProfileTool currentUserProfileTool;

    @Mock
    private EligibleActivitiesTool eligibleActivitiesTool;

    @Mock
    private CareConversationStateService conversationStateService;

    @Mock
    private ReactAgent careAgent;

    private ActivityRecommendationExecutor executor;

    @BeforeEach
    void setUp() {
        CareAgentProperties properties = new CareAgentProperties();
        properties.setActivityCandidateLimit(4);
        executor = new ActivityRecommendationExecutor(
                currentUserProfileTool,
                eligibleActivitiesTool,
                conversationStateService,
                new CareStreamingResponseAssembler(),
                properties,
                careAgent,
                new AiObservationSupport(new AiObservabilityProperties(), "pms-agent")
        );
    }

    @Test
    void shouldSaveOnlyDisplayedActivitiesAfterSuccessfulCompletion() throws Exception {
        CareQuery query = new CareQuery(
                CareTaskType.RECOMMEND_ACTIVITIES,
                2,
                false,
                "推荐活动",
                "",
                "推荐活动",
                java.util.Map.of(),
                ""
        );
        CareConversationState state = new CareConversationState(
                "session-1",
                CareTaskType.RECOMMEND_ACTIVITIES,
                2,
                "",
                "推荐活动",
                java.util.Map.of(),
                1,
                Set.of("old-1"),
                LocalDateTime.now()
        );
        when(currentUserProfileTool.execute("trace-1", "session-1"))
                .thenReturn(new UserProfileSummary("u-1", "张三", 68, "男", List.of("健步"), List.of("活跃"), "浦东", ""));
        when(eligibleActivitiesTool.execute("trace-1", "session-1"))
                .thenReturn(List.of(
                        activity("a-1"),
                        activity("a-2"),
                        activity("a-3"),
                        activity("a-4")
                ));
        when(careAgent.stream(anyString(), any(RunnableConfig.class))).thenReturn((Flux) Flux.empty());

        executor.execute("trace-1", "session-1", query, state).collectList().block();

        ArgumentCaptor<CareConversationState> captor = ArgumentCaptor.forClass(CareConversationState.class);
        verify(conversationStateService).save(captor.capture());
        assertEquals(Set.of("old-1", "a-1", "a-2"), captor.getValue().deliveredIds());

        ArgumentCaptor<RunnableConfig> configCaptor = ArgumentCaptor.forClass(RunnableConfig.class);
        verify(careAgent).stream(anyString(), configCaptor.capture());
        RunnableConfig runnableConfig = configCaptor.getValue();
        assertEquals("session-1", runnableConfig.threadId().orElseThrow());
        assertEquals("trace-1", runnableConfig.metadata().orElseThrow().get(CareTraceLogSupport.METADATA_TRACE_ID));
        assertEquals("session-1", runnableConfig.metadata().orElseThrow().get(CareTraceLogSupport.METADATA_SESSION_ID));
        assertEquals("活动推荐", runnableConfig.metadata().orElseThrow().get(CareTraceLogSupport.METADATA_SCENE));
        assertEquals("trace-1", runnableConfig.metadata().orElseThrow().get("requestId"));
        assertEquals("trace-1", runnableConfig.metadata().orElseThrow().get("flowId"));
    }

    @Test
    void shouldNotSaveDeliveredActivitiesWhenStreamingFails() throws Exception {
        CareQuery query = new CareQuery(
                CareTaskType.RECOMMEND_ACTIVITIES,
                2,
                false,
                "推荐活动",
                "",
                "推荐活动",
                java.util.Map.of(),
                ""
        );
        when(currentUserProfileTool.execute("trace-1", "session-1"))
                .thenReturn(new UserProfileSummary("u-1", "张三", 68, "男", List.of(), List.of(), "浦东", ""));
        when(eligibleActivitiesTool.execute("trace-1", "session-1"))
                .thenReturn(List.of(activity("a-1"), activity("a-2"), activity("a-3")));
        when(careAgent.stream(anyString(), any(RunnableConfig.class)))
                .thenReturn((Flux) Flux.error(new RuntimeException("boom")));

        assertThrows(RuntimeException.class,
                () -> executor.execute("trace-1", "session-1", query, null).collectList().block());

        verify(conversationStateService, never()).save(org.mockito.ArgumentMatchers.any());
    }

    private ActivitySummary activity(String id) {
        return new ActivitySummary(id, "活动" + id, "运动", "老人", List.of("户外"), "浦东", "周三", "摘要");
    }
}
