package com.ruoyi.web.modules.message.service;

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.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Collections;
import java.util.Date;
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.entry.constant.MerchantEntryApplyConstants;
import com.ruoyi.web.modules.entry.domain.BizMemberMessage;
import com.ruoyi.web.modules.entry.domain.BizMerchantEntryApply;
import com.ruoyi.web.modules.entry.mapper.BizMemberMessageMapper;
import com.ruoyi.web.modules.entry.mapper.BizMerchantEntryApplyMapper;
import com.ruoyi.web.modules.message.constant.MemberMessageConstants;
import com.ruoyi.web.modules.message.service.impl.MemberMessageAppServiceImpl;
import com.ruoyi.web.modules.message.vo.MemberMessageDetailVO;
import com.ruoyi.web.modules.message.vo.MemberMessageListItemVO;
import com.ruoyi.web.modules.message.vo.MemberMessageUnreadVO;

/**
 * C 端站内消息 Service 单元测试
 */
@ExtendWith(MockitoExtension.class)
class MemberMessageAppServiceImplTest
{
    private static final Long MEMBER_ID = 200L;

    private static final Long MESSAGE_ID = 1L;

    private static final Long APPLY_ID = 50L;

    @Mock
    private BizMemberMessageMapper messageMapper;

    @Mock
    private BizMerchantEntryApplyMapper applyMapper;

    @InjectMocks
    private MemberMessageAppServiceImpl memberMessageAppService;

    @Test
    void getUnreadCount_returnsCount()
    {
        when(messageMapper.countUnreadByMemberId(MEMBER_ID)).thenReturn(3);

        MemberMessageUnreadVO vo = memberMessageAppService.getUnreadCount(MEMBER_ID);

        assertEquals(3, vo.getUnreadCount());
    }

    @Test
    void listMessages_empty()
    {
        when(messageMapper.selectListByMemberId(MEMBER_ID)).thenReturn(Collections.emptyList());

        assertTrue(memberMessageAppService.listMessages(MEMBER_ID).isEmpty());
    }

    @Test
    void listMessages_truncatesSummary()
    {
        String longContent = repeat('a', 100);
        when(messageMapper.selectListByMemberId(MEMBER_ID))
                .thenReturn(Collections.singletonList(message(MESSAGE_ID, longContent, MemberMessageConstants.READ_UNREAD)));

        MemberMessageListItemVO item = memberMessageAppService.listMessages(MEMBER_ID).get(0);

        assertEquals(MemberMessageConstants.SUMMARY_MAX_LENGTH, item.getSummary().length());
        assertTrue(item.getSummary().endsWith("..."));
    }

    @Test
    void getDetailAndMarkRead_marksUnread()
    {
        BizMemberMessage message = message(MESSAGE_ID, "正文", MemberMessageConstants.READ_UNREAD);
        when(messageMapper.selectByIdAndMemberId(MESSAGE_ID, MEMBER_ID)).thenReturn(message);
        when(applyMapper.selectById(APPLY_ID)).thenReturn(new BizMerchantEntryApply());

        MemberMessageDetailVO vo = memberMessageAppService.getDetailAndMarkRead(MEMBER_ID, MESSAGE_ID);

        assertEquals(MemberMessageConstants.READ_READ, vo.getReadFlag());
        assertEquals(MemberMessageConstants.LINK_TYPE_MERCHANT_ENTRY, vo.getLinkType());
        assertTrue(vo.isLinkAvailable());
        verify(messageMapper).updateReadFlag(MESSAGE_ID, MEMBER_ID, MemberMessageConstants.READ_READ);
    }

    @Test
    void getDetailAndMarkRead_idempotentWhenAlreadyRead()
    {
        BizMemberMessage message = message(MESSAGE_ID, "正文", MemberMessageConstants.READ_READ);
        when(messageMapper.selectByIdAndMemberId(MESSAGE_ID, MEMBER_ID)).thenReturn(message);
        when(applyMapper.selectById(APPLY_ID)).thenReturn(new BizMerchantEntryApply());

        MemberMessageDetailVO vo = memberMessageAppService.getDetailAndMarkRead(MEMBER_ID, MESSAGE_ID);

        assertEquals(MemberMessageConstants.READ_READ, vo.getReadFlag());
        verify(messageMapper, never()).updateReadFlag(eq(MESSAGE_ID), eq(MEMBER_ID), eq(MemberMessageConstants.READ_READ));
    }

    @Test
    void getDetailAndMarkRead_notFound()
    {
        when(messageMapper.selectByIdAndMemberId(MESSAGE_ID, MEMBER_ID)).thenReturn(null);

        ServiceException ex = assertThrows(ServiceException.class,
                () -> memberMessageAppService.getDetailAndMarkRead(MEMBER_ID, MESSAGE_ID));

        assertEquals(MemberMessageConstants.MSG_NOT_FOUND, ex.getMessage());
        assertEquals(404, ex.getCode());
    }

    @Test
    void getDetailAndMarkRead_linkUnavailableWhenApplyMissing()
    {
        BizMemberMessage message = message(MESSAGE_ID, "正文", MemberMessageConstants.READ_UNREAD);
        when(messageMapper.selectByIdAndMemberId(MESSAGE_ID, MEMBER_ID)).thenReturn(message);
        when(applyMapper.selectById(APPLY_ID)).thenReturn(null);

        MemberMessageDetailVO vo = memberMessageAppService.getDetailAndMarkRead(MEMBER_ID, MESSAGE_ID);

        assertFalse(vo.isLinkAvailable());
    }

    private static BizMemberMessage message(Long messageId, String content, String readFlag)
    {
        BizMemberMessage message = new BizMemberMessage();
        message.setMessageId(messageId);
        message.setMemberId(MEMBER_ID);
        message.setBizType(MerchantEntryApplyConstants.BIZ_TYPE_ENTRY_AUDIT);
        message.setBizId(APPLY_ID);
        message.setTitle(MerchantEntryApplyConstants.MSG_PUBLICITY_TITLE);
        message.setContent(content);
        message.setReadFlag(readFlag);
        message.setCreateTime(new Date());
        return message;
    }

    private static String repeat(char c, int count)
    {
        StringBuilder sb = new StringBuilder(count);
        for (int i = 0; i < count; i++)
        {
            sb.append(c);
        }
        return sb.toString();
    }
}
