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.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.util.Collections;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.web.modules.diagnosis.MedicalResourceTestSamples;
import com.ruoyi.web.modules.diagnosis.domain.BizMedicalResource;
import com.ruoyi.web.modules.diagnosis.mapper.BizMedicalResourceMapper;
import com.ruoyi.web.modules.diagnosis.support.MedicalResourceAccountAssigner;
import com.ruoyi.web.modules.diagnosis.support.MedicalResourceReferenceChecker;
import com.ruoyi.web.modules.diagnosis.support.MedicalResourceRules;
import com.ruoyi.web.modules.diagnosis.support.MedicalResourceTypeValidator;
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;

@ExtendWith(MockitoExtension.class)
@DisplayName("BizMedicalResourceServiceImpl")
class BizMedicalResourceServiceImplTest
{
    @Mock
    private BizMedicalResourceMapper mapper;

    @Mock
    private MedicalResourceReferenceChecker referenceChecker;

    @Mock
    private MedicalResourceAccountAssigner accountAssigner;

    @Mock
    private MedicalResourceTypeValidator medicalResourceTypeValidator;

    @Mock
    private com.ruoyi.web.modules.industryservice.support.TownDeptSupport townDeptSupport;

    @InjectMocks
    private BizMedicalResourceServiceImpl service;

    @Test
    @DisplayName("新增落库未发布")
    void insert()
    {
        when(mapper.insertBizMedicalResource(any())).thenReturn(1);
        BizMedicalResource row = MedicalResourceTestSamples.sampleVetDraft();
        assertEquals(1, service.insertBizMedicalResource(row));
        verify(mapper).insertBizMedicalResource(argThat(r ->
                MedicalResourceRules.PUBLISH_STATUS_DRAFT == r.getPublishStatus()
                        && MedicalResourceRules.ACCOUNT_ASSIGNED_NO == r.getAccountAssigned()
                        && r.getOfflineTime() == null));
    }

    @Test
    @DisplayName("已发布不可改")
    void updatePublishedBlocked()
    {
        BizMedicalResource db = MedicalResourceTestSamples.samplePublishedVet();
        when(mapper.selectBizMedicalResourceById(2L)).thenReturn(db);
        BizMedicalResource in = MedicalResourceTestSamples.sampleVetDraft();
        in.setId(2L);
        assertThrows(ServiceException.class, () -> service.updateBizMedicalResource(in));
        verify(mapper, never()).updateBizMedicalResource(any());
    }

    @Test
    @DisplayName("发布")
    void publish()
    {
        BizMedicalResource db = MedicalResourceTestSamples.sampleVetDraft();
        db.setId(1L);
        db.setConsultModes("1,2");
        db.setServiceWeekdays("1,2,3,4,5");
        when(mapper.selectBizMedicalResourceById(1L)).thenReturn(db);
        when(mapper.updateBizMedicalResource(any())).thenReturn(1);
        assertEquals(1, service.publish(1L));
        verify(mapper).updateBizMedicalResource(argThat(r ->
                MedicalResourceRules.PUBLISH_STATUS_PUBLISHED == r.getPublishStatus()
                        && r.getPublishTime() != null
                        && r.getOfflineTime() == null));
    }

    @Test
    @DisplayName("下架保留发布时间")
    void offline()
    {
        BizMedicalResource db = MedicalResourceTestSamples.samplePublishedVet();
        when(mapper.selectBizMedicalResourceById(2L)).thenReturn(db);
        when(mapper.updateBizMedicalResource(any())).thenReturn(1);
        assertEquals(1, service.offline(2L));
        verify(mapper).updateBizMedicalResource(argThat(r ->
                MedicalResourceRules.PUBLISH_STATUS_DRAFT == r.getPublishStatus()
                        && r.getOfflineTime() != null
                        && r.getPublishTime() != null));
    }

    @Test
    @DisplayName("删除有关联拒绝")
    void deleteWithReference()
    {
        BizMedicalResource db = MedicalResourceTestSamples.sampleVetDraft();
        db.setId(5L);
        db.setDelFlag(MedicalResourceRules.DEL_FLAG_NORMAL);
        when(mapper.selectBizMedicalResourceByIdAny(5L)).thenReturn(db);
        when(referenceChecker.hasReference(5L)).thenReturn(true);
        assertThrows(ServiceException.class, () -> service.deleteBizMedicalResourceByIds(new Long[] { 5L }, "tester"));
        verify(mapper, never()).logicDeleteBizMedicalResourceByIds(any(), any());
    }

