package com.ruoyi.web.modules.diagnosis.service.impl;

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

import java.util.Collections;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.web.modules.diagnosis.VetOnlineConsultTestSamples;
import com.ruoyi.web.modules.diagnosis.domain.BizConsultSession;
import com.ruoyi.web.modules.diagnosis.domain.BizMedicalResource;
import com.ruoyi.web.modules.diagnosis.domain.VetConsultSendBody;
import com.ruoyi.web.modules.diagnosis.mapper.BizConsultMessageMapper;
import com.ruoyi.web.modules.diagnosis.mapper.BizConsultSessionMapper;
import com.ruoyi.web.modules.diagnosis.mapper.BizMedicalResourceMapper;
import com.ruoyi.web.modules.diagnosis.support.ConsultPushService;
import com.ruoyi.web.modules.diagnosis.support.ConsultSessionRules;
import com.ruoyi.web.modules.diagnosis.support.MedicalResourceRules;
import com.ruoyi.web.modules.diagnosis.support.VetProviderResolver;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;

@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
@DisplayName("BizVetOnlineConsultServiceImpl")
class BizVetOnlineConsultServiceImplTest
{
    @Mock
    private BizConsultSessionMapper sessionMapper;

    @Mock
    private BizConsultMessageMapper messageMapper;

    @Mock
    private BizMedicalResourceMapper medicalResourceMapper;

    @Mock
    private VetProviderResolver vetProviderResolver;

    @Mock
    private ConsultPushService consultPushService;

    @InjectMocks
    private BizVetOnlineConsultServiceImpl service;

    @BeforeEach
    void bindProvider()
    {
        when(vetProviderResolver.resolveProviderId(VetOnlineConsultTestSamples.VET_SYS_USER_ID))
                .thenReturn(VetOnlineConsultTestSamples.VET_PROVIDER_ID);
    }

    @Test
    @DisplayName("未绑定返回空列表")
    void listUnbound()
    {
        when(vetProviderResolver.resolveProviderId(99L)).thenReturn(null);
        assertTrue(service.selectVetSessionList(new BizConsultSession(), 99L).isEmpty());
        verify(sessionMapper, never()).selectVetSessionList(any());
    }

    @Test
    @DisplayName("默认列表含 3 个月条件")
    void listDefaultWindow()
    {
        when(sessionMapper.selectVetSessionList(any()))
                .thenReturn(Collections.singletonList(VetOnlineConsultTestSamples.activeSession()));
        service.selectVetSessionList(new BizConsultSession(), VetOnlineConsultTestSamples.VET_SYS_USER_ID);
        verify(sessionMapper).selectVetSessionList(argThat(q ->
                ConsultSessionRules.CONSULT_TYPE_VET == q.getConsultType()
                        && VetOnlineConsultTestSamples.VET_PROVIDER_ID.equals(q.getReceiverProviderId())
                        && q.getThreeMonthsAgo() != null
                        && !Boolean.TRUE.equals(q.getSearchMode())));
    }

    @Test
    @DisplayName("搜索模式无 3 个月限制")
    void listSearchMode()
    {
        BizConsultSession query = new BizConsultSession();
        query.setSearchMode(true);
        query.setContentKeyword("超窗");
        when(sessionMapper.selectVetSessionList(any())).thenReturn(Collections.emptyList());
        service.selectVetSessionList(query, VetOnlineConsultTestSamples.VET_SYS_USER_ID);
        verify(sessionMapper).selectVetSessionList(argThat(q ->
                Boolean.TRUE.equals(q.getSearchMode()) && q.getThreeMonthsAgo() == null));
    }

    @Test
    @DisplayName("兽医发送成功")
    void sendVetMessage()
    {
        when(sessionMapper.selectVetSessionByIdAndProvider(VetOnlineConsultTestSamples.SESSION_ID,
                VetOnlineConsultTestSamples.VET_PROVIDER_ID))
                .thenReturn(VetOnlineConsultTestSamples.activeSession());
        when(messageMapper.insertBizConsultMessage(any())).thenAnswer(inv -> {
            com.ruoyi.web.modules.diagnosis.domain.BizConsultMessage m = inv.getArgument(0);
            m.setId(1L);
            return 1;
        });
        when(sessionMapper.updateSessionLastMessage(any())).thenReturn(1);
        VetConsultSendBody body = new VetConsultSendBody();
        body.setMsgType(ConsultSessionRules.MSG_TYPE_TEXT);
        body.setContent("建议隔离");
        service.sendVetMessage(VetOnlineConsultTestSamples.SESSION_ID, body,
                VetOnlineConsultTestSamples.VET_SYS_USER_ID, "vetA");
        verify(consultPushService).publishNewMessage(any());
    }

