package com.gxwebsoft.house.ai;

import com.alibaba.fastjson.JSONArray;
import com.gxwebsoft.house.entity.HouseAiChatRequest;
import com.gxwebsoft.house.entity.HouseAiChatResponse;
import com.gxwebsoft.house.entity.HouseInfo;
import com.gxwebsoft.house.param.HouseInfoParam;
import com.gxwebsoft.house.service.HouseInfoService;
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 org.springframework.test.util.ReflectionTestUtils;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;

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

@ExtendWith(MockitoExtension.class)
class HouseAiAgentServiceTest {

    @Mock
    private HouseAiModelClient modelClient;
    @Mock
    private HouseInfoService houseInfoService;

    private HouseAiAgentService agentService;

    @BeforeEach
    void setUp() {
        HouseAiSearchEngine searchEngine = new HouseAiSearchEngine();
        ReflectionTestUtils.setField(searchEngine, "houseInfoService", houseInfoService);
        agentService = new HouseAiAgentService();
        ReflectionTestUtils.setField(agentService, "modelClient", modelClient);
        ReflectionTestUtils.setField(agentService, "conversationMemory", new HouseAiConversationMemory());
        ReflectionTestUtils.setField(agentService, "searchEngine", searchEngine);
        ReflectionTestUtils.setField(agentService, "recommendationExplainer", new HouseAiRecommendationExplainer());
        ReflectionTestUtils.setField(agentService, "houseInfoService", houseInfoService);
    }

    @Test
    void searchUsesParsedConditionsAndKeepsTenantScope() {
        when(modelClient.complete(any(JSONArray.class))).thenReturn(
            "{\"action\":\"search\",\"intent\":{\"tradeType\":\"rent\",\"cityKeyword\":\"南宁\","
                + "\"regionKeyword\":\"青秀区\",\"monthlyRentMax\":3000}}"
        );
        when(houseInfoService.listRel(any(HouseInfoParam.class))).thenReturn(Collections.singletonList(house(1, 2800)));

        HouseAiChatResponse response = agentService.answer(request("帮我在南宁青秀区租房"));

        assertEquals(HouseAiMatchTypes.EXACT, response.getMatchType());
        assertEquals(1, response.getHouses().size());
        assertFalse(response.getShowContactForm());
        ArgumentCaptor<HouseInfoParam> captor = ArgumentCaptor.forClass(HouseInfoParam.class);
        verify(houseInfoService).listRel(captor.capture());
        assertEquals(Integer.valueOf(2001), captor.getValue().getTenantId());
        assertEquals(0, captor.getValue().getStatus());
    }

    @Test
    void searchDefaultsToNanningWhenCityIsOmitted() {
        when(modelClient.complete(any(JSONArray.class))).thenReturn("{\"action\":\"search\",\"intent\":{}}");
        when(houseInfoService.listRel(any(HouseInfoParam.class))).thenReturn(Collections.singletonList(house(1, 2800)));

        agentService.answer(request("帮我找房"));

        ArgumentCaptor<HouseInfoParam> captor = ArgumentCaptor.forClass(HouseInfoParam.class);
        verify(houseInfoService).listRel(captor.capture());
        assertEquals("南宁", captor.getValue().getCity());
    }

    @Test
    void noCandidateShowsLeadEntryAndKeepsStructuredDemandSummary() {
        when(modelClient.complete(any(JSONArray.class))).thenReturn(
            "{\"action\":\"search\",\"intent\":{\"tradeType\":\"rent\",\"monthlyRentMax\":3000,"
                + "\"parkingAvailable\":true,\"requiredFields\":[\"parkingAvailable\"]}}"
        );
        when(houseInfoService.listRel(any(HouseInfoParam.class))).thenReturn(Collections.emptyList());

        HouseAiChatRequest request = request("南宁青秀区租房，要必须停车");
        HouseAiChatResponse response = agentService.answer(request);

        assertEquals(HouseAiMatchTypes.NONE, response.getMatchType());
        assertTrue(response.getShowContactForm());
        String summary = agentService.buildLeadSummary(request);
        assertTrue(summary.contains("类型：rent"));
        assertTrue(summary.contains("城市：南宁"));
        assertTrue(summary.contains("停车：需要"));
    }

    @Test
    void propertyQuestionOnlyUsesCurrentCandidateAndVerifiedDetail() {
        HouseInfo currentHouse = house(1, 2800);
        when(modelClient.complete(any(JSONArray.class)))
            .thenReturn("{\"action\":\"search\",\"intent\":{\"monthlyRentMax\":3000}}")
            .thenReturn("{\"action\":\"property_question\",\"houseId\":1}")
            .thenReturn("该房源月租为 2800 元，停车信息未提供。");
        when(houseInfoService.listRel(any(HouseInfoParam.class)))
            .thenReturn(Collections.singletonList(currentHouse));

        agentService.answer(request("南宁租房，预算 3000"));
        HouseAiChatResponse response = agentService.answer(request("这套房可以停车吗"));

        assertEquals("house", response.getSource());
        assertEquals("该房源月租为 2800 元，停车信息未提供。", response.getAnswer());
        assertFalse(response.getShowContactForm());
        verify(houseInfoService, times(2)).listRel(any(HouseInfoParam.class));
    }

    @Test
    void ambiguousPropertyQuestionDoesNotGuessCandidate() {
        when(modelClient.complete(any(JSONArray.class)))
            .thenReturn("{\"action\":\"search\",\"intent\":{\"monthlyRentMax\":3000}}")
            .thenReturn("{\"action\":\"property_question\"}");
        when(houseInfoService.listRel(any(HouseInfoParam.class))).thenReturn(Arrays.asList(house(1, 2800), house(2, 2900)));

        agentService.answer(request("南宁租房，预算 3000"));
        HouseAiChatResponse response = agentService.answer(request("这个房源有停车位吗"));

        assertTrue(response.getAnswer().contains("房源标题或序号"));
        verify(houseInfoService, times(1)).listRel(any(HouseInfoParam.class));
    }

    @Test
    void transientModelFailureRetriesOnceBeforeReturningBoundaryAnswer() {
        when(modelClient.complete(any(JSONArray.class)))
            .thenThrow(new IllegalStateException("临时失败"))
            .thenReturn("{\"action\":\"out_of_scope\"}");

        HouseAiChatResponse response = agentService.answer(request("今天天气怎么样"));

        assertTrue(response.getAnswer().contains("只协助找房"));
        verify(modelClient, times(2)).complete(any(JSONArray.class));
    }

    private HouseAiChatRequest request(String question) {
        HouseAiChatRequest request = new HouseAiChatRequest();
        request.setUserId(1001);
        request.setTenantId(2001);
        request.setConversationId("conversation-1");
        request.setQuestion(question);
        return request;
    }

    private HouseInfo house(int id, int monthlyRent) {
        HouseInfo house = new HouseInfo();
        house.setHouseId(id);
        house.setHouseTitle("青秀区精装两房" + id);
        house.setCity("南宁");
        house.setRegion("青秀区");
        house.setExtent("90");
        house.setHouseType("两室一厅");
        house.setMonthlyRent(new BigDecimal(monthlyRent));
        house.setStatus(0);
        return house;
    }
}