    @Test
    @DisplayName("删除已分配联动删账号")
    void deleteRemovesLinkedAccount()
    {
        BizMedicalResource db = MedicalResourceTestSamples.sampleVetDraft();
        db.setId(6L);
        db.setDelFlag(MedicalResourceRules.DEL_FLAG_NORMAL);
        db.setAccountAssigned(MedicalResourceRules.ACCOUNT_ASSIGNED_YES);
        db.setSysUserId(88L);
        when(mapper.selectBizMedicalResourceByIdAny(6L)).thenReturn(db);
        when(referenceChecker.hasReference(6L)).thenReturn(false);
        doNothing().when(accountAssigner).deleteLinkedAccount(db);
        when(mapper.logicDeleteBizMedicalResourceByIds(any(), eq("tester"))).thenReturn(1);
        assertEquals(1, service.deleteBizMedicalResourceByIds(new Long[] { 6L }, "tester"));
        verify(accountAssigner).deleteLinkedAccount(db);
        verify(mapper).logicDeleteBizMedicalResourceByIds(new Long[] { 6L }, "tester");
    }

    @Test
    @DisplayName("批量发布")
    void publishByIds()
    {
        BizMedicalResource db1 = MedicalResourceTestSamples.sampleVetDraft();
        db1.setId(1L);
        db1.setConsultModes("1,2");
        db1.setServiceWeekdays("1,2,3,4,5");
        BizMedicalResource db2 = MedicalResourceTestSamples.sampleVetDraft();
        db2.setId(2L);
        db2.setConsultModes("1,2");
        db2.setServiceWeekdays("1,2,3,4,5");
        when(mapper.selectBizMedicalResourceById(1L)).thenReturn(db1);
        when(mapper.selectBizMedicalResourceById(2L)).thenReturn(db2);
        when(mapper.updateBizMedicalResource(any())).thenReturn(1);
        assertEquals(2, service.publishByIds(new Long[] { 1L, 2L }));
        verify(mapper, times(2)).updateBizMedicalResource(any());
    }

    @Test
    @DisplayName("批量下架")
    void offlineByIds()
    {
        BizMedicalResource db1 = MedicalResourceTestSamples.samplePublishedVet();
        db1.setId(1L);
        BizMedicalResource db2 = MedicalResourceTestSamples.samplePublishedVet();
        db2.setId(2L);
        when(mapper.selectBizMedicalResourceById(1L)).thenReturn(db1);
        when(mapper.selectBizMedicalResourceById(2L)).thenReturn(db2);
        when(mapper.updateBizMedicalResource(any())).thenReturn(1);
        assertEquals(2, service.offlineByIds(new Long[] { 1L, 2L }));
        verify(mapper, times(2)).updateBizMedicalResource(any());
    }

    @Test
    @DisplayName("修改兽医且已分配账号时同步用户部门")
    void updateSyncsLinkedUserDept()
    {
        BizMedicalResource db = MedicalResourceTestSamples.sampleVetDraft();
        db.setId(1L);
        db.setAccountAssigned(MedicalResourceRules.ACCOUNT_ASSIGNED_YES);
        db.setSysUserId(88L);
        db.setConsultModes("1,2,3");
        when(mapper.selectBizMedicalResourceById(1L)).thenReturn(db);
        when(mapper.updateBizMedicalResource(any())).thenReturn(1);
        com.ruoyi.common.core.domain.entity.SysDept town = new com.ruoyi.common.core.domain.entity.SysDept();
        town.setDeptId(201L);
        town.setDeptName("雅安镇");
        when(townDeptSupport.requireReportTown(201L)).thenReturn(town);
        doNothing().when(accountAssigner).syncLinkedUserDept(any());

        BizMedicalResource in = MedicalResourceTestSamples.sampleVetDraft();
        in.setId(1L);
        in.setTownDeptId(201L);
        in.setConsultModesList(java.util.Arrays.asList(1, 2, 3));
        assertEquals(1, service.updateBizMedicalResource(in));
        verify(accountAssigner).syncLinkedUserDept(argThat(r ->
                Long.valueOf(201L).equals(r.getTownDeptId())
                        && "雅安镇".equals(r.getTownName())
                        && Long.valueOf(88L).equals(r.getSysUserId())));
    }

