package com.ruoyi.web.modules.architecture.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.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
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 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.constant.Constants;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.web.modules.architecture.domain.BizBreedingStandard;
import com.ruoyi.web.modules.architecture.mapper.BizBreedingStandardMapper;
import com.ruoyi.web.modules.architecture.support.BreedingStandardTypeValidator;
import com.ruoyi.web.kb.support.KbApiProperties;
import com.ruoyi.web.kb.service.KnowledgeBaseClient;

/**
 * YZ-BZ-UT-013～015：Service 分支（Mapper Mock）
 */
@ExtendWith(MockitoExtension.class)
@DisplayName("BizBreedingStandardServiceImpl")
class BizBreedingStandardServiceImplTest
{

    @Mock
    private BizBreedingStandardMapper mapper;

    @Mock
    private BreedingStandardTypeValidator breedingStandardTypeValidator;

    @Mock
    private KnowledgeBaseClient knowledgeBaseClient;

    @Mock
    private KbApiProperties kbApiProperties;

    @InjectMocks
    private BizBreedingStandardServiceImpl service;

    @Test
    @DisplayName("YZ-BZ-UT-013 修改已发布抛业务异常")
    void ut013_updatePublishedThrows()
    {
        BizBreedingStandard db = baseRow();
        db.setId(9L);
        db.setPublishStatus(1);
        db.setKbSyncStatus(1);
        when(mapper.selectBizBreedingStandardById(9L)).thenReturn(db);

        BizBreedingStandard incoming = baseRow();
        incoming.setId(9L);
        incoming.setStandardName("新的标准名称一二三");

        assertThrows(ServiceException.class, () -> service.updateBizBreedingStandard(incoming));
        verify(mapper, never()).updateBizBreedingStandard(any());
    }

    @Test
    @DisplayName("YZ-BZ-UT-014 同步成功写 kb_doc_id")
    void ut014_syncKbUpdatesState()
    {
        BizBreedingStandard db = baseRow();
        db.setId(3L);
        db.setPublishStatus(0);
        db.setKbSyncStatus(0);
        when(mapper.selectBizBreedingStandardById(3L)).thenReturn(db);
        when(knowledgeBaseClient.uploadFileByUrl(eq("http://localhost/f"), eq("养殖管理标准"), eq("养")))
                .thenReturn("KB-DOC-3");
        when(mapper.updateBizBreedingStandard(any())).thenReturn(1);

        assertEquals(1, service.syncKb(3L));

        verify(mapper).updateBizBreedingStandard(argThat(r ->
                r.getKbSyncStatus() == 1 && "KB-DOC-3".equals(r.getKbDocId())));
    }

    @Test
    @DisplayName("YZ-BZ-UT-015 持久化返回 0 时不视为成功（未对接知识库时无回滚，仅断言返回值）")
    void ut015_syncKbMapperReturnsZero()
    {
        BizBreedingStandard db = baseRow();
        db.setId(4L);
        db.setPublishStatus(0);
        db.setKbSyncStatus(0);
        when(mapper.selectBizBreedingStandardById(4L)).thenReturn(db);
        when(knowledgeBaseClient.uploadFileByUrl(eq("http://localhost/f"), eq("养殖管理标准"), anyString()))
                .thenReturn("KB-DOC-4");
        when(mapper.updateBizBreedingStandard(any())).thenReturn(0);

        assertEquals(0, service.syncKb(4L));
        verify(mapper).updateBizBreedingStandard(any());
    }

    @Test
    @DisplayName("下架：未发布 + 已同步，保留 kb_doc_id")
    void offlineKeepsKbSyncedAndDocId()
    {
        BizBreedingStandard db = baseRow();
        db.setId(20L);
        db.setPublishStatus(1);
        db.setKbSyncStatus(1);
        db.setKbDocId("REAL-KB-DOC");
        when(mapper.selectBizBreedingStandardById(20L)).thenReturn(db);
        when(mapper.updateBizBreedingStandard(any())).thenReturn(1);

        assertEquals(1, service.offline(20L));

        verify(mapper).updateBizBreedingStandard(argThat(r ->
                r.getPublishStatus() == 0
                        && r.getKbSyncStatus() == 1
                        && "REAL-KB-DOC".equals(r.getKbDocId())
                        && r.getOfflineTime() != null));
    }

