package tech.easyflow.admin.service.agent;

import org.mockito.Mockito;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import tech.easyflow.agent.runtime.AgentRuntimeStateCleanupService;
import tech.easyflow.agent.runtime.composer.AgentComposerDraftService;
import tech.easyflow.agent.runtime.media.AgentMediaService;
import tech.easyflow.agent.service.AgentService;
import tech.easyflow.ai.service.DocumentCollectionService;
import tech.easyflow.chatlog.domain.dto.ChatSessionSummary;
import tech.easyflow.chatlog.service.ChatSessionCommandService;
import tech.easyflow.chatlog.service.ChatSessionQueryService;
import tech.easyflow.chatlog.support.ChatJsonSupport;
import tech.easyflow.common.entity.LoginAccount;
import tech.easyflow.common.web.exceptions.BusinessException;
import tech.easyflow.system.service.ResourceAccessService;

import java.math.BigInteger;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

/**
 * {@link AgentSessionService} 会话删除测试。
 */
public class AgentSessionServiceTest {

    private static final BigInteger ACCOUNT_ID = BigInteger.valueOf(7);
    private static final BigInteger SESSION_ID = BigInteger.valueOf(101);

    private ChatSessionQueryService chatSessionQueryService;
    private ChatSessionCommandService chatSessionCommandService;
    private AgentRuntimeStateCleanupService agentRuntimeStateCleanupService;
    private AgentMediaService agentMediaService;
    private AgentComposerDraftService agentComposerDraftService;
    private AgentSessionService service;
    private LoginAccount account;

    /**
     * 初始化测试依赖。
     */
    @BeforeMethod
    public void setUp() {
        chatSessionQueryService = mock(ChatSessionQueryService.class);
        chatSessionCommandService = mock(ChatSessionCommandService.class);
        agentRuntimeStateCleanupService = mock(AgentRuntimeStateCleanupService.class);
        agentMediaService = mock(AgentMediaService.class);
        agentComposerDraftService = mock(AgentComposerDraftService.class);
        service = new AgentSessionService(
                chatSessionQueryService,
                chatSessionCommandService,
                mock(AgentService.class),
                mock(DocumentCollectionService.class),
                mock(ResourceAccessService.class),
                agentRuntimeStateCleanupService,
                agentMediaService,
                agentComposerDraftService,
                mock(ChatJsonSupport.class)
        );
        account = new LoginAccount();
        account.setId(ACCOUNT_ID);
    }

    /**
     * 验证正常删除会清理运行态、写入删除命令并删除媒体目录。
     */
    @Test
    public void shouldDeleteActiveOwnedAgentSession() {
        when(chatSessionQueryService.getSessionSummary(SESSION_ID))
                .thenReturn(buildSession(ACCOUNT_ID, 0, "AGENT"));

        service.deleteCurrentUserSession(account, SESSION_ID);

        verify(agentRuntimeStateCleanupService).clearChatSession(SESSION_ID, ACCOUNT_ID);
        verify(chatSessionCommandService).deleteSession(SESSION_ID, ACCOUNT_ID, ACCOUNT_ID);
        verify(agentComposerDraftService).delete(AgentMediaService.MODE_FORMAL, "9", "101", account);
        verify(agentMediaService).deleteFormalSession(SESSION_ID.toString(), account);
    }

    /**
     * 验证会话已删除时重复请求仍会重试媒体目录清理并成功返回。
     */
    @Test
    public void shouldRetryMediaCleanupForDeletedSession() {
        when(chatSessionQueryService.getSessionSummary(SESSION_ID))
                .thenReturn(buildSession(ACCOUNT_ID, 1, "AGENT"));

        service.deleteCurrentUserSession(account, SESSION_ID);

        verify(agentRuntimeStateCleanupService, never()).clearChatSession(Mockito.any(), Mockito.any());
        verify(chatSessionCommandService, never()).deleteSession(Mockito.any(), Mockito.any(), Mockito.any());
        verify(agentComposerDraftService).delete(AgentMediaService.MODE_FORMAL, "9", "101", account);
        verify(agentMediaService).deleteFormalSession(SESSION_ID.toString(), account);
    }

    /**
     * 验证查询不到会话时删除保持幂等，并按当前用户目录重试媒体清理。
     */
    @Test
    public void shouldRetryMediaCleanupWhenSessionIsMissing() {
        when(chatSessionQueryService.getSessionSummary(SESSION_ID)).thenReturn(null);

        service.deleteCurrentUserSession(account, SESSION_ID);

        verify(agentRuntimeStateCleanupService, never()).clearChatSession(Mockito.any(), Mockito.any());
        verify(chatSessionCommandService, never()).deleteSession(Mockito.any(), Mockito.any(), Mockito.any());
        verify(agentComposerDraftService, never()).delete(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any());
        verify(agentMediaService).deleteFormalSession(SESSION_ID.toString(), account);
    }

    /**
     * 验证活动会话属于其他用户时仍拒绝删除，且不执行任何清理。
     */
    @Test
    public void shouldRejectActiveSessionOwnedByAnotherUser() {
        when(chatSessionQueryService.getSessionSummary(SESSION_ID))
                .thenReturn(buildSession(BigInteger.valueOf(8), 0, "AGENT"));

        BusinessException exception = Assert.expectThrows(
                BusinessException.class,
                () -> service.deleteCurrentUserSession(account, SESSION_ID)
        );

        Assert.assertEquals(exception.getMessage(), "无权访问该 Agent 会话");
        verify(agentRuntimeStateCleanupService, never()).clearChatSession(Mockito.any(), Mockito.any());
        verify(chatSessionCommandService, never()).deleteSession(Mockito.any(), Mockito.any(), Mockito.any());
        verify(agentComposerDraftService, never()).delete(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any());
        verify(agentMediaService, never()).deleteFormalSession(Mockito.any(), Mockito.any());
    }

    /**
     * 构造会话摘要。
     *
     * @param userId 用户 ID
     * @param isDeleted 删除标记
     * @param assistantCode 助手类型
     * @return 会话摘要
     */
    private ChatSessionSummary buildSession(BigInteger userId, Integer isDeleted, String assistantCode) {
        ChatSessionSummary summary = new ChatSessionSummary();
        summary.setId(SESSION_ID);
        summary.setUserId(userId);
        summary.setIsDeleted(isDeleted);
        summary.setAssistantCode(assistantCode);
        summary.setAssistantId(BigInteger.valueOf(9));
        return summary;
    }
}
