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

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
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.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.math.BigDecimal;
import org.junit.jupiter.api.DisplayName;
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 com.ruoyi.common.exception.ServiceException;
import com.ruoyi.web.modules.booking.domain.AppBookingSubmitBody;
import com.ruoyi.web.modules.booking.domain.vo.AppointmentResourceVo;
import com.ruoyi.web.modules.booking.mapper.AppAppointmentResourceMapper;
import com.ruoyi.web.modules.booking.support.AppointmentResourceRules;
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.mapper.BizServiceAppointmentReviewMapper;

@ExtendWith(MockitoExtension.class)
@DisplayName("AppBookingServiceImpl")
class AppBookingServiceImplTest
{
    @Mock
    private AppAppointmentResourceMapper appAppointmentResourceMapper;

    @Mock
    private BizServiceAppointmentMapper bizServiceAppointmentMapper;

    @Mock
    private BizServiceAppointmentReviewMapper bizServiceAppointmentReviewMapper;

    @InjectMocks
    private AppBookingServiceImpl service;

    @Test
    @DisplayName("当日未预约")
    void notBookedOnDate()
    {
        when(bizServiceAppointmentMapper.countMyActiveAppointmentOnDate(1, 1L, "2026-05-20", "u1")).thenReturn(0);
        assertFalse(service.checkBookedOnDate(1, 1L, "2026-05-20", "u1").isBooked());
    }

    @Test
    @DisplayName("当日已预约")
    void bookedOnDate()
    {
        when(bizServiceAppointmentMapper.countMyActiveAppointmentOnDate(3, 10L, "2026-05-22", "u1")).thenReturn(1);
        assertTrue(service.checkBookedOnDate(3, 10L, "2026-05-22", "u1").isBooked());
    }

    @Test
    @DisplayName("提交兽医预约为待确认")
    void submitVetPending()
    {
        AppointmentResourceVo resource = publishedVet();
        when(appAppointmentResourceMapper.selectAppointmentResourceByIdAndType(1L, AppointmentResourceRules.TYPE_VET))
                .thenReturn(resource);
        when(bizServiceAppointmentMapper.insertBizServiceAppointment(any())).thenAnswer(inv -> {
            BizServiceAppointment row = inv.getArgument(0);
            row.setId(100L);
            return 1;
        });

        AppBookingSubmitBody body = new AppBookingSubmitBody();
        body.setResourceType(AppointmentResourceRules.TYPE_VET);
        body.setResourceId(1L);
        body.setAppointDate("2026-05-20");
        body.setAppointeeName("才让");
        body.setContactPhone("13800138001");
        body.setTimeSlot("09:00~11:00");
        body.setServiceAddress("服务地址");

        assertEquals(100L, service.submitAppointment(body, "herder"));

        ArgumentCaptor<BizServiceAppointment> cap = ArgumentCaptor.forClass(BizServiceAppointment.class);
        verify(bizServiceAppointmentMapper).insertBizServiceAppointment(cap.capture());
        assertEquals(ServiceAppointmentRules.STATUS_PENDING, cap.getValue().getStatus());
        assertEquals(ServiceAppointmentRules.PROVIDER_TYPE_VET, cap.getValue().getProviderType());
    }

    @Test
    @DisplayName("提交机构预约为已预约")
    void submitOrgBooked()
    {
        AppointmentResourceVo resource = publishedOrg();
        when(appAppointmentResourceMapper.selectAppointmentResourceByIdAndType(2L, AppointmentResourceRules.TYPE_ORG))
                .thenReturn(resource);
        when(bizServiceAppointmentMapper.countActiveAppointmentsOnDate(eq(2), eq(2L), eq("2026-05-21")))
                .thenReturn(0);
        when(bizServiceAppointmentMapper.insertBizServiceAppointment(any())).thenAnswer(inv -> {
            BizServiceAppointment row = inv.getArgument(0);
            row.setId(101L);
            return 1;
        });

        AppBookingSubmitBody body = new AppBookingSubmitBody();
        body.setResourceType(AppointmentResourceRules.TYPE_ORG);
        body.setResourceId(2L);
        body.setAppointDate("2026-05-21");
        body.setAppointeeName("用户");
        body.setContactPhone("13900139000");
        body.setTimeStart("10:00");
        body.setTimeEnd("12:00");
        body.setServiceAddress("机构地址");

        service.submitAppointment(body, "u1");
        ArgumentCaptor<BizServiceAppointment> cap = ArgumentCaptor.forClass(BizServiceAppointment.class);
        verify(bizServiceAppointmentMapper).insertBizServiceAppointment(cap.capture());
        assertEquals(ServiceAppointmentRules.STATUS_BOOKED, cap.getValue().getStatus());
    }

    @Test
    @DisplayName("机构当日已满")
    void orgFull()
    {
        AppointmentResourceVo resource = publishedOrg();
        when(appAppointmentResourceMapper.selectAppointmentResourceByIdAndType(2L, AppointmentResourceRules.TYPE_ORG))
                .thenReturn(resource);
        when(bizServiceAppointmentMapper.countActiveAppointmentsOnDate(eq(2), eq(2L), eq("2026-05-21")))
                .thenReturn(5);

        AppBookingSubmitBody body = new AppBookingSubmitBody();
        body.setResourceType(AppointmentResourceRules.TYPE_ORG);
        body.setResourceId(2L);
        body.setAppointDate("2026-05-21");
        body.setAppointeeName("用户");
        body.setContactPhone("13900139000");
        body.setTimeSlot("10:00~12:00");
        body.setServiceAddress("地址");

        assertThrows(ServiceException.class, () -> service.submitAppointment(body, "u1"));
    }

    private static AppointmentResourceVo publishedVet()
    {
        AppointmentResourceVo vo = new AppointmentResourceVo();
        vo.setId(1L);
        vo.setResourceType(AppointmentResourceRules.TYPE_VET);
        vo.setServiceWeekdays("1,2,3,4,5");
        return vo;
    }

    private static AppointmentResourceVo publishedOrg()
    {
        AppointmentResourceVo vo = new AppointmentResourceVo();
        vo.setId(2L);
        vo.setResourceType(AppointmentResourceRules.TYPE_ORG);
        vo.setServiceWeekdays("1,2,3,4,5,6,7");
        vo.setMaxDailyAppointments(5);
        return vo;
    }
}
