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

import com.infoepoch.pms.agent.domain.care.model.ActivityMatchUserRule;
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.observability.ai.AiModelInvoker;
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.provider.CareBusinessDataProvider;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.ai.chat.model.ChatModel;

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

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

@ExtendWith(MockitoExtension.class)
class CareQueryUnderstandingServiceTest {

    @Mock
    private ChatModel chatModel;

    @Mock
    private CareBusinessDataProvider careBusinessDataProvider;

    private CareQueryUnderstandingService service;

    @BeforeEach
    void setUp() {
        CareAgentProperties properties = new CareAgentProperties();
        properties.setDefaultCount(10);
        properties.setMaxRequestedCount(500);
        AiObservationSupport support = new AiObservationSupport(new AiObservabilityProperties(), "pms-agent");
        AiModelInvoker invoker = new AiModelInvoker(new AiObservationLogger(support), support);
        service = new CareQueryUnderstandingService(chatModel, properties, careBusinessDataProvider, invoker, support);
    }

    @Test
    void shouldUseGeneralChatForUnsupportedHistoryQuestion() {
        when(chatModel.call(anyString())).thenReturn("GENERAL_CHAT");

        CareQuery query = service.understand("trace-1", "session-1", "活动历史有哪些", null);

        assertEquals(CareTaskType.GENERAL_CHAT, query.taskType());
        assertEquals("", query.directReply());
        verify(chatModel, times(1)).call(anyString());
    }

    @Test
    void shouldKeepContinuationStateWithoutCallingModel() {
        CareConversationState state = new CareConversationState(
                "session-1",
                CareTaskType.RECOMMEND_USERS,
                25,
                "健步走活动",
                "推荐更多适合健步走的用户",
                Map.of("regionName", "浦东新区"),
                2,
                Set.of("u-1"),
                LocalDateTime.now()
        );

        CareQuery query = service.understand("trace-1", "session-1", "继续", state);

        assertEquals(CareTaskType.RECOMMEND_USERS, query.taskType());
        assertTrue(query.continuation());
        assertEquals("健步走活动", query.activityIntent());
        assertEquals(Map.of("regionName", "浦东新区"), query.searchConditions());
        verify(chatModel, never()).call(anyString());
    }

    @Test
    void shouldFallbackToGeneralChatWhenClassificationModelThrows() {
        when(chatModel.call(anyString())).thenThrow(new RuntimeException("model error"));

        CareQuery query = service.understand("trace-1", "session-1", "帮我看看活动历史", null);

        assertEquals(CareTaskType.GENERAL_CHAT, query.taskType());
        assertEquals("", query.directReply());
        verify(chatModel, times(1)).call(anyString());
    }

    @Test
    void shouldFallbackToGeneralChatWhenClassificationModelReturnsUnknownValue() {
        when(chatModel.call(anyString())).thenReturn("SOMETHING_ELSE");

        CareQuery query = service.understand("trace-1", "session-1", "你好，你是谁", null);

        assertEquals(CareTaskType.GENERAL_CHAT, query.taskType());
        assertEquals("", query.directReply());
        verify(chatModel, times(1)).call(anyString());
    }

    @Test
    void shouldNotTreatAgeAsRequestedCount() {
        when(chatModel.call(anyString())).thenReturn("GENERAL_CHAT");

        CareQuery query = service.understand("trace-1", "session-1", "给65岁老人推荐活动", null);

        assertEquals(10, query.requestedCount());
        assertEquals(CareTaskType.GENERAL_CHAT, query.taskType());
        verify(chatModel, times(1)).call(anyString());
    }

    @Test
    void shouldNotTreatMonthAsRequestedCount() {
        when(chatModel.call(anyString())).thenReturn("GENERAL_CHAT");

        CareQuery query = service.understand("trace-1", "session-1", "推荐3月的活动", null);

        assertEquals(10, query.requestedCount());
        assertEquals(CareTaskType.GENERAL_CHAT, query.taskType());
        verify(chatModel, times(1)).call(anyString());
    }

