package com.sino.mci.inbound.engine;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.sino.mci.flow.entity.FlowDefinition;
import com.sino.mci.flow.repository.FlowDefinitionMapper;
import com.sino.mci.inbound.entity.MessageEvent;
import com.sino.mci.inbound.intent.IntentResult;
import com.sino.mci.inbound.repository.FlowExecutionTraceMapper;
import com.sino.mci.inbound.repository.MessageEventMapper;
import com.sino.mci.send.dto.SendRequest;
import com.sino.mci.send.dto.SendResponse;
import com.sino.mci.send.service.SendService;
import com.sino.mci.shared.util.JsonUtils;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.Collections;
import java.util.List;
import java.util.Map;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
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 FlowEngineTest {

    @Mock
    private FlowDefinitionMapper flowDefinitionMapper;
    @Mock
    private MessageEventMapper messageEventMapper;
    @Mock
    private IntentRecognitionService intentRecognitionService;
    @Mock
    private WebhookPushService webhookPushService;
    @Mock
    private FlowExecutionTraceMapper flowExecutionTraceMapper;
    @Mock
    private SendService sendService;

    @InjectMocks
    private FlowEngine flowEngine;

    @Test
    void shouldExecuteWebhookPathWhenConditionIsTrue() {
        MessageEvent event = createEvent();
        FlowDefinition flow = createFlowDefinition();
        IntentResult intentResult =
                new IntentResult("create_order", 0.95, Map.of("customer_name", "张三"), List.of("phone"), true);

        when(messageEventMapper.selectById(event.getId())).thenReturn(event);
        when(flowDefinitionMapper.selectList(any(LambdaQueryWrapper.class))).thenReturn(List.of(flow));
        when(intentRecognitionService.recognize(event)).thenReturn(intentResult);

        flowEngine.executeInboundFlow(event.getId());

        verify(webhookPushService, times(1))
                .pushToBusiness(eq("rescue"), eq(event), eq(intentResult), eq("https://order.sino.com/webhook"));
        verify(sendService, never()).send(any(), any(SendRequest.class));
        assertEventMarkedProcessed(event);
    }

    @Test
    void shouldExecuteReplyPathWhenConditionIsFalse() {
        MessageEvent event = createEvent();
        FlowDefinition flow = createFlowDefinition();
        IntentResult intentResult =
                new IntentResult("other", 0.6, Map.of(), List.of(), false);

        when(messageEventMapper.selectById(event.getId())).thenReturn(event);
        when(flowDefinitionMapper.selectList(any(LambdaQueryWrapper.class))).thenReturn(List.of(flow));
        when(intentRecognitionService.recognize(event)).thenReturn(intentResult);
        when(sendService.send(any(), any(SendRequest.class)))
                .thenReturn(new SendResponse("SND001", 1, "accepted"));

        flowEngine.executeInboundFlow(event.getId());

        verify(webhookPushService, never()).pushToBusiness(any(), any(), any(), any());

        ArgumentCaptor<SendRequest> requestCaptor = ArgumentCaptor.forClass(SendRequest.class);
        verify(sendService, times(1)).send(eq("rescue"), requestCaptor.capture());
        SendRequest request = requestCaptor.getValue();
        assertThat(request.getTargetType()).isEqualTo("conversation");
        assertThat(request.getTargetValue()).containsEntry("conversation_id", 100L);
        assertThat(request.getContentType()).isEqualTo("text");
        assertThat(request.getContent()).containsEntry("type", "text")
                .containsEntry("body", "已收到，正在处理");
        assertThat(request.getChannelStrategy()).containsEntry("channel_type", "wecom");

        assertEventMarkedProcessed(event);
    }

    @Test
    void shouldMarkEventProcessedWhenNoPublishedFlow() {
        MessageEvent event = createEvent();

        when(messageEventMapper.selectById(event.getId())).thenReturn(event);
        when(flowDefinitionMapper.selectList(any(LambdaQueryWrapper.class))).thenReturn(Collections.emptyList());

        flowEngine.executeInboundFlow(event.getId());

        verify(webhookPushService, never()).pushToBusiness(any(), any(), any(), any());
        verify(sendService, never()).send(any(), any(SendRequest.class));
        assertEventMarkedProcessed(event);
    }

    private MessageEvent createEvent() {
        MessageEvent event = new MessageEvent();
        event.setId(1L);
        event.setEventId("evt_20260707_001");
        event.setTenantCode("rescue");
        event.setChannelType("wecom");
        event.setConversationId(100L);
        event.setPersonId(200L);
        event.setContent("{\"type\":\"text\",\"body\":\"我要下单\"}");
        event.setBusinessContext("{}");
        event.setIsProcessed(0);
        return event;
    }

    private FlowDefinition createFlowDefinition() {
        FlowDefinition flow = new FlowDefinition();
        flow.setId(10L);
        flow.setTenantCode("rescue");
        flow.setFlowCode("inbound_default");
        flow.setFlowName("默认入站流程");
        flow.setFlowType(1);
        flow.setStatus(1);

        Map<String, Object> definition = Map.of(
                "nodes", List.of(
                        Map.of("id", "n1", "type", "receive", "config", Map.of("channel_type", "wecom")),
                        Map.of("id", "n2", "type", "intent", "config", Map.of("mode", "llm")),
                        Map.of("id", "n3", "type", "condition",
                                "config", Map.of("expression", "intent.intent == 'create_order'")),
                        Map.of("id", "n4", "type", "webhook",
                                "config", Map.of("url", "https://order.sino.com/webhook")),
                        Map.of("id", "n5", "type", "reply",
                                "config", Map.of("content", Map.of("type", "text", "body", "已收到，正在处理")))
                ),
                "edges", List.of(
                        Map.of("source", "n1", "target", "n2"),
                        Map.of("source", "n2", "target", "n3"),
                        Map.of("source", "n3", "target", "n4", "label", "true"),
                        Map.of("source", "n3", "target", "n5", "label", "false")
                )
        );
        flow.setDefinitionJson(JsonUtils.toJson(definition));
        return flow;
    }

    private void assertEventMarkedProcessed(MessageEvent event) {
        ArgumentCaptor<MessageEvent> captor = ArgumentCaptor.forClass(MessageEvent.class);
        verify(messageEventMapper, times(1)).updateById(captor.capture());
        assertThat(captor.getValue().getIsProcessed()).isEqualTo(1);
    }
}
