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.VetMyAppointmentTestSamples;
import com.ruoyi.web.modules.diagnosis.domain.BizServiceAppointment;
import com.ruoyi.web.modules.diagnosis.mapper.BizServiceAppointmentMapper;
import com.ruoyi.web.modules.diagnosis.support.ServiceAppointmentRules;
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("BizVetMyAppointmentServiceImpl")
class BizVetMyAppointmentServiceImplTest
{
    @Mock
    private BizServiceAppointmentMapper appointmentMapper;

    @Mock
    private VetProviderResolver vetProviderResolver;

    @InjectMocks
    private BizVetMyAppointmentServiceImpl service;

    @BeforeEach
    void bindProvider()
    {
        when(vetProviderResolver.resolveProviderId(VetMyAppointmentTestSamples.SYS_USER_ID))
                .thenReturn(VetMyAppointmentTestSamples.VET_PROVIDER_ID);
    }

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

    @Test
    @DisplayName("列表 scope")
    void listScope()
    {
        when(appointmentMapper.selectVetAppointmentList(any()))
                .thenReturn(Collections.singletonList(VetMyAppointmentTestSamples.pendingAppointment()));
        service.selectVetAppointmentList(new BizServiceAppointment(), VetMyAppointmentTestSamples.SYS_USER_ID);
        verify(appointmentMapper).selectVetAppointmentList(argThat(q ->
                ServiceAppointmentRules.PROVIDER_TYPE_VET == q.getProviderType()
                        && VetMyAppointmentTestSamples.VET_PROVIDER_ID.equals(q.getProviderId())));
    }

    @Test
    @DisplayName("确认成功")
    void confirm()
    {
        BizServiceAppointment pending = VetMyAppointmentTestSamples.pendingAppointment();
        when(appointmentMapper.selectVetAppointmentByIdAndProvider(1000L, VetMyAppointmentTestSamples.VET_PROVIDER_ID))
                .thenReturn(pending);
        when(appointmentMapper.updateConfirmVetAppointment(any())).thenReturn(1);
        assertEquals(1, service.confirm(1000L, VetMyAppointmentTestSamples.SYS_USER_ID, "vetA"));
        verify(appointmentMapper).updateConfirmVetAppointment(argThat(p ->
                p.getConfirmTime() != null && "vetA".equals(p.getUpdateBy())));
    }

    @Test
    @DisplayName("拒绝成功")
    void reject()
    {
        BizServiceAppointment pending = VetMyAppointmentTestSamples.pendingAppointment();
        when(appointmentMapper.selectVetAppointmentByIdAndProvider(1000L, VetMyAppointmentTestSamples.VET_PROVIDER_ID))
                .thenReturn(pending);
        when(appointmentMapper.updateRejectVetAppointment(any())).thenReturn(1);
        assertEquals(1, service.reject(1000L, "  时段冲突  ", VetMyAppointmentTestSamples.SYS_USER_ID, "vetA"));
        verify(appointmentMapper).updateRejectVetAppointment(argThat(p ->
                "时段冲突".equals(p.getRejectReason()) && p.getRejectTime() != null));
    }

    @Test
    @DisplayName("非待确认不可确认")
    void confirmNotPending()
    {
        when(appointmentMapper.selectVetAppointmentByIdAndProvider(1001L, VetMyAppointmentTestSamples.VET_PROVIDER_ID))
                .thenReturn(VetMyAppointmentTestSamples.confirmedAppointment());
        assertThrows(ServiceException.class,
                () -> service.confirm(1001L, VetMyAppointmentTestSamples.SYS_USER_ID, "vetA"));
    }

    @Test
    @DisplayName("已取消不可操作")
    void confirmCancelled()
    {
        when(appointmentMapper.selectVetAppointmentByIdAndProvider(1002L, VetMyAppointmentTestSamples.VET_PROVIDER_ID))
                .thenReturn(VetMyAppointmentTestSamples.cancelledAppointment());
        ServiceException ex = assertThrows(ServiceException.class,
                () -> service.confirm(1002L, VetMyAppointmentTestSamples.SYS_USER_ID, "vetA"));
        assertTrue(ex.getMessage().contains("已取消"));
    }

    @Test
    @DisplayName("越权记录")
    void notOwned()
    {
        when(appointmentMapper.selectVetAppointmentByIdAndProvider(eq(9999L), any())).thenReturn(null);
        assertThrows(ServiceException.class,
                () -> service.selectVetAppointmentById(9999L, VetMyAppointmentTestSamples.SYS_USER_ID));
    }

    @Test
    @DisplayName("未绑定无法操作")
    void unboundWrite()
    {
        when(vetProviderResolver.resolveProviderId(99L)).thenReturn(null);
        assertThrows(ServiceException.class, () -> service.confirm(1L, 99L, "x"));
    }

    @Test
    @DisplayName("拒绝理由为空")
    void rejectEmptyReason()
    {
        assertThrows(ServiceException.class,
                () -> service.reject(1000L, "", VetMyAppointmentTestSamples.SYS_USER_ID, "vetA"));
    }

    @Test
    @DisplayName("已确认可完成接诊")
    void completeConfirmed()
    {
        when(appointmentMapper.selectVetAppointmentByIdAndProvider(1001L, VetMyAppointmentTestSamples.VET_PROVIDER_ID))
                .thenReturn(VetMyAppointmentTestSamples.confirmedAppointment());
        when(appointmentMapper.updateCompleteVetAppointment(any())).thenReturn(1);
        assertEquals(1, service.complete(1001L, VetMyAppointmentTestSamples.SYS_USER_ID, "vetA"));
    }

    @Test
    @DisplayName("待确认不可完成接诊")
    void completePendingBlocked()
    {
        when(appointmentMapper.selectVetAppointmentByIdAndProvider(1000L, VetMyAppointmentTestSamples.VET_PROVIDER_ID))
                .thenReturn(VetMyAppointmentTestSamples.pendingAppointment());
        assertThrows(ServiceException.class,
                () -> service.complete(1000L, VetMyAppointmentTestSamples.SYS_USER_ID, "vetA"));
    }

    @Test
    @DisplayName("并发状态变更")
    void confirmRace()
    {
        BizServiceAppointment pending = VetMyAppointmentTestSamples.pendingAppointment();
        when(appointmentMapper.selectVetAppointmentByIdAndProvider(1000L, VetMyAppointmentTestSamples.VET_PROVIDER_ID))
                .thenReturn(pending)
                .thenReturn(VetMyAppointmentTestSamples.cancelledAppointment());
        when(appointmentMapper.updateConfirmVetAppointment(any())).thenReturn(0);
        assertThrows(ServiceException.class,
                () -> service.confirm(1000L, VetMyAppointmentTestSamples.SYS_USER_ID, "vetA"));
    }
}
