package com.ruoyi.web.modules.technology.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.util.Calendar;
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 com.ruoyi.common.exception.ServiceException;
import com.ruoyi.web.modules.technology.PracticalTrainingTestSamples;
import com.ruoyi.web.modules.technology.domain.BizPracticalTraining;
import com.ruoyi.web.modules.technology.domain.BizPracticalTrainingMember;
import com.ruoyi.web.modules.technology.domain.PracticalTrainingMyEnrollDetailVo;
import com.ruoyi.web.modules.technology.mapper.BizPracticalTrainingMapper;
import com.ruoyi.web.modules.technology.mapper.BizPracticalTrainingMemberMapper;
import com.ruoyi.web.modules.technology.support.PracticalTrainingRules;
import com.ruoyi.web.modules.technology.support.PracticalTrainingStatusSupport;

@ExtendWith(MockitoExtension.class)
@DisplayName("BizPracticalTrainingAppServiceImpl")
class BizPracticalTrainingAppServiceImplTest
{
    @Mock
    private BizPracticalTrainingMapper bizPracticalTrainingMapper;

    @Mock
    private BizPracticalTrainingMemberMapper bizPracticalTrainingMemberMapper;

    @Mock
    private PracticalTrainingStatusSupport practicalTrainingStatusSupport;

    @InjectMocks
    private BizPracticalTrainingAppServiceImpl service;

    @Test
    @DisplayName("报名中可报名并回写人数")
    void enrollSuccess()
    {
        BizPracticalTraining row = enrollingPublished();
        row.setId(1L);
        when(bizPracticalTrainingMapper.selectPublishedBizPracticalTrainingById(1L)).thenReturn(row);
        when(bizPracticalTrainingMemberMapper.selectByTrainingIdAndUserId(1L, 9L)).thenReturn(null);
        when(bizPracticalTrainingMemberMapper.countActiveByTrainingId(1L)).thenReturn(0, 1);
        when(bizPracticalTrainingMemberMapper.insertBizPracticalTrainingMember(any())).thenReturn(1);

        assertEquals(1, service.enroll(1L, 9L, "牧民甲", "herder"));
        verify(practicalTrainingStatusSupport).refreshCountAndPersist(eq(row), eq(1), any());
    }

    @Test
    @DisplayName("非报名中不可报名")
    void enrollWhenNotEnrolling()
    {
        BizPracticalTraining row = enrollingPublished();
        row.setId(2L);
        row.setTrainingStatus(PracticalTrainingRules.TRAINING_STATUS_FULL);
        when(bizPracticalTrainingMapper.selectPublishedBizPracticalTrainingById(2L)).thenReturn(row);
        assertThrows(ServiceException.class, () -> service.enroll(2L, 9L, "牧民甲", "herder"));
    }

    @Test
    @DisplayName("已报名可取消")
    void cancelEnrollSuccess()
    {
        BizPracticalTraining row = enrollingPublished();
        row.setId(3L);
        BizPracticalTrainingMember member = new BizPracticalTrainingMember();
        member.setId(100L);
        member.setEnrollStatus(PracticalTrainingRules.MEMBER_ENROLL_ACTIVE);
        when(bizPracticalTrainingMapper.selectPublishedBizPracticalTrainingById(3L)).thenReturn(row);
        when(bizPracticalTrainingMemberMapper.selectByTrainingIdAndUserId(3L, 9L)).thenReturn(member);
        when(bizPracticalTrainingMemberMapper.countActiveByTrainingId(3L)).thenReturn(0);
        when(bizPracticalTrainingMemberMapper.updateMemberEnrollStatus(any())).thenReturn(1);

        assertEquals(1, service.cancelEnroll(3L, 9L, "herder"));
        verify(practicalTrainingStatusSupport).refreshCountAndPersist(eq(row), eq(0), any());
    }

    @Test
    @DisplayName("已截止不可取消报名")
    void cancelEnrollWhenClosed()
    {
        BizPracticalTraining row = enrollingPublished();
        row.setId(4L);
        row.setTrainingStatus(PracticalTrainingRules.TRAINING_STATUS_CLOSED);
        BizPracticalTrainingMember member = new BizPracticalTrainingMember();
        member.setEnrollStatus(PracticalTrainingRules.MEMBER_ENROLL_ACTIVE);
        when(bizPracticalTrainingMemberMapper.selectByTrainingIdAndUserId(4L, 9L)).thenReturn(member);
        when(bizPracticalTrainingMapper.selectPublishedBizPracticalTrainingById(4L)).thenReturn(row);

        ServiceException ex = assertThrows(ServiceException.class, () -> service.cancelEnroll(4L, 9L, "herder"));
        assertTrue(ex.getMessage().contains("已截止"));
    }

    @Test
    @DisplayName("我的报名详情含 canCancel")
    void myEnrollDetailCanCancel()
    {
        PracticalTrainingMyEnrollDetailVo vo = new PracticalTrainingMyEnrollDetailVo();
        vo.setId(5L);
        BizPracticalTraining row = enrollingPublished();
        row.setId(5L);
        when(bizPracticalTrainingMemberMapper.selectMyActiveEnrollDetail(5L, 9L)).thenReturn(vo);
        when(bizPracticalTrainingMapper.selectPublishedBizPracticalTrainingById(5L)).thenReturn(row);

        PracticalTrainingMyEnrollDetailVo result = service.selectMyEnrollDetail(5L, 9L);
        assertEquals(PracticalTrainingRules.TRAINING_STATUS_ENROLLING, result.getTrainingStatus());
        assertTrue(result.getCanCancel());
    }

    @Test
    @DisplayName("已截止详情不可取消")
    void myEnrollDetailCannotCancelWhenClosed()
    {
        PracticalTrainingMyEnrollDetailVo vo = new PracticalTrainingMyEnrollDetailVo();
        vo.setId(6L);
        BizPracticalTraining row = enrollingPublished();
        row.setId(6L);
        row.setTrainingStatus(PracticalTrainingRules.TRAINING_STATUS_CLOSED);
        when(bizPracticalTrainingMemberMapper.selectMyActiveEnrollDetail(6L, 9L)).thenReturn(vo);
        when(bizPracticalTrainingMapper.selectPublishedBizPracticalTrainingById(6L)).thenReturn(row);

        PracticalTrainingMyEnrollDetailVo result = service.selectMyEnrollDetail(6L, 9L);
        assertFalse(result.getCanCancel());
    }

    private static BizPracticalTraining enrollingPublished()
    {
        BizPracticalTraining row = PracticalTrainingTestSamples.sampleDraft();
        row.setPublishStatus(PracticalTrainingRules.PUBLISH_STATUS_PUBLISHED);
        Calendar cal = Calendar.getInstance();
        cal.add(Calendar.HOUR, -1);
        row.setRegistrationStartTime(cal.getTime());
        cal.add(Calendar.DAY_OF_MONTH, 7);
        row.setRegistrationEndTime(cal.getTime());
        cal.add(Calendar.DAY_OF_MONTH, 7);
        row.setTrainingTime(cal.getTime());
        row.setActualEnrolledCount(0);
        row.setPlannedHeadCount(20);
        row.setTrainingStatus(PracticalTrainingRules.TRAINING_STATUS_ENROLLING);
        return row;
    }
}
