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.BizFeedingStandard;
import com.ruoyi.web.modules.architecture.mapper.BizFeedingStandardMapper;
import com.ruoyi.web.modules.architecture.support.FeedingStandardTypeValidator;
import com.ruoyi.web.kb.support.KbApiProperties;
import com.ruoyi.web.kb.service.KnowledgeBaseClient;

/**
 * WF-BZ-UT-013～020：Service 分支（Mapper Mock）
 */
@ExtendWith(MockitoExtension.class)
@DisplayName("BizFeedingStandardServiceImpl")
class BizFeedingStandardServiceImplTest
{

    @Mock
    private BizFeedingStandardMapper mapper;

    @Mock
    private FeedingStandardTypeValidator feedingStandardTypeValidator;

    @Mock
    private KnowledgeBaseClient knowledgeBaseClient;

    @Mock
    private KbApiProperties kbApiProperties;

    @InjectMocks
    private BizFeedingStandardServiceImpl service;

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

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

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

    @Test
    @DisplayName("WF-BZ-UT-014 同步成功写 kb_doc_id（file_id）")
    void ut014_syncKbUpdatesState()
    {
        BizFeedingStandard db = baseRow();
        db.setId(3L);
        db.setPublishStatus(0);
        db.setKbSyncStatus(0);
        when(mapper.selectBizFeedingStandardById(3L)).thenReturn(db);
        when(kbApiProperties.getFileUrlBase()).thenReturn("");
        when(knowledgeBaseClient.uploadFileByUrl(eq("http://localhost/f"), eq("饲喂标准"), eq("饲")))
                .thenReturn("KB-DOC-3");
        when(mapper.updateBizFeedingStandard(any())).thenReturn(1);

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

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

    @Test
    @DisplayName("WF-BZ-UT-015 同步失败：Mapper 返回 0")
    void ut015_syncKbMapperReturnsZero()
    {
        BizFeedingStandard db = baseRow();
        db.setId(4L);
        db.setPublishStatus(0);
        db.setKbSyncStatus(0);
        when(mapper.selectBizFeedingStandardById(4L)).thenReturn(db);
        when(kbApiProperties.getFileUrlBase()).thenReturn("");
        when(knowledgeBaseClient.uploadFileByUrl(eq("http://localhost/f"), eq("饲喂标准"), anyString()))
                .thenReturn("KB-DOC-4");
        when(mapper.updateBizFeedingStandard(any())).thenReturn(0);

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

    @Test
    @DisplayName("WF-BZ-UT-016 已同步禁止修改")
    void ut016_updateWhenSyncedRejected()
    {
        BizFeedingStandard db = baseRow();
        db.setId(10L);
        db.setPublishStatus(0);
        db.setKbSyncStatus(1);
        db.setKbDocId("LOCAL-10");
        when(mapper.selectBizFeedingStandardById(10L)).thenReturn(db);

        BizFeedingStandard incoming = baseRow();
        incoming.setId(10L);
        incoming.setIntroduction("改简介");
        assertThrows(ServiceException.class, () -> service.updateBizFeedingStandard(incoming));
        verify(mapper, never()).updateBizFeedingStandard(any());
    }

    @Test
    @DisplayName("WF-BZ-UT-017 下架成功：未发布+已同步，保留 kb_doc_id")
    void ut017_offline()
    {
        BizFeedingStandard db = baseRow();
        db.setId(20L);
        db.setPublishStatus(1);
        db.setKbSyncStatus(1);
        db.setKbDocId("REAL-KB-DOC");
        when(mapper.selectBizFeedingStandardById(20L)).thenReturn(db);
        when(mapper.updateBizFeedingStandard(any())).thenReturn(1);

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

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

    @Test
    @DisplayName("WF-BZ-UT-018 移出成功")
    void ut018_removeKb()
    {
        BizFeedingStandard db = baseRow();
        db.setId(13L);
        db.setPublishStatus(0);
        db.setKbSyncStatus(1);
        db.setKbDocId("DOC-13");
        when(mapper.selectBizFeedingStandardById(13L)).thenReturn(db);
        when(mapper.updateBizFeedingStandard(any())).thenReturn(1);

        assertEquals(1, service.removeKb(13L));
        verify(knowledgeBaseClient).deleteFile("DOC-13");
        verify(mapper).updateBizFeedingStandard(argThat(r ->
                r.getKbSyncStatus() == 0 && r.getKbDocId() == null));
    }

    @Test
    @DisplayName("WF-BZ-UT-019 发布成功")
    void ut019_publish()
    {
        BizFeedingStandard db = baseRow();
        db.setId(14L);
        db.setPublishStatus(0);
        db.setKbSyncStatus(1);
        when(mapper.selectBizFeedingStandardById(14L)).thenReturn(db);
        when(mapper.updateBizFeedingStandard(any())).thenReturn(1);

        assertEquals(1, service.publish(14L));
        verify(mapper).updateBizFeedingStandard(argThat(r ->
                r.getPublishStatus() == 1 && r.getPublishTime() != null));
    }

    @Test
    @DisplayName("WF-BZ-UT-020 未同步保存不调外部：仅 update")
    void ut020_updateUnsynced()
    {
        BizFeedingStandard db = baseRow();
        db.setId(11L);
        db.setPublishStatus(0);
        db.setKbSyncStatus(0);
        db.setKbDocId(null);
        when(mapper.selectBizFeedingStandardById(11L)).thenReturn(db);
        when(mapper.updateBizFeedingStandard(any())).thenReturn(1);

        BizFeedingStandard incoming = baseRow();
        incoming.setId(11L);
        incoming.setIntroduction("仅本地");
        assertEquals(1, service.updateBizFeedingStandard(incoming));
        verify(mapper).updateBizFeedingStandard(argThat(r -> r.getKbSyncStatus() == 0));
    }

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

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

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

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

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