package com.qqchen.deploy.backend.framework.utils;

import lombok.Data;
import org.junit.jupiter.api.Test;

import java.util.HashMap;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;

/**
 * Spring EL 表达式解析工具测试
 *
 * @author qqchen
 * @date 2025-11-04
 */
class SpelExpressionResolverTest {

    /**
     * 测试用的 DTO
     */
    @Data
    static class TestDTO {
        private String taskDescription;
        private String approvalTitle;
        private String approvalContent;
        private String statusMessage;
        private Integer version; // 非字符串字段，不应被解析
    }

    /**
     * 测试：简单变量引用
     */
    @Test
    void testSimpleVariableReference() {
        Map<String, Object> variables = new HashMap<>();
        Map<String, Object> deploy = new HashMap<>();
        deploy.put("applicationName", "SSO管理后台");
        variables.put("deploy", deploy);

        String template = "${deploy.applicationName}需要部署";
        String result = SpelExpressionResolver.resolve(template, variables);

        assertEquals("SSO管理后台需要部署", result);
    }

    /**
     * 测试：条件判断（三元运算符）
     */
    @Test
    void testConditionalExpression() {
        Map<String, Object> variables = new HashMap<>();
        Map<String, Object> deploy = new HashMap<>();
        deploy.put("status", "SUCCESS");
        variables.put("deploy", deploy);

        String template = "部署${deploy.status == 'SUCCESS' ? '成功' : '失败'}";
        String result = SpelExpressionResolver.resolve(template, variables);

        assertEquals("部署成功", result);
    }

    /**
     * 测试：复杂条件判断
     */
    @Test
    void testComplexConditionalExpression() {
        Map<String, Object> variables = new HashMap<>();
        Map<String, Object> deploy = new HashMap<>();
        deploy.put("duration", 120);
        variables.put("deploy", deploy);

        String template = "部署耗时${deploy.duration > 60 ? '较长(' + deploy.duration + '秒)' : '正常'}";
        String result = SpelExpressionResolver.resolve(template, variables);

        assertEquals("部署耗时较长(120秒)", result);
    }

    /**
     * 测试：多表达式混合
     */
    @Test
    void testMultipleExpressions() {
        Map<String, Object> variables = new HashMap<>();
        Map<String, Object> deploy = new HashMap<>();
        deploy.put("applicationName", "SSO管理后台");
        deploy.put("environment", "生产环境");
        deploy.put("status", "SUCCESS");
        variables.put("deploy", deploy);

        String template = "【部署通知】应用 ${deploy.applicationName} 在 ${deploy.environment} 部署${deploy.status == 'SUCCESS' ? '成功' : '失败'}";
        String result = SpelExpressionResolver.resolve(template, variables);

        assertEquals("【部署通知】应用 SSO管理后台 在 生产环境 部署成功", result);
    }

    /**
     * 测试：嵌套对象访问
     */
    @Test
    void testNestedObjectAccess() {
        Map<String, Object> variables = new HashMap<>();
        Map<String, Object> user = new HashMap<>();
        Map<String, Object> department = new HashMap<>();
        department.put("name", "研发部");
        user.put("name", "张三");
        user.put("department", department);
        variables.put("user", user);

        String template = "用户 ${user.name} 来自 ${user.department.name}";
        String result = SpelExpressionResolver.resolve(template, variables);

        assertEquals("用户 张三 来自 研发部", result);
    }

    /**
     * 测试：数学运算
     */
    @Test
    void testMathematicalExpression() {
        Map<String, Object> variables = new HashMap<>();
        variables.put("count", 5);
        variables.put("price", 10.5);

        String template = "总价：${count * price} 元";
        String result = SpelExpressionResolver.resolve(template, variables);

        assertEquals("总价：52.5 元", result);
    }

    /**
     * 测试：布尔逻辑运算
     */
    @Test
    void testBooleanLogic() {
        Map<String, Object> variables = new HashMap<>();
        Map<String, Object> deploy = new HashMap<>();
        deploy.put("status", "SUCCESS");
        deploy.put("requireApproval", true);
        variables.put("deploy", deploy);

        String template = "${deploy.status == 'SUCCESS' && deploy.requireApproval ? '审批通过并部署成功' : '等待审批或部署失败'}";
        String result = SpelExpressionResolver.resolve(template, variables);

        assertEquals("审批通过并部署成功", result);
    }

