package com.ruoyi.web.modules.content.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.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
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.content.constant.MerchantEntryAgreementConstants;
import com.ruoyi.web.modules.content.domain.BizMerchantEntryAgreement;
import com.ruoyi.web.modules.content.mapper.BizMerchantEntryAgreementMapper;
import com.ruoyi.web.modules.content.service.impl.MerchantEntryAgreementServiceImpl;
import com.ruoyi.web.modules.content.vo.MerchantEntryAgreementAppVO;
import com.ruoyi.web.modules.content.vo.MerchantEntryAgreementConfigVO;

/**
 * 入驻协议 Service 单元测试（MEA-UT）
 */
@ExtendWith(MockitoExtension.class)
class MerchantEntryAgreementServiceImplTest
{
    @Mock
    private BizMerchantEntryAgreementMapper agreementMapper;

    @InjectMocks
    private MerchantEntryAgreementServiceImpl agreementService;

    @Test
    void getConfig_noRow_returnsDefaultShell()
    {
        when(agreementMapper.selectById(MerchantEntryAgreementConstants.CONFIG_ID)).thenReturn(null);

        MerchantEntryAgreementConfigVO vo = agreementService.getConfig();

        assertEquals(MerchantEntryAgreementConstants.CONFIG_ID, vo.getConfigId());
        assertEquals(MerchantEntryAgreementConstants.ENABLE_NO, vo.getEnableFlag());
    }

    @Test
    void save_emptyTitle_rejected()
    {
        BizMerchantEntryAgreement row = baseRow();
        row.setAgreementTitle("  ");

        ServiceException ex = assertThrows(ServiceException.class,
                () -> agreementService.saveConfig(row, "admin"));
        assertEquals(MerchantEntryAgreementConstants.MSG_TITLE_REQUIRED, ex.getMessage());
    }

    @Test
    void save_enabledWithoutContent_rejected()
    {
        BizMerchantEntryAgreement row = baseRow();
        row.setContent("");

        ServiceException ex = assertThrows(ServiceException.class,
                () -> agreementService.saveConfig(row, "admin"));
        assertEquals(MerchantEntryAgreementConstants.MSG_CONTENT_REQUIRED_WHEN_ENABLED, ex.getMessage());
    }

    @Test
    void save_enabledBlankHtml_rejected()
    {
        BizMerchantEntryAgreement row = baseRow();
        row.setContent("<p></p>");

        ServiceException ex = assertThrows(ServiceException.class,
                () -> agreementService.saveConfig(row, "admin"));
        assertEquals(MerchantEntryAgreementConstants.MSG_CONTENT_BLANK, ex.getMessage());
    }

    @Test
    void save_disabledEmptyContent_ok()
    {
        BizMerchantEntryAgreement row = baseRow();
        row.setEnableFlag(MerchantEntryAgreementConstants.ENABLE_NO);
        row.setContent("");
        when(agreementMapper.selectById(MerchantEntryAgreementConstants.CONFIG_ID)).thenReturn(null);
        when(agreementMapper.insert(any())).thenReturn(1);

        agreementService.saveConfig(row, "admin");

        verify(agreementMapper).insert(any());
        verify(agreementMapper, never()).update(any());
    }

    @Test
    void save_invalidEnableFlag_rejected()
    {
        BizMerchantEntryAgreement row = baseRow();
        row.setEnableFlag("2");

        assertThrows(ServiceException.class, () -> agreementService.saveConfig(row, "admin"));
    }

    @Test
    void save_firstInsert_configIdOne()
    {
        BizMerchantEntryAgreement row = baseRow();
        when(agreementMapper.selectById(MerchantEntryAgreementConstants.CONFIG_ID)).thenReturn(null);
        when(agreementMapper.insert(any())).thenReturn(1);

        agreementService.saveConfig(row, "admin");

        verify(agreementMapper).insert(any());
    }

    @Test
    void save_existing_updates()
    {
        BizMerchantEntryAgreement row = baseRow();
        when(agreementMapper.selectById(MerchantEntryAgreementConstants.CONFIG_ID)).thenReturn(baseRow());
        when(agreementMapper.update(any())).thenReturn(1);

        agreementService.saveConfig(row, "admin");

        verify(agreementMapper).update(any());
        verify(agreementMapper, never()).insert(any());
    }

