package tech.easyflow.chatlog.service.impl;

import org.junit.Assert;
import org.junit.Test;
import tech.easyflow.chatlog.domain.command.ChatRoundSelectCommand;
import tech.easyflow.chatlog.domain.command.ChatRoundUpsertCommand;
import tech.easyflow.chatlog.domain.dto.ChatMessageRecord;
import tech.easyflow.chatlog.domain.dto.ChatRoundRecord;
import tech.easyflow.chatlog.service.ChatRoundCommandService;
import tech.easyflow.chatlog.service.ChatRoundQueryService;
import tech.easyflow.chatlog.support.ChatConstants;
import tech.easyflow.common.web.exceptions.BusinessException;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

/**
 * {@link ChatRoundOperateServiceImpl} 单元测试。
 */
public class ChatRoundOperateServiceImplTest {

    /**
     * 切换答案版本时应精准查询目标版本，避免先加载全部版本再过滤。
     */
    @Test
    public void selectVariantShouldReadTargetVariantDirectly() {
        FakeRoundQueryService queryService = new FakeRoundQueryService();
        queryService.round = round(BigInteger.valueOf(1001), BigInteger.valueOf(2001), 2, ChatConstants.ROUND_STATUS_READY);
        queryService.latestRound = queryService.round;
        queryService.targetVariant = message(BigInteger.valueOf(3002), 2);
        FakeRoundCommandService commandService = new FakeRoundCommandService();
        ChatRoundOperateServiceImpl service = new ChatRoundOperateServiceImpl(queryService, commandService);

        ChatMessageRecord selected = service.selectVariant(
                BigInteger.valueOf(1001),
                BigInteger.valueOf(2001),
                2,
                BigInteger.valueOf(7)
        );

        Assert.assertEquals(BigInteger.valueOf(3002), selected.getId());
        Assert.assertEquals(Integer.valueOf(2), selected.getSelectedVariantIndex());
        Assert.assertEquals(Integer.valueOf(2), selected.getVariantCount());
        Assert.assertEquals(Boolean.TRUE, selected.getSwitchable());
        Assert.assertEquals(1, queryService.getRoundVariantCalls);
        Assert.assertEquals(0, queryService.listRoundVariantsCalls);
        Assert.assertNotNull(commandService.selectedCommand);
        Assert.assertEquals(BigInteger.valueOf(3002), commandService.selectedCommand.getSelectedAssistantMessageId());
    }

    /**
     * 列出答案版本时应由业务层统一补齐当前选中态和可切换状态。
     */
    @Test
    public void listVariantsShouldFillVariantMetadata() {
        FakeRoundQueryService queryService = new FakeRoundQueryService();
        queryService.round = round(BigInteger.valueOf(1001), BigInteger.valueOf(2001), 2, ChatConstants.ROUND_STATUS_READY);
        queryService.latestRound = queryService.round;
        queryService.variants = List.of(message(BigInteger.valueOf(3001), 1), message(BigInteger.valueOf(3002), 2));
        ChatRoundOperateServiceImpl service = new ChatRoundOperateServiceImpl(queryService, new FakeRoundCommandService());

        List<ChatMessageRecord> variants = service.listVariants(BigInteger.valueOf(1001), BigInteger.valueOf(2001));

        Assert.assertEquals(2, variants.size());
        for (ChatMessageRecord variant : variants) {
            Assert.assertEquals(Integer.valueOf(2), variant.getVariantCount());
            Assert.assertEquals(Integer.valueOf(2), variant.getSelectedVariantIndex());
            Assert.assertEquals(Boolean.TRUE, variant.getSwitchable());
        }
    }

    /**
     * 已锁定轮次禁止切换，避免改变已有后续上下文。
     */
    @Test(expected = BusinessException.class)
    public void selectVariantShouldRejectLockedRound() {
        FakeRoundQueryService queryService = new FakeRoundQueryService();
        queryService.round = round(BigInteger.valueOf(1001), BigInteger.valueOf(2001), 2, ChatConstants.ROUND_STATUS_LOCKED);
        queryService.latestRound = queryService.round;
        ChatRoundOperateServiceImpl service = new ChatRoundOperateServiceImpl(queryService, new FakeRoundCommandService());

        service.selectVariant(BigInteger.valueOf(1001), BigInteger.valueOf(2001), 1, BigInteger.valueOf(7));
    }

