package com.emotion.service;

import com.emotion.dto.request.EpicScriptCreateRequest;
import com.emotion.entity.AiConfig;
import com.emotion.service.impl.EpicScriptServiceImpl;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.stream.IntStream;

import static org.junit.jupiter.api.Assertions.*;

/**
 * EpicScriptServiceImpl测试类
 * 包含属性测试，验证输入组装完整性等
 * 使用贴近真实业务场景的测试数据
 * 
 * Feature: coze-ai-integration
 * 
 * @author system
 * @date 2025-12-23
 */
@SpringBootTest
@ActiveProfiles("local")
public class EpicScriptServiceImplTest {

    @Autowired
    private EpicScriptService epicScriptService;
    
    @Autowired
    private AiConfigService aiConfigService;
    
    private Random random;
    
    /**
     * 爽文剧本生成的配置键
     */
    private static final String EPIC_SCRIPT_CONFIG_KEY = "coze.course.life.generate";
    
    /**
     * 真实的剧本标题示例
     */
    private static final List<String> SAMPLE_TITLES = Arrays.asList(
            "逆袭人生：从底层到巅峰",
            "命运转折点",
            "我的职场逆袭之路",
            "重生之商业帝国",
            "平凡人的不平凡故事",
            "从零开始的创业传奇",
            "人生赢家养成记",
            "绝地反击"
    );
    
    /**
     * 真实的主题/渴望示例
     */
    private static final List<String> SAMPLE_THEMES = Arrays.asList(
            "成为行业领袖，实现财务自由",
            "找到真爱，拥有幸福家庭",
            "突破自我，成就非凡人生",
            "获得认可，证明自己的价值",
            "改变命运，逆转人生轨迹",
            "实现梦想，活出精彩人生"
    );
    
    /**
     * 真实的序幕（低谷回响）示例
     */
    private static final List<String> SAMPLE_PLOT_INTROS = Arrays.asList(
            "我是一个普通的上班族，每天朝九晚五，工资勉强够生活。公司裁员名单上赫然出现了我的名字。",
            "大学毕业后，我满怀憧憬来到大城市，却发现现实远比想象残酷。租住在狭小的地下室，每天为生计发愁。",
            "创业失败后，我背负着巨额债务，朋友疏远，家人失望。站在人生的最低谷，我不知道该何去何从。",
            "三十岁了，事业无成，感情空白。看着同龄人都已成家立业，我感到前所未有的迷茫和焦虑。"
    );
    
    /**
     * 真实的转折（契机出现）示例
     */
    private static final List<String> SAMPLE_PLOT_TURNINGS = Arrays.asList(
            "一次偶然的机会，我遇到了一位行业前辈。他的一番话点醒了我，让我看到了新的可能。",
            "在最绝望的时候，我发现了一个被忽视的市场机会。这可能是改变命运的转折点。",
            "一封意外的邮件，一个久违的电话，让我重新燃起了希望。原来机会一直都在，只是我没有发现。",
            "参加了一场行业峰会，结识了志同道合的伙伴。我们决定一起做点不一样的事情。"
    );
    
    /**
     * 真实的高潮（命运抉择）示例
     */
    private static final List<String> SAMPLE_PLOT_CLIMAXES = Arrays.asList(
            "面对两个选择：稳定但平庸的工作，还是充满风险但可能改变人生的创业机会。我必须做出决定。",
            "关键时刻，曾经的对手提出了合作邀请。是放下过去携手共进，还是坚持己见独自前行？",
            "项目进入最关键的阶段，资金链即将断裂。是放弃还是孤注一掷？这个决定将决定一切。",
            "成功近在咫尺，但代价是牺牲与家人相处的时间。事业与家庭，我该如何抉择？"
    );
    
    /**
     * 真实的结局（新的开始）示例
     */
    private static final List<String> SAMPLE_PLOT_ENDINGS = Arrays.asList(
            "经过不懈努力，我终于实现了自己的目标。站在新的起点，我知道这只是开始，更精彩的人生还在前方。",
            "回首来时路，那些曾经的困难都成为了宝贵的财富。我不仅收获了成功，更收获了成长。",
            "梦想成真的那一刻，我流下了激动的泪水。感谢那个在低谷中没有放弃的自己。",
            "新的篇章已经开启，我带着过去的经验和教训，向着更高的目标前进。人生，永远充满可能。"
    );