    @Test
    void shouldNotTreatBmiAsRequestedCount() {
        when(chatModel.call(anyString())).thenReturn("RECOMMEND_USERS")
                .thenReturn("""
                        {
                          "taskType": "RECOMMEND_USERS",
                          "activityIntent": "健康讲座",
                          "preferenceSummary": "BMI 24 的用户",
                          "searchConditions": {
                            "bmi": "24"
                          }
                        }
                        """);

        CareQuery query = service.understand("trace-1", "session-1", "推荐 BMI 24 的用户", null);

        assertEquals(10, query.requestedCount());
        assertEquals(CareTaskType.RECOMMEND_USERS, query.taskType());
        assertEquals("24", query.searchConditions().get("bmi"));
        verify(chatModel, times(2)).call(anyString());
    }

    @Test
    void shouldExtractRequestedCountOnlyFromExplicitCountExpression() {
        when(chatModel.call(anyString())).thenReturn("GENERAL_CHAT");

        CareQuery query = service.understand("trace-1", "session-1", "推荐5个活动", null);

        assertEquals(5, query.requestedCount());
        assertEquals(CareTaskType.GENERAL_CHAT, query.taskType());
        verify(chatModel, times(1)).call(anyString());
    }

    @Test
    void shouldReclassifyContinuationWhenMessageContainsNewIntent() {
        CareConversationState state = new CareConversationState(
                "session-1",
                CareTaskType.RECOMMEND_ACTIVITIES,
                10,
                "广场舞活动",
                "推荐适合当前用户的活动",
                Map.of("departmentName", "老年大学"),
                1,
                Set.of("a-1"),
                LocalDateTime.now()
        );
        when(chatModel.call(anyString()))
                .thenReturn("RECOMMEND_USERS")
                .thenReturn("""
                        {
                          "taskType": "RECOMMEND_USERS",
                          "activityIntent": "健步走",
                          "preferenceSummary": "继续推荐适合健步走的用户",
                          "searchConditions": {
                            "regionName": "浦东新区"
                          }
                        }
                        """);

        CareQuery query = service.understand("trace-1", "session-1", "继续推荐适合健步走的用户", state);

        assertEquals(CareTaskType.RECOMMEND_USERS, query.taskType());
        assertTrue(query.continuation());
        assertEquals("健步走", query.activityIntent());
        assertEquals("浦东新区", query.searchConditions().get("regionName"));
        assertEquals(1, query.searchConditions().size());
        verify(chatModel, times(2)).call(anyString());
    }

    @Test
    void shouldFallbackWhenUnderstandingModelThrows() {
        when(chatModel.call(anyString()))
                .thenReturn("RECOMMEND_USERS")
                .thenThrow(new RuntimeException("timeout"));

        CareQuery query = service.understand("trace-1", "session-1", "推荐参加健步走活动的用户", null);

        assertEquals(CareTaskType.RECOMMEND_USERS, query.taskType());
        assertEquals("推荐参加健步走活动的用户", query.activityIntent());
        assertEquals("推荐参加健步走活动的用户", query.preferenceSummary());
        assertTrue(query.searchConditions().isEmpty());
        verify(chatModel, times(2)).call(anyString());
    }

    @Test
    void shouldUseModelForRecommendUsersAndParseUnderstandingResult() {
        when(chatModel.call(anyString()))
                .thenReturn("RECOMMEND_USERS")
                .thenReturn("""
                        {
                          "taskType": "RECOMMEND_USERS",
                          "activityIntent": "健步走活动",
                          "preferenceSummary": "推荐喜欢运动的老年用户",
                          "searchConditions": {
                            "regionName": "浦东新区",
                            "sex": "女",
                            "hobby": ["健步走", "太极"]
                          }
                        }
                        """);

        CareQuery query = service.understand("trace-1", "session-1", "推荐参加健步走活动的用户", null);

        assertEquals(CareTaskType.RECOMMEND_USERS, query.taskType());
        assertEquals("健步走活动", query.activityIntent());
        assertEquals("推荐喜欢运动的老年用户", query.preferenceSummary());
        assertEquals("浦东新区", query.searchConditions().get("regionName"));
        assertEquals("1", query.searchConditions().get("sex"));
        assertEquals("健步走,太极", query.searchConditions().get("hobby"));
        verify(chatModel, times(2)).call(anyString());
    }