    @Test
    @DisplayName("删除：非未发布状态拒绝")
    void deleteRejectsNonDraftPublishStatus()
    {
        BizBreedingStandard db = baseRow();
        db.setId(30L);
        db.setPublishStatus(2);
        db.setKbSyncStatus(0);
        when(mapper.selectBizBreedingStandardById(30L)).thenReturn(db);

        assertThrows(ServiceException.class, () -> service.deleteBizBreedingStandardByIds(new Long[] { 30L }));
        verify(mapper, never()).deleteBizBreedingStandardById(anyLong());
    }

    @Test
    @DisplayName("下架：kb_doc_id 为空时补 LOCAL 占位")
    void offlineFillsLocalKbDocIdWhenMissing()
    {
        BizBreedingStandard db = baseRow();
        db.setId(21L);
        db.setPublishStatus(1);
        db.setKbSyncStatus(1);
        db.setKbDocId(null);
        when(mapper.selectBizBreedingStandardById(21L)).thenReturn(db);
        when(mapper.updateBizBreedingStandard(any())).thenReturn(1);

        assertEquals(1, service.offline(21L));

        verify(mapper).updateBizBreedingStandard(argThat(r ->
                r.getPublishStatus() == 0
                        && r.getKbSyncStatus() == 1
                        && "LOCAL-21".equals(r.getKbDocId())));
    }

    @Test
    @DisplayName("移出知识库：调用删除接口并清空本地状态")
    void removeKbCallsClientAndClears()
    {
        BizBreedingStandard db = baseRow();
        db.setId(13L);
        db.setPublishStatus(0);
        db.setKbSyncStatus(1);
        db.setKbDocId("remote-doc-1");
        when(mapper.selectBizBreedingStandardById(13L)).thenReturn(db);
        when(mapper.updateBizBreedingStandard(any())).thenReturn(1);

        assertEquals(1, service.removeKb(13L));

        verify(knowledgeBaseClient).deleteFile("remote-doc-1");
        verify(mapper).updateBizBreedingStandard(argThat(r ->
                r.getKbSyncStatus() == 0 && r.getKbDocId() == null));
    }

    @Test
    @DisplayName("移出知识库：LOCAL- 占位 id 不调远程删除")
    void removeKbSkipsHttpForLocalPlaceholder()
    {
        BizBreedingStandard db = baseRow();
        db.setId(14L);
        db.setPublishStatus(0);
        db.setKbSyncStatus(1);
        db.setKbDocId("LOCAL-14");
        when(mapper.selectBizBreedingStandardById(14L)).thenReturn(db);
        when(mapper.updateBizBreedingStandard(any())).thenReturn(1);

        assertEquals(1, service.removeKb(14L));

        verify(knowledgeBaseClient, never()).deleteFile(anyString());
    }

    @Test
    @DisplayName("非法状态同步应抛异常")
    void syncKbWhenAlreadySyncedThrows()
    {
        BizBreedingStandard db = baseRow();
        db.setId(5L);
        db.setPublishStatus(0);
        db.setKbSyncStatus(1);
        when(mapper.selectBizBreedingStandardById(5L)).thenReturn(db);

        assertThrows(ServiceException.class, () -> service.syncKb(5L));
        verify(mapper, never()).updateBizBreedingStandard(any());
    }

    private static BizBreedingStandard baseRow()
    {
        BizBreedingStandard r = new BizBreedingStandard();
        r.setStandardName("养");
        r.setStandardType("001001");
        r.setIntroduction("");
        r.setContentFileUrl("http://localhost/f");
        r.setContentFilePath(Constants.RESOURCE_PREFIX + "/upload/2026/a.pdf");
        r.setContentFileName("a.pdf");
        return r;
    }
}