    /**
     * 测试：evaluateExpression - 返回原始类型
     */
    @Test
    void testEvaluateExpression() {
        Map<String, Object> variables = new HashMap<>();
        Map<String, Object> deploy = new HashMap<>();
        deploy.put("status", "SUCCESS");
        deploy.put("duration", 120);
        variables.put("deploy", deploy);

        // 返回布尔值
        Boolean isSuccess = SpelExpressionResolver.evaluateExpression(
            "deploy.status == 'SUCCESS'", variables, Boolean.class);
        assertEquals(true, isSuccess);

        // 返回整数
        Integer duration = SpelExpressionResolver.evaluateExpression(
            "deploy.duration", variables, Integer.class);
        assertEquals(120, duration);

        // 返回字符串
        String message = SpelExpressionResolver.evaluateExpression(
            "deploy.status == 'SUCCESS' ? '成功' : '失败'", variables, String.class);
        assertEquals("成功", message);
    }

    /**
     * 测试：resolveObject - 自动解析对象中的所有字段（核心功能）
     */
    @Test
    void testResolveObject() {
        // 准备测试数据
        Map<String, Object> variables = new HashMap<>();
        Map<String, Object> deploy = new HashMap<>();
        deploy.put("applicationName", "SSO管理后台");
        deploy.put("environment", "生产环境");
        deploy.put("status", "SUCCESS");
        deploy.put("duration", 150);
        variables.put("deploy", deploy);

        // 创建包含表达式的 DTO
        TestDTO dto = new TestDTO();
        dto.setTaskDescription("${deploy.applicationName}需要部署到${deploy.environment}");
        dto.setApprovalTitle("部署审批");
        dto.setApprovalContent("应用 ${deploy.applicationName} 部署${deploy.status == 'SUCCESS' ? '成功' : '失败'}");
        dto.setStatusMessage("耗时${deploy.duration > 60 ? '较长' : '正常'}");
        dto.setVersion(1); // 非字符串字段

        // ✅ 自动解析所有包含表达式的字符串字段
        SpelExpressionResolver.resolveObject(dto, variables);

        // 验证结果
        assertEquals("SSO管理后台需要部署到生产环境", dto.getTaskDescription());
        assertEquals("部署审批", dto.getApprovalTitle()); // 无表达式，保持原样
        assertEquals("应用 SSO管理后台 部署成功", dto.getApprovalContent());
        assertEquals("耗时较长", dto.getStatusMessage());
        assertEquals(1, dto.getVersion()); // 非字符串字段不受影响
    }

    /**
     * 测试：表达式解析失败时的处理
     */
    @Test
    void testExpressionResolutionFailure() {
        Map<String, Object> variables = new HashMap<>();
        variables.put("deploy", new HashMap<>());

        // 访问不存在的字段
        String template = "应用 ${deploy.nonExistentField} 部署完成";
        String result = SpelExpressionResolver.resolve(template, variables);

        // 应该保留原样或返回空值
        assertNotNull(result);
        System.out.println("解析失败时的结果: " + result);
    }

    /**
     * 测试：无表达式的字符串
     */
    @Test
    void testNoExpression() {
        Map<String, Object> variables = new HashMap<>();

        String template = "这是一个普通字符串，没有表达式";
        String result = SpelExpressionResolver.resolve(template, variables);

        assertEquals("这是一个普通字符串，没有表达式", result);
    }

    /**
     * 测试：null 值处理
     */
    @Test
    void testNullHandling() {
        String result = SpelExpressionResolver.resolve(null, new HashMap<>());
        assertEquals(null, result);
    }

    /**
     * 测试：批量解析
     */
    @Test
    void testResolveAll() {
        Map<String, Object> variables = new HashMap<>();
        Map<String, Object> deploy = new HashMap<>();
        deploy.put("applicationName", "SSO管理后台");
        deploy.put("status", "SUCCESS");
        variables.put("deploy", deploy);

        String[] templates = {
            "${deploy.applicationName}需要部署",
            "部署${deploy.status == 'SUCCESS' ? '成功' : '失败'}",
            "普通文本"
        };

        String[] results = SpelExpressionResolver.resolveAll(templates, variables);

        assertEquals(3, results.length);
        assertEquals("SSO管理后台需要部署", results[0]);
        assertEquals("部署成功", results[1]);
        assertEquals("普通文本", results[2]);
    }