    /**
     * 新增迁移必须为热表版本切换查询补齐索引。
     *
     * @throws Exception 读取迁移文件失败时抛出
     */
    @Test
    public void migrationShouldCreateRoundVariantIndex() throws Exception {
        String sql = Files.readString(
                resolveMigrationPath("V18__mysql_chat_round_variant_index.sql"),
                StandardCharsets.UTF_8
        );

        Assert.assertTrue(sql.contains("idx_chat_log_round_variant"));
        Assert.assertTrue(sql.contains("`session_id`, `round_id`, `message_kind`, `variant_index`, `created`, `id`"));
        Assert.assertFalse(sql.contains("V16__mysql_chat_round_variant"));
    }

    /**
     * 从当前测试工作目录向上查找迁移文件，兼容根工程与模块工程两种运行方式。
     *
     * @param fileName 迁移文件名
     * @return 迁移文件路径
     * @throws Exception 未找到迁移文件时抛出
     */
    private static Path resolveMigrationPath(String fileName) throws Exception {
        Path current = Path.of("").toAbsolutePath();
        while (current != null) {
            Path candidate = current.resolve(
                    "easyflow-starter/easyflow-starter-all/src/main/resources/db/migration/mysql/" + fileName
            );
            if (Files.exists(candidate)) {
                return candidate;
            }
            current = current.getParent();
        }
        throw new java.nio.file.NoSuchFileException(fileName);
    }

    private static ChatRoundRecord round(BigInteger sessionId, BigInteger roundId, int selectedVariantIndex, String status) {
        ChatRoundRecord round = new ChatRoundRecord();
        round.setId(roundId);
        round.setSessionId(sessionId);
        round.setRoundNo(1);
        round.setSelectedVariantIndex(selectedVariantIndex);
        round.setVariantCount(2);
        round.setStatus(status);
        return round;
    }

    private static ChatMessageRecord message(BigInteger id, int variantIndex) {
        ChatMessageRecord record = new ChatMessageRecord();
        record.setId(id);
        record.setSessionId(BigInteger.valueOf(1001));
        record.setRoundId(BigInteger.valueOf(2001));
        record.setVariantIndex(variantIndex);
        record.setSenderRole("assistant");
        record.setMessageKind(ChatConstants.MESSAGE_KIND_ASSISTANT_VARIANT);
        record.setContentText("答案 " + variantIndex);
        return record;
    }

    /**
     * 轮次读服务测试替身。
     */
    private static final class FakeRoundQueryService implements ChatRoundQueryService {

        private ChatRoundRecord round;
        private ChatRoundRecord latestRound;
        private ChatMessageRecord targetVariant;
        private List<ChatMessageRecord> variants = List.of();
        private int getRoundVariantCalls;
        private int listRoundVariantsCalls;

        @Override
        public ChatRoundRecord getLatestRound(BigInteger sessionId) {
            return latestRound;
        }

        @Override
        public ChatRoundRecord getRound(BigInteger sessionId, BigInteger roundId) {
            return round;
        }

        @Override
        public List<ChatMessageRecord> listRoundVariants(BigInteger sessionId, BigInteger roundId) {
            listRoundVariantsCalls += 1;
            return variants;
        }

        @Override
        public ChatMessageRecord getRoundVariant(BigInteger sessionId, BigInteger roundId, Integer variantIndex) {
            getRoundVariantCalls += 1;
            return targetVariant;
        }

        @Override
        public boolean hasRounds(BigInteger sessionId) {
            return round != null;
        }
    }

    /**
     * 轮次写服务测试替身。
     */
    private static final class FakeRoundCommandService implements ChatRoundCommandService {

        private ChatRoundSelectCommand selectedCommand;

        @Override
        public ChatRoundRecord createOrTouchRound(ChatRoundUpsertCommand command) {
            return null;
        }

        @Override
        public void selectVariant(ChatRoundSelectCommand command) {
            selectedCommand = command;
        }
    }
}