    @Test
    @DisplayName("分配账号")
    void assignAccount()
    {
        BizMedicalResource db = MedicalResourceTestSamples.sampleVetDraft();
        db.setId(1L);
        when(mapper.selectBizMedicalResourceById(1L)).thenReturn(db);
        SysUser user = new SysUser();
        user.setUserId(100L);
        user.setUserName("zhangshouyi1");
        when(accountAssigner.createAccount(any(), any())).thenReturn(user);
        when(mapper.updateBizMedicalResource(any())).thenReturn(1);
        assertEquals(1, service.assignAccount(1L, "admin"));
        verify(mapper).updateBizMedicalResource(argThat(r ->
                MedicalResourceRules.ACCOUNT_ASSIGNED_YES == r.getAccountAssigned()
                        && Long.valueOf(100L).equals(r.getSysUserId())));
    }

    @Test
    @DisplayName("批量分配账号跳过已分配")
    void assignAccountByIdsSkipsAssigned()
    {
        BizMedicalResource assigned = MedicalResourceTestSamples.sampleVetDraft();
        assigned.setId(1L);
        assigned.setAccountAssigned(MedicalResourceRules.ACCOUNT_ASSIGNED_YES);
        assigned.setSysUserId(99L);
        BizMedicalResource unassigned = MedicalResourceTestSamples.sampleVetDraft();
        unassigned.setId(2L);
        when(mapper.selectBizMedicalResourceById(1L)).thenReturn(assigned);
        when(mapper.selectBizMedicalResourceById(2L)).thenReturn(unassigned);
        SysUser user = new SysUser();
        user.setUserId(100L);
        user.setUserName("zhangshouyi2");
        when(accountAssigner.createAccount(any(), any())).thenReturn(user);
        when(mapper.updateBizMedicalResource(any())).thenReturn(1);
        assertEquals(1, service.assignAccountByIds(new Long[] { 1L, 2L }, "admin"));
        verify(accountAssigner, times(1)).createAccount(any(), any());
        verify(mapper, times(1)).updateBizMedicalResource(any());
    }

    @Test
    @DisplayName("批量分配账号全部已分配返回0")
    void assignAccountByIdsAllSkipped()
    {
        BizMedicalResource assigned = MedicalResourceTestSamples.sampleVetDraft();
        assigned.setId(1L);
        assigned.setAccountAssigned(MedicalResourceRules.ACCOUNT_ASSIGNED_YES);
        when(mapper.selectBizMedicalResourceById(1L)).thenReturn(assigned);
        assertEquals(0, service.assignAccountByIds(new Long[] { 1L }, "admin"));
        verify(accountAssigner, never()).createAccount(any(), any());
        verify(mapper, never()).updateBizMedicalResource(any());
    }

    @Test
    @DisplayName("乡镇人员列表仅本镇兽医")
    void listAppliesTownScopeForVet()
    {
        when(townDeptSupport.resolveScopedTownDeptId()).thenReturn(200L);
        when(mapper.selectBizMedicalResourceList(any())).thenReturn(Collections.emptyList());
        BizMedicalResource query = new BizMedicalResource();
        query.setResourceType(MedicalResourceRules.RESOURCE_TYPE_VET);
        service.selectBizMedicalResourceList(query);
        verify(mapper).selectBizMedicalResourceList(argThat(q ->
                String.valueOf(q.getParams().get("dataScope")).contains("town_dept_id = 200")));
    }

    @Test
    @DisplayName("非兽医类型不按乡镇收窄")
    void listSkipsTownScopeForNonVet()
    {
        when(townDeptSupport.resolveScopedTownDeptId()).thenReturn(200L);
        when(mapper.selectBizMedicalResourceList(any())).thenReturn(Collections.emptyList());
        BizMedicalResource query = new BizMedicalResource();
        query.setResourceType(MedicalResourceRules.RESOURCE_TYPE_ORG);
        service.selectBizMedicalResourceList(query);
        verify(mapper).selectBizMedicalResourceList(argThat(q ->
                "".equals(q.getParams().get("dataScope"))));
    }
}
