package tech.easyflow.chatlog.service;

import org.junit.Assert;
import org.junit.Test;
import org.mockito.InOrder;
import org.mockito.Mockito;
import tech.easyflow.chatlog.cache.ChatHotStateService;
import tech.easyflow.chatlog.domain.event.ChatPersistEvent;
import tech.easyflow.chatlog.support.ChatJsonSupport;
import tech.easyflow.common.web.exceptions.BusinessException;

import java.math.BigInteger;
import java.util.List;

/**
 * {@link ChatPersistDispatcher} 会话删除可靠持久化顺序测试。
 */
public class ChatPersistDispatcherTest {

    /**
     * 验证 MySQL 同步删除失败时不发送异步事件。
     */
    @Test
    public void deleteShouldStopBeforeProducerWhenMysqlApplyFails() {
        Fixture fixture = fixture();
        Mockito.doThrow(new IllegalStateException("mysql unavailable"))
                .when(fixture.applyService).apply(Mockito.anyList());

        Assert.assertThrows(BusinessException.class, () -> fixture.dispatcher.deleteSession(
                BigInteger.ONE, BigInteger.TWO, BigInteger.TWO));

        Mockito.verify(fixture.eventProducer, Mockito.never()).send(Mockito.any());
    }

    /**
     * 验证消息发送失败发生在 MySQL 同步删除成功之后并继续向上抛出。
     */
    @Test
    public void deleteShouldPersistBeforePropagatingProducerFailure() {
        Fixture fixture = fixture();
        Mockito.doThrow(new IllegalStateException("mq unavailable"))
                .when(fixture.eventProducer).send(Mockito.any());

        Assert.assertThrows(IllegalStateException.class, () -> fixture.dispatcher.deleteSession(
                BigInteger.ONE, BigInteger.TWO, BigInteger.TWO));

        InOrder order = Mockito.inOrder(fixture.applyService, fixture.eventProducer);
        order.verify(fixture.applyService).apply(Mockito.<List<ChatPersistEvent>>any());
        order.verify(fixture.eventProducer).send(Mockito.any());
    }

    private Fixture fixture() {
        ChatHotStateService hotStateService = Mockito.mock(ChatHotStateService.class);
        ChatPersistEventProducer eventProducer = Mockito.mock(ChatPersistEventProducer.class);
        ChatPersistMySqlApplyService applyService = Mockito.mock(ChatPersistMySqlApplyService.class);
        ChatJsonSupport jsonSupport = Mockito.mock(ChatJsonSupport.class);
        Mockito.when(jsonSupport.toJson(Mockito.any())).thenReturn("{}");
        return new Fixture(eventProducer, applyService,
                new ChatPersistDispatcher(hotStateService, eventProducer, applyService, jsonSupport));
    }

    private record Fixture(ChatPersistEventProducer eventProducer,
                           ChatPersistMySqlApplyService applyService,
                           ChatPersistDispatcher dispatcher) {
    }
}