    @BeforeEach
    public void setUp() {
        random = new Random();
    }

    // ==================== Property 3: Input Assembly Completeness ====================
    // Feature: coze-ai-integration, Property 3: Input Assembly Completeness
    // Validates: Requirements 4.2

    @Test
    @DisplayName("Property 3: 验证数据库中存在爽文剧本生成配置")
    public void testEpicScriptConfigExists() {
        // 从数据库获取配置
        AiConfig config = aiConfigService.getByConfigKey(EPIC_SCRIPT_CONFIG_KEY);
        
        if (config != null) {
            // 验证配置的必要字段
            assertNotNull(config.getWorkflowId(), "workflowId不应为null");
            assertNotNull(config.getApiToken(), "apiToken不应为null");
            assertNotNull(config.getApiBaseUrl(), "apiBaseUrl不应为null");
            
            System.out.println("爽文剧本配置验证通过: " + EPIC_SCRIPT_CONFIG_KEY);
            System.out.println("  workflowId: " + config.getWorkflowId());
            System.out.println("  apiBaseUrl: " + config.getApiBaseUrl());
        } else {
            System.out.println("警告：未找到配置 " + EPIC_SCRIPT_CONFIG_KEY + "，请先在数据库中添加配置");
        }
    }

    @Test
    @DisplayName("Property 3: 输入组装完整性 - 验证所有非空字段都被包含在输入中")
    public void testInputAssemblyCompleteness() throws Exception {
        // 使用反射获取私有方法
        Method assembleMethod = EpicScriptServiceImpl.class.getDeclaredMethod(
                "assembleScriptInput", EpicScriptCreateRequest.class);
        assembleMethod.setAccessible(true);

        // 使用真实业务数据进行多次测试
        IntStream.range(0, 10).forEach(i -> {
            try {
                // 从样本数据中随机选择真实业务数据
                String title = getRandomSample(SAMPLE_TITLES);
                String theme = getRandomSample(SAMPLE_THEMES);
                String style = getRandomStyle();
                String length = getRandomLength();
                String plotIntro = getRandomSample(SAMPLE_PLOT_INTROS);
                String plotTurning = getRandomSample(SAMPLE_PLOT_TURNINGS);
                String plotClimax = getRandomSample(SAMPLE_PLOT_CLIMAXES);
                String plotEnding = getRandomSample(SAMPLE_PLOT_ENDINGS);

                // 创建请求对象
                EpicScriptCreateRequest request = new EpicScriptCreateRequest();
                request.setTitle(title);
                request.setTheme(theme);
                request.setStyle(style);
                request.setLength(length);
                request.setPlotIntro(plotIntro);
                request.setPlotTurning(plotTurning);
                request.setPlotClimax(plotClimax);
                request.setPlotEnding(plotEnding);

                String result = (String) assembleMethod.invoke(epicScriptService, request);

                // 验证所有字段都被包含
                assertTrue(result.contains(title), "输入应包含标题: " + title);
                assertTrue(result.contains(theme), "输入应包含主题: " + theme);
                assertTrue(result.contains(plotIntro), "输入应包含序幕");
                assertTrue(result.contains(plotTurning), "输入应包含转折");
                assertTrue(result.contains(plotClimax), "输入应包含高潮");
                assertTrue(result.contains(plotEnding), "输入应包含结局");

                // 验证风格和篇幅描述
                assertTrue(result.contains("【剧本风格】"), "输入应包含风格标签");
                assertTrue(result.contains("【篇幅长度】"), "输入应包含篇幅标签");
            } catch (Exception e) {
                fail("测试执行失败: " + e.getMessage());
            }
        });
    }

