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

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
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.domain.BizServiceAppointment;
import com.ruoyi.web.modules.diagnosis.mapper.BizServiceAppointmentMapper;
import com.ruoyi.web.modules.diagnosis.support.ServiceAppointmentRules;
import com.ruoyi.web.modules.farming.ExpertMyAppointmentTestSamples;
import com.ruoyi.web.modules.farming.domain.BizServiceAppointmentReview;
import com.ruoyi.web.modules.farming.mapper.BizServiceAppointmentReviewMapper;
import com.ruoyi.web.modules.farming.support.ExpertProviderResolver;
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("BizExpertMyAppointmentServiceImpl")
class BizExpertMyAppointmentServiceImplTest
{
    @Mock
    private BizServiceAppointmentMapper appointmentMapper;

    @Mock
    private BizServiceAppointmentReviewMapper reviewMapper;

    @Mock
    private ExpertProviderResolver expertProviderResolver;

    @InjectMocks
    private BizExpertMyAppointmentServiceImpl service;

    @BeforeEach
    void bindProvider()
    {
        when(expertProviderResolver.resolveProviderId(ExpertMyAppointmentTestSamples.SYS_USER_ID))
                .thenReturn(ExpertMyAppointmentTestSamples.EXPERT_PROVIDER_ID);
    }

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

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

    @Test
    @DisplayName("确认成功")
    void confirm()
    {
        BizServiceAppointment pending = ExpertMyAppointmentTestSamples.pendingAppointment();
        when(appointmentMapper.selectExpertAppointmentByIdAndProvider(3000L, ExpertMyAppointmentTestSamples.EXPERT_PROVIDER_ID))
                .thenReturn(pending);
        when(appointmentMapper.updateConfirmExpertAppointment(any())).thenReturn(1);
        assertEquals(1, service.confirm(3000L, ExpertMyAppointmentTestSamples.SYS_USER_ID, "expertA"));
    }

    @Test
    @DisplayName("拒绝成功")
    void reject()
    {
        BizServiceAppointment pending = ExpertMyAppointmentTestSamples.pendingAppointment();
        when(appointmentMapper.selectExpertAppointmentByIdAndProvider(3000L, ExpertMyAppointmentTestSamples.EXPERT_PROVIDER_ID))
                .thenReturn(pending);
        when(appointmentMapper.updateRejectExpertAppointment(any())).thenReturn(1);
        assertEquals(1, service.reject(3000L, "  时段冲突  ", ExpertMyAppointmentTestSamples.SYS_USER_ID, "expertA"));
        verify(appointmentMapper).updateRejectExpertAppointment(argThat(p -> "时段冲突".equals(p.getRejectReason())));
    }

    @Test
    @DisplayName("详情已完成含评价")
    void detailWithReview()
    {
        BizServiceAppointment completed = ExpertMyAppointmentTestSamples.completedAppointment();
        when(appointmentMapper.selectExpertAppointmentByIdAndProvider(3004L, ExpertMyAppointmentTestSamples.EXPERT_PROVIDER_ID))
                .thenReturn(completed);
        when(reviewMapper.selectLatestReviewByAppointmentId(3004L))
                .thenReturn(ExpertMyAppointmentTestSamples.sampleReview());
        BizServiceAppointment result = service.selectExpertAppointmentById(3004L, ExpertMyAppointmentTestSamples.SYS_USER_ID);
        assertEquals(5, result.getPunctualityScore());
        assertNotNull(result.getSummary());
    }

    @Test
    @DisplayName("已确认可完成接诊")
    void completeConfirmed()
    {
        BizServiceAppointment confirmed = ExpertMyAppointmentTestSamples.pendingAppointment();
        confirmed.setId(3001L);
        confirmed.setStatus(ServiceAppointmentRules.STATUS_CONFIRMED);
        when(appointmentMapper.selectExpertAppointmentByIdAndProvider(3001L, ExpertMyAppointmentTestSamples.EXPERT_PROVIDER_ID))
                .thenReturn(confirmed);
        when(appointmentMapper.updateCompleteExpertAppointment(any())).thenReturn(1);
        assertEquals(1, service.complete(3001L, ExpertMyAppointmentTestSamples.SYS_USER_ID, "expertA"));
    }

    @Test
    @DisplayName("评价列表仅已完成")
    void reviewListCompleted()
    {
        BizServiceAppointment completed = ExpertMyAppointmentTestSamples.completedAppointment();
        when(appointmentMapper.selectExpertAppointmentByIdAndProvider(3004L, ExpertMyAppointmentTestSamples.EXPERT_PROVIDER_ID))
                .thenReturn(completed);
        when(reviewMapper.selectReviewListByAppointmentId(3004L))
                .thenReturn(Collections.singletonList(ExpertMyAppointmentTestSamples.sampleReview()));
        assertEquals(1, service.selectReviewList(3004L, ExpertMyAppointmentTestSamples.SYS_USER_ID).size());
    }

    @Test
    @DisplayName("待确认不可查评价")
    void reviewListNotCompleted()
    {
        when(appointmentMapper.selectExpertAppointmentByIdAndProvider(3000L, ExpertMyAppointmentTestSamples.EXPERT_PROVIDER_ID))
                .thenReturn(ExpertMyAppointmentTestSamples.pendingAppointment());
        assertThrows(ServiceException.class,
                () -> service.selectReviewList(3000L, ExpertMyAppointmentTestSamples.SYS_USER_ID));
    }

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

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

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

    @Test
    @DisplayName("已完成无评价详情")
    void detailNoReview()
    {
        BizServiceAppointment completed = ExpertMyAppointmentTestSamples.completedAppointment();
        when(appointmentMapper.selectExpertAppointmentByIdAndProvider(3004L, ExpertMyAppointmentTestSamples.EXPERT_PROVIDER_ID))
                .thenReturn(completed);
        when(reviewMapper.selectLatestReviewByAppointmentId(3004L)).thenReturn(null);
        BizServiceAppointment result = service.selectExpertAppointmentById(3004L, ExpertMyAppointmentTestSamples.SYS_USER_ID);
        assertNull(result.getSummary());
    }
}