    @Test
    void shouldCompleteRecommendUserConditionsFromActivityRules() {
        when(chatModel.call(anyString()))
                .thenReturn("RECOMMEND_USERS")
                .thenReturn("""
                        {
                          "taskType": "RECOMMEND_USERS",
                          "activityIntent": "健步走活动",
                          "preferenceSummary": "推荐适合健步走活动的用户",
                          "searchConditions": {
                            "regionName": "浦东新区"
                          }
                        }
                        """);
        when(careBusinessDataProvider.listActivityMatchUserRules("trace-1", "session-1"))
                .thenReturn(List.of(new ActivityMatchUserRule(
                        "健步走",
                        "",
                        55,
                        75,
                        "偏高",
                        "跑步,徒步",
                        ""
                )));

        CareQuery query = service.understand("trace-1", "session-1", "健步走活动，推荐一些用户", null);

        assertEquals("浦东新区", query.searchConditions().get("regionName"));
        assertEquals(55, query.searchConditions().get("minAge"));
        assertEquals(75, query.searchConditions().get("maxAge"));
        assertEquals("偏高", query.searchConditions().get("bmi"));
        assertEquals("跑步,徒步", query.searchConditions().get("hobby"));
    }

    @Test
    void shouldPreferExplicitConditionsOverActivityRules() {
        when(chatModel.call(anyString()))
                .thenReturn("RECOMMEND_USERS")
                .thenReturn("""
                        {
                          "taskType": "RECOMMEND_USERS",
                          "activityIntent": "健步走活动",
                          "preferenceSummary": "推荐喜欢太极的用户",
                          "searchConditions": {
                            "hobby": "太极,徒步",
                            "bmi": "超重",
                            "sex": "女"
                          }
                        }
                        """);
        when(careBusinessDataProvider.listActivityMatchUserRules("trace-1", "session-1"))
                .thenReturn(List.of(new ActivityMatchUserRule(
                        "健步走",
                        "0",
                        55,
                        75,
                        "偏高",
                        "跑步,徒步",
                        "组织活动"
                )));

        CareQuery query = service.understand("trace-1", "session-1", "健步走活动，推荐一些用户", null);

        assertEquals("1", query.searchConditions().get("sex"));
        assertEquals("太极,徒步,跑步", query.searchConditions().get("hobby"));
        assertEquals(55, query.searchConditions().get("minAge"));
        assertEquals(75, query.searchConditions().get("maxAge"));
        assertEquals("超重,偏高", query.searchConditions().get("bmi"));
        assertEquals("组织活动", query.searchConditions().get("specialty"));
    }

    @Test
    void shouldFallbackToModelConditionsWhenRuleLookupFails() {
        when(chatModel.call(anyString()))
                .thenReturn("RECOMMEND_USERS")
                .thenReturn("""
                        {
                          "taskType": "RECOMMEND_USERS",
                          "activityIntent": "健步走活动",
                          "preferenceSummary": "推荐适合健步走的用户",
                          "searchConditions": {
                            "regionName": "浦东新区"
                          }
                        }
                        """);
        when(careBusinessDataProvider.listActivityMatchUserRules("trace-1", "session-1"))
                .thenThrow(new RuntimeException("tool error"));

        CareQuery query = service.understand("trace-1", "session-1", "健步走活动，推荐一些用户", null);

        assertEquals(Map.of("regionName", "浦东新区"), query.searchConditions());
    }
}