    @Test
    @DisplayName("Property 3: 输入组装 - 验证风格描述转换")
    public void testStyleDescriptionConversion() throws Exception {
        Method getStyleDescMethod = EpicScriptServiceImpl.class.getDeclaredMethod(
                "getStyleDescription", String.class);
        getStyleDescMethod.setAccessible(true);

        // 验证各种风格的描述转换
        assertEquals("职场逆袭", getStyleDescMethod.invoke(epicScriptService, "career"));
        assertEquals("情感圆满", getStyleDescMethod.invoke(epicScriptService, "love"));
        assertEquals("玄幻觉醒", getStyleDescMethod.invoke(epicScriptService, "fantasy"));
        assertEquals("unknown", getStyleDescMethod.invoke(epicScriptService, "unknown"));
    }

    @Test
    @DisplayName("Property 3: 输入组装 - 验证篇幅描述转换")
    public void testLengthDescriptionConversion() throws Exception {
        Method getLengthDescMethod = EpicScriptServiceImpl.class.getDeclaredMethod(
                "getLengthDescription", String.class);
        getLengthDescMethod.setAccessible(true);

        // 验证各种篇幅的描述转换
        assertEquals("标准篇", getLengthDescMethod.invoke(epicScriptService, "medium"));
        assertEquals("长篇", getLengthDescMethod.invoke(epicScriptService, "long"));
        assertEquals("short", getLengthDescMethod.invoke(epicScriptService, "short"));
    }

    @Test
    @DisplayName("Property 3: 输入组装 - 验证空字段不被包含")
    public void testInputAssemblyWithEmptyFields() throws Exception {
        // 创建只有部分字段的请求
        EpicScriptCreateRequest request = new EpicScriptCreateRequest();
        request.setTitle("测试标题");
        request.setTheme("测试主题");
        // 其他字段为空

        Method assembleMethod = EpicScriptServiceImpl.class.getDeclaredMethod(
                "assembleScriptInput", EpicScriptCreateRequest.class);
        assembleMethod.setAccessible(true);

        String result = (String) assembleMethod.invoke(epicScriptService, request);

        // 验证包含非空字段
        assertTrue(result.contains("测试标题"), "输入应包含标题");
        assertTrue(result.contains("测试主题"), "输入应包含主题");

        // 验证不包含空字段的标签
        assertFalse(result.contains("【序幕-低谷回响】"), "空字段不应被包含");
        assertFalse(result.contains("【转折-契机出现】"), "空字段不应被包含");
        assertFalse(result.contains("【高潮-命运抉择】"), "空字段不应被包含");
        assertFalse(result.contains("【结局-新的开始】"), "空字段不应被包含");
    }

    @Test
    @DisplayName("Property 3: 输入组装 - 验证所有字段为空时返回空字符串")
    public void testInputAssemblyWithAllEmptyFields() throws Exception {
        EpicScriptCreateRequest request = new EpicScriptCreateRequest();

        Method assembleMethod = EpicScriptServiceImpl.class.getDeclaredMethod(
                "assembleScriptInput", EpicScriptCreateRequest.class);
        assembleMethod.setAccessible(true);

        String result = (String) assembleMethod.invoke(epicScriptService, request);

        // 验证返回空字符串
        assertTrue(result.isEmpty(), "所有字段为空时应返回空字符串");
    }