    @Test
    void isEnabled_trueWhenEnabledAndContent()
    {
        when(agreementMapper.selectById(MerchantEntryAgreementConstants.CONFIG_ID)).thenReturn(baseRow());

        assertTrue(agreementService.isEnabled());
    }

    @Test
    void isEnabled_falseWhenDisabled()
    {
        BizMerchantEntryAgreement row = baseRow();
        row.setEnableFlag(MerchantEntryAgreementConstants.ENABLE_NO);
        when(agreementMapper.selectById(MerchantEntryAgreementConstants.CONFIG_ID)).thenReturn(row);

        assertFalse(agreementService.isEnabled());
    }

    @Test
    void isEnabled_falseWhenEnabledButBlankHtml()
    {
        BizMerchantEntryAgreement row = baseRow();
        row.setContent("<p></p>");
        when(agreementMapper.selectById(MerchantEntryAgreementConstants.CONFIG_ID)).thenReturn(row);

        assertFalse(agreementService.isEnabled());
    }

    @Test
    void getForApp_disabled_noContent()
    {
        BizMerchantEntryAgreement row = baseRow();
        row.setEnableFlag(MerchantEntryAgreementConstants.ENABLE_NO);
        when(agreementMapper.selectById(MerchantEntryAgreementConstants.CONFIG_ID)).thenReturn(row);

        MerchantEntryAgreementAppVO vo = agreementService.getForApp();

        assertFalse(vo.isEnabled());
        assertEquals(MerchantEntryAgreementConstants.MSG_ENTRY_CLOSED, vo.getMessage());
    }

    @Test
    void getForApp_enabled_withCheckboxLabel()
    {
        when(agreementMapper.selectById(MerchantEntryAgreementConstants.CONFIG_ID)).thenReturn(baseRow());

        MerchantEntryAgreementAppVO vo = agreementService.getForApp();

        assertTrue(vo.isEnabled());
        assertEquals("我已阅读并同意《农资商城商家入驻协议》（v1.0）", vo.getCheckboxLabel());
    }

    @Test
    void buildCheckboxLabel_withoutVersion()
    {
        String label = agreementService.buildCheckboxLabel("入驻协议", null);
        assertEquals("我已阅读并同意《入驻协议》", label);
    }

    @Test
    void assertAccepted_notAccepted_rejected()
    {
        when(agreementMapper.selectById(MerchantEntryAgreementConstants.CONFIG_ID)).thenReturn(baseRow());

        ServiceException ex = assertThrows(ServiceException.class,
                () -> agreementService.assertAccepted(false));
        assertEquals(MerchantEntryAgreementConstants.MSG_AGREE_REQUIRED, ex.getMessage());
    }

    @Test
    void assertAccepted_whenClosed_rejected()
    {
        BizMerchantEntryAgreement row = baseRow();
        row.setEnableFlag(MerchantEntryAgreementConstants.ENABLE_NO);
        when(agreementMapper.selectById(MerchantEntryAgreementConstants.CONFIG_ID)).thenReturn(row);

        ServiceException ex = assertThrows(ServiceException.class,
                () -> agreementService.assertAccepted(true));
        assertEquals(MerchantEntryAgreementConstants.MSG_ENTRY_CLOSED, ex.getMessage());
    }

    @Test
    void assertAccepted_ok()
    {
        when(agreementMapper.selectById(MerchantEntryAgreementConstants.CONFIG_ID)).thenReturn(baseRow());

        agreementService.assertAccepted(true);
    }

    @Test
    void getEntryStatus_matchesEnabled()
    {
        when(agreementMapper.selectById(MerchantEntryAgreementConstants.CONFIG_ID)).thenReturn(baseRow());

        assertTrue(agreementService.getEntryStatus().isEntryOpen());
    }

    private static BizMerchantEntryAgreement baseRow()
    {
        BizMerchantEntryAgreement row = new BizMerchantEntryAgreement();
        row.setConfigId(MerchantEntryAgreementConstants.CONFIG_ID);
        row.setAgreementTitle("农资商城商家入驻协议");
        row.setVersionLabel("v1.0");
        row.setContent("<p>正文</p>");
        row.setEnableFlag(MerchantEntryAgreementConstants.ENABLE_YES);
        return row;
    }
}