    @Test
    @DisplayName("越权会话")
    void sendForbidden()
    {
        when(sessionMapper.selectVetSessionByIdAndProvider(VetOnlineConsultTestSamples.SESSION_ID,
                VetOnlineConsultTestSamples.VET_PROVIDER_ID))
                .thenReturn(null);
        VetConsultSendBody body = new VetConsultSendBody();
        body.setMsgType(ConsultSessionRules.MSG_TYPE_TEXT);
        body.setContent("x");
        assertThrows(ServiceException.class,
                () -> service.sendVetMessage(VetOnlineConsultTestSamples.SESSION_ID, body,
                        VetOnlineConsultTestSamples.VET_SYS_USER_ID, "vetA"));
    }

    @Test
    @DisplayName("hide 会话")
    void hide()
    {
        when(sessionMapper.updateVetVisible(eq(VetOnlineConsultTestSamples.SESSION_ID),
                eq(VetOnlineConsultTestSamples.ASKER_USER_ID), eq(ConsultSessionRules.VET_VISIBLE_NO)))
                .thenReturn(1);
        assertEquals(1, service.hideSessionForVet(VetOnlineConsultTestSamples.SESSION_ID,
                VetOnlineConsultTestSamples.ASKER_USER_ID));
    }

    @Test
    @DisplayName("问诊人拉取消息")
    void askerMessages()
    {
        when(sessionMapper.selectSessionByIdAndAsker(VetOnlineConsultTestSamples.SESSION_ID,
                VetOnlineConsultTestSamples.ASKER_USER_ID))
                .thenReturn(VetOnlineConsultTestSamples.activeSession());
        when(messageMapper.selectMessagesBeforeId(eq(VetOnlineConsultTestSamples.SESSION_ID), eq(null), eq(50)))
                .thenReturn(Collections.singletonList(VetOnlineConsultTestSamples.sampleTextMessage()));
        assertEquals(1, service.selectAskerSessionMessages(VetOnlineConsultTestSamples.SESSION_ID, null, 50,
                VetOnlineConsultTestSamples.ASKER_USER_ID).size());
    }

    @Test
    @DisplayName("open 复用已有会话")
    void openExisting()
    {
        BizConsultSession existing = VetOnlineConsultTestSamples.activeSession();
        when(medicalResourceMapper.selectBizMedicalResourceById(VetOnlineConsultTestSamples.VET_PROVIDER_ID))
                .thenReturn(vetResource());
        when(sessionMapper.selectVetSessionByAskerAndProvider(VetOnlineConsultTestSamples.ASKER_USER_ID,
                VetOnlineConsultTestSamples.VET_PROVIDER_ID))
                .thenReturn(existing);
        BizConsultSession result = service.openOrGetVetSession(VetOnlineConsultTestSamples.VET_PROVIDER_ID,
                VetOnlineConsultTestSamples.ASKER_USER_ID, "扎西");
        assertEquals(existing.getId(), result.getId());
        verify(sessionMapper, never()).insertBizConsultSession(any());
    }

    private static BizMedicalResource vetResource()
    {
        BizMedicalResource r = new BizMedicalResource();
        r.setId(VetOnlineConsultTestSamples.VET_PROVIDER_ID);
        r.setResourceType(MedicalResourceRules.RESOURCE_TYPE_VET);
        r.setResourceName("测试兽医");
        r.setSysUserId(VetOnlineConsultTestSamples.VET_SYS_USER_ID);
        r.setDelFlag(MedicalResourceRules.DEL_FLAG_NORMAL);
        r.setPublishStatus(MedicalResourceRules.PUBLISH_STATUS_PUBLISHED);
        r.setConsultModes("1,3");
        r.setAccountAssigned(MedicalResourceRules.ACCOUNT_ASSIGNED_YES);
        return r;
    }
}