    /**
     * 测试：集合访问（数组/列表索引）
     */
    @Test
    void testCollectionAccess() {
        Map<String, Object> variables = new HashMap<>();
        
        // 购物车示例
        Map<String, Object> shoppingCart = new HashMap<>();
        java.util.List<Map<String, Object>> items = new java.util.ArrayList<>();
        
        Map<String, Object> item1 = new HashMap<>();
        item1.put("name", "iPhone 15 Pro");
        item1.put("price", 7999);
        item1.put("quantity", 1);
        
        Map<String, Object> item2 = new HashMap<>();
        item2.put("name", "MacBook Pro");
        item2.put("price", 14999);
        item2.put("quantity", 2);
        
        items.add(item1);
        items.add(item2);
        shoppingCart.put("items", items);
        shoppingCart.put("total", 3);
        variables.put("shoppingCart", shoppingCart);

        // 测试列表索引访问
        String template1 = "第一个商品: ${shoppingCart.items[0].name}";
        String result1 = SpelExpressionResolver.resolve(template1, variables);
        assertEquals("第一个商品: iPhone 15 Pro", result1);

        // 测试嵌套访问和运算
        String template2 = "第二个商品 ${shoppingCart.items[1].name} 单价 ${shoppingCart.items[1].price} 元，数量 ${shoppingCart.items[1].quantity} 件";
        String result2 = SpelExpressionResolver.resolve(template2, variables);
        assertEquals("第二个商品 MacBook Pro 单价 14999 元，数量 2 件", result2);

        // 测试列表大小访问
        String template3 = "购物车共有 ${shoppingCart.items.size()} 件商品";
        String result3 = SpelExpressionResolver.resolve(template3, variables);
        assertEquals("购物车共有 2 件商品", result3);
        
        // 测试复杂表达式：索引 + 条件判断
        String template4 = "第一个商品价格${shoppingCart.items[0].price > 5000 ? '较高' : '合理'}";
        String result4 = SpelExpressionResolver.resolve(template4, variables);
        assertEquals("第一个商品价格较高", result4);
    }

    /**
     * 测试：数组访问
     */
    @Test
    void testArrayAccess() {
        Map<String, Object> variables = new HashMap<>();
        
        String[] fruits = {"苹果", "香蕉", "橙子"};
        variables.put("fruits", fruits);
        
        int[] numbers = {10, 20, 30, 40, 50};
        variables.put("numbers", numbers);

        // 字符串数组访问
        String template1 = "第一个水果: ${fruits[0]}，第三个水果: ${fruits[2]}";
        String result1 = SpelExpressionResolver.resolve(template1, variables);
        assertEquals("第一个水果: 苹果，第三个水果: 橙子", result1);

        // 数字数组访问和运算
        String template2 = "第二个数字是 ${numbers[1]}，总和是 ${numbers[0] + numbers[1] + numbers[2]}";
        String result2 = SpelExpressionResolver.resolve(template2, variables);
        assertEquals("第二个数字是 20，总和是 60", result2);
        
        // 数组长度访问
        String template3 = "水果数组长度: ${fruits.length}，数字数组长度: ${numbers.length}";
        String result3 = SpelExpressionResolver.resolve(template3, variables);
        assertEquals("水果数组长度: 3，数字数组长度: 5", result3);
    }

    /**
     * 测试：Map 键访问（使用方括号语法）
     */
    @Test
    void testMapKeyAccess() {
        Map<String, Object> variables = new HashMap<>();
        
        Map<String, Object> config = new HashMap<>();
        config.put("max-connections", 100);
        config.put("timeout-seconds", 30);
        config.put("enable-ssl", true);
        variables.put("config", config);

        // Map 键访问（带特殊字符的键需要使用方括号）
        String template1 = "最大连接数: ${config['max-connections']}";
        String result1 = SpelExpressionResolver.resolve(template1, variables);
        assertEquals("最大连接数: 100", result1);

        // 多个 Map 键访问
        String template2 = "超时时间 ${config['timeout-seconds']} 秒，SSL${config['enable-ssl'] ? '已启用' : '未启用'}";
        String result2 = SpelExpressionResolver.resolve(template2, variables);
        assertEquals("超时时间 30 秒，SSL已启用", result2);
    }
}