    @Test
    @DisplayName("Property 3: 输入组装 - 随机部分字段填充测试")
    public void testInputAssemblyWithRandomPartialFields() throws Exception {
        Method assembleMethod = EpicScriptServiceImpl.class.getDeclaredMethod(
                "assembleScriptInput", EpicScriptCreateRequest.class);
        assembleMethod.setAccessible(true);

        // 使用真实业务数据进行多次测试
        IntStream.range(0, 10).forEach(i -> {
            try {
                EpicScriptCreateRequest request = new EpicScriptCreateRequest();

                // 随机决定哪些字段有值，使用真实业务数据
                String title = random.nextBoolean() ? getRandomSample(SAMPLE_TITLES) : null;
                String theme = random.nextBoolean() ? getRandomSample(SAMPLE_THEMES) : null;
                String style = random.nextBoolean() ? getRandomStyle() : null;
                String length = random.nextBoolean() ? getRandomLength() : null;
                String plotIntro = random.nextBoolean() ? getRandomSample(SAMPLE_PLOT_INTROS) : null;
                String plotTurning = random.nextBoolean() ? getRandomSample(SAMPLE_PLOT_TURNINGS) : null;
                String plotClimax = random.nextBoolean() ? getRandomSample(SAMPLE_PLOT_CLIMAXES) : null;
                String plotEnding = random.nextBoolean() ? getRandomSample(SAMPLE_PLOT_ENDINGS) : null;

                request.setTitle(title);
                request.setTheme(theme);
                request.setStyle(style);
                request.setLength(length);
                request.setPlotIntro(plotIntro);
                request.setPlotTurning(plotTurning);
                request.setPlotClimax(plotClimax);
                request.setPlotEnding(plotEnding);

                String result = (String) assembleMethod.invoke(epicScriptService, request);

                // 验证非空字段被包含，空字段不被包含
                if (title != null && !title.isEmpty()) {
                    assertTrue(result.contains(title), "非空标题应被包含");
                }
                if (theme != null && !theme.isEmpty()) {
                    assertTrue(result.contains(theme), "非空主题应被包含");
                }
                if (plotIntro != null && !plotIntro.isEmpty()) {
                    assertTrue(result.contains(plotIntro), "非空序幕应被包含");
                }
                if (plotTurning != null && !plotTurning.isEmpty()) {
                    assertTrue(result.contains(plotTurning), "非空转折应被包含");
                }
                if (plotClimax != null && !plotClimax.isEmpty()) {
                    assertTrue(result.contains(plotClimax), "非空高潮应被包含");
                }
                if (plotEnding != null && !plotEnding.isEmpty()) {
                    assertTrue(result.contains(plotEnding), "非空结局应被包含");
                }
            } catch (Exception e) {
                fail("测试执行失败: " + e.getMessage());
            }
        });
    }

    @Test
    @DisplayName("Property 3: 输入组装 - 验证输出格式正确")
    public void testInputAssemblyFormat() throws Exception {
        EpicScriptCreateRequest request = new EpicScriptCreateRequest();
        request.setTitle("我的逆袭人生");
        request.setTheme("成为行业领袖");
        request.setStyle("career");
        request.setLength("medium");
        request.setPlotIntro("从一个普通员工开始");
        request.setPlotTurning("遇到贵人指点");
        request.setPlotClimax("面临重大抉择");
        request.setPlotEnding("成功逆袭");

        Method assembleMethod = EpicScriptServiceImpl.class.getDeclaredMethod(
                "assembleScriptInput", EpicScriptCreateRequest.class);
        assembleMethod.setAccessible(true);

        String result = (String) assembleMethod.invoke(epicScriptService, request);

        // 验证格式标签
        assertTrue(result.contains("【剧本标题】我的逆袭人生"), "应包含正确格式的标题");
        assertTrue(result.contains("【主题渴望】成为行业领袖"), "应包含正确格式的主题");
        assertTrue(result.contains("【剧本风格】职场逆袭"), "应包含正确格式的风格");
        assertTrue(result.contains("【篇幅长度】标准篇"), "应包含正确格式的篇幅");
        assertTrue(result.contains("【序幕-低谷回响】从一个普通员工开始"), "应包含正确格式的序幕");
        assertTrue(result.contains("【转折-契机出现】遇到贵人指点"), "应包含正确格式的转折");
        assertTrue(result.contains("【高潮-命运抉择】面临重大抉择"), "应包含正确格式的高潮");
        assertTrue(result.contains("【结局-新的开始】成功逆袭"), "应包含正确格式的结局");
    }

    /**
     * 获取随机风格
     */
    private String getRandomStyle() {
        String[] styles = {"career", "love", "fantasy"};
        return styles[random.nextInt(styles.length)];
    }

    /**
     * 获取随机篇幅
     */
    private String getRandomLength() {
        String[] lengths = {"medium", "long"};
        return lengths[random.nextInt(lengths.length)];
    }

    /**
     * 从样本列表中随机获取一个元素
     * @param samples 样本列表
     * @return 随机选择的样本
     */
    private String getRandomSample(List<String> samples) {
        return samples.get(random.nextInt(samples.size()));
    }
}
