package com.agent.management.service;

import com.agent.management.common.exception.BusinessException;
import com.agent.management.config.ErrorPatternProperties;
import com.agent.management.model.entity.HermesErrorPattern;
import com.agent.management.repository.HermesErrorPatternRepository;
import com.agent.management.repository.json.JsonStore;
import com.agent.management.service.impl.HermesErrorPatternServiceImpl;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

/**
 * {@link HermesErrorPatternServiceImpl} 单元测试。
 *
 * <p>覆盖：</p>
 * <ul>
 *   <li>B 方案核心：{@code isErrorResponse} 子串匹配（大小写不敏感、SCAN_LIMIT、空响应）</li>
 *   <li>CRUD：创建/编辑/删除/切换/重置</li>
 *   <li>内置模式保护：禁止删除、禁止改名</li>
 *   <li>启动时自动初始化内置模式</li>
 * </ul>
 */
class HermesErrorPatternServiceTest {

    @TempDir
    Path tempDir;

    private HermesErrorPatternRepository repository;
    private HermesErrorPatternServiceImpl service;

    @BeforeEach
    void setUp() {
        JsonStore store = new JsonStore(tempDir);
        repository = new HermesErrorPatternRepository(store);
        // 注入默认配置（与 Spring 容器外行为一致：使用 ErrorPatternProperties.DEFAULT_BUILTINS）
        service = new HermesErrorPatternServiceImpl(repository, new ErrorPatternProperties());
        // 触发 @PostConstruct 逻辑（单元测试不走 Spring 生命周期）
        service.initBuiltInPatterns();
    }

    // ========== 嗅探匹配 ==========

    @Test
    void isErrorResponse_matchesExactSubstring() {
        // 内置模式 "API call failed after"
        HermesErrorPatternService.ErrorDetectionResult r =
                service.isErrorResponse("API call failed after 3 retries: HTTP 429");
        assertThat(r.isError()).isTrue();
        assertThat(r.getMatchedPatternName()).isEqualTo("LLM SDK 重试耗尽");
    }

    @Test
    void isErrorResponse_caseInsensitive() {
        // 大写不敏感
        assertThat(service.isErrorResponse("api call failed after 1 retry").isError()).isTrue();
        assertThat(service.isErrorResponse("API CALL FAILED AFTER 3 RETRIES").isError()).isTrue();
    }

    @Test
    void isErrorResponse_matchesChinesePattern() {
        // 内置模式 "该模型当前访问量过大"
        assertThat(service.isErrorResponse("错误：该模型当前访问量过大，请稍后重试").isError()).isTrue();
    }

    @Test
    void isResponse_noMatch_returnsFalse() {
        assertThat(service.isErrorResponse("这是一段正常的 LLM 回复，内容涉及 HTTP 协议历史").isError()).isFalse();
    }

    @Test
    void isResponse_nullOrBlank_returnsFalse() {
        assertThat(service.isErrorResponse(null).isError()).isFalse();
        assertThat(service.isErrorResponse("").isError()).isFalse();
        assertThat(service.isErrorResponse("   ").isError()).isFalse();
    }

    @Test
    void isResponse_disabledPatternNotMatched() {
        // 创建一个新模式，禁用后应不参与匹配
        HermesErrorPattern p = new HermesErrorPattern();
        p.setName("测试专用");
        p.setPattern("MY_VERY_UNIQUE_TEST_MARKER");
        p.setEnabled(true);
        p = service.create(p);
        assertThat(service.isErrorResponse("文本包含 MY_VERY_UNIQUE_TEST_MARKER").isError()).isTrue();

        // 禁用后不再匹配
        service.update(p.getId(), buildUpdate(p.getName(), p.getPattern(), p.getDescription(), false));
        assertThat(service.isErrorResponse("文本包含 MY_VERY_UNIQUE_TEST_MARKER").isError()).isFalse();
    }

    @Test
    void isResponse_scanLimit_doesNotMissErrorsAtStart() {
        // 内置错误前缀在 finalText 开头，超过 SCAN_LIMIT 后的部分不算（但开头能命中）
        String head = "API call failed after 3 retries";
        // 即便后面有 600 字符的"正常文本"，开头仍是错误
        String body = head + "a".repeat(600);
        assertThat(service.isErrorResponse(body).isError()).isTrue();
    }

    @Test
    void isResponse_scanLimit_patternBeyondLimitNotMatched() {
        // 自定义 pattern 只在超过 500 字符后出现，不应被匹配（SCAN_LIMIT 截断）
        HermesErrorPattern p = new HermesErrorPattern();
        p.setName("超长模式");
        p.setPattern("ZZZ_UNIQUE_BEYOND_LIMIT");
        p.setEnabled(true);
        service.create(p);
        String body = "a".repeat(600) + " ZZZ_UNIQUE_BEYOND_LIMIT";
        assertThat(service.isErrorResponse(body).isError()).isFalse();
    }

    // ========== 分隔符归一化（- / _ / 空格 等价） ==========

    @Test
    void isResponse_underscorePatternMatchesSpacedText() {
        // 用户场景：pattern "api_error" 应匹配文本 "API ERROR ..."
        HermesErrorPattern p = new HermesErrorPattern();
        p.setName("API 错误");
        p.setPattern("api_error");
        p.setEnabled(true);
        service.create(p);

        assertThat(service.isErrorResponse("调用失败：API ERROR encountered").isError()).isTrue();
    }

    @Test
    void isResponse_hyphenPatternMatchesUnderscoreText() {
        // pattern "rate-limit" 应匹配文本 "RATE_LIMIT exceeded"
        HermesErrorPattern p = new HermesErrorPattern();
        p.setName("限流");
        p.setPattern("rate-limit");
        p.setEnabled(true);
        service.create(p);

        assertThat(service.isErrorResponse("RATE_LIMIT exceeded").isError()).isTrue();
    }

    @Test
    void isResponse_multipleSeparatorsAllEquivalent() {
        // 多分隔符混合：pattern "context_length_exceeded" 应匹配 "CONTEXT-LENGTH EXCEEDED"
        HermesErrorPattern p = new HermesErrorPattern();
        p.setName("上下文超限");
        p.setPattern("context_length_exceeded");
        p.setEnabled(true);
        service.create(p);

        assertThat(service.isErrorResponse("请求失败：CONTEXT-LENGTH EXCEEDED").isError()).isTrue();
        assertThat(service.isErrorResponse("请求失败：CONTEXT LENGTH EXCEEDED").isError()).isTrue();
        assertThat(service.isErrorResponse("请求失败：CONTEXT_LENGTH_EXCEEDED").isError()).isTrue();
    }

    @Test
    void isResponse_noSeparatorPatternDoesNotMatchSeparatedText() {
        // 防止过度归一化：pattern "apierror"（无分隔符）不应匹配 "API ERROR"（有分隔符）
        HermesErrorPattern p = new HermesErrorPattern();
        p.setName("连写模式");
        p.setPattern("apierror");
        p.setEnabled(true);
        service.create(p);

        assertThat(service.isErrorResponse("调用失败：API ERROR encountered").isError()).isFalse();
    }

    @Test
    void isResponse_separatedPatternDoesNotMatchNoSeparatorText() {
        // 反向同样不匹配：pattern "api error"（带分隔符）不应匹配 "APIERROR"（无分隔符）
        HermesErrorPattern p = new HermesErrorPattern();
        p.setName("带分隔符模式");
        p.setPattern("api error");
        p.setEnabled(true);
        service.create(p);

        assertThat(service.isErrorResponse("调用失败：APIERROR encountered").isError()).isFalse();
    }

    // ========== 内置初始化 ==========

    @Test
    void initBuiltInPatterns_writesDefaultsOnFirstRun() {
        List<HermesErrorPattern> all = service.list();
        // 默认内置数量与 ErrorPatternProperties.DEFAULT_BUILTINS 一致（当前 25 条）
        assertThat(all.size()).isEqualTo(ErrorPatternProperties.DEFAULT_BUILTINS.size());
        // 都应是内置
        assertThat(all.stream().filter(p -> Boolean.TRUE.equals(p.getIsBuiltIn())).count()).isEqualTo(all.size());
    }

    @Test
    void initBuiltInPatterns_doesNotOverwriteUserEdits() {
        // 用户禁用某条内置
        HermesErrorPattern first = service.list().get(0);
        service.update(first.getId(),
                buildUpdate(first.getName(), first.getPattern(), first.getDescription(), false));

        // 再次触发初始化（模拟重启）
        service.initBuiltInPatterns();

        // 内置数量保持不变（不会被重新写入覆盖）
        assertThat(service.list().size()).isEqualTo(ErrorPatternProperties.DEFAULT_BUILTINS.size());
        // 那条内置仍是禁用状态
        assertThat(repository.findById(first.getId()).orElseThrow().getEnabled()).isFalse();
    }

    @Test
    void initBuiltInPatterns_incrementallyAppendsMissingBuiltin() throws Exception {
        // 模拟"老部署"：独立目录预置少量数据（1 条同名老内置已被禁用 + 1 条自定义）
        Path legacyDir = Files.createTempDirectory("legacy-error-patterns");
        JsonStore legacyStore = new JsonStore(legacyDir);
        HermesErrorPatternRepository legacyRepo = new HermesErrorPatternRepository(legacyStore);
        HermesErrorPatternServiceImpl legacyService =
                new HermesErrorPatternServiceImpl(legacyRepo, new ErrorPatternProperties());

        HermesErrorPattern oldBuiltin = new HermesErrorPattern();
        oldBuiltin.setName("LLM SDK 重试耗尽");   // 与默认列表同名
        oldBuiltin.setPattern("old-pattern-text");
        oldBuiltin.setEnabled(false);             // 用户已禁用
        oldBuiltin.setIsBuiltIn(true);
        oldBuiltin.onCreate();
        legacyRepo.save(oldBuiltin);

        HermesErrorPattern userCustom = new HermesErrorPattern();
        userCustom.setName("用户自定义模式");
        userCustom.setPattern("custom-pat");
        userCustom.setEnabled(true);
        userCustom.setIsBuiltIn(false);
        userCustom.onCreate();
        legacyRepo.save(userCustom);

        // 触发增量补齐
        legacyService.initBuiltInPatterns();

        List<HermesErrorPattern> all = legacyService.list();
        // 1 同名保留 + 1 自定义 + (默认 25 - 1 同名) 新增 = 26
        assertThat(all.size()).isEqualTo(ErrorPatternProperties.DEFAULT_BUILTINS.size() + 1);

        // 同名老内置保持原样（pattern 与 enabled 都未被覆盖）
        HermesErrorPattern stillOld = all.stream()
                .filter(p -> "LLM SDK 重试耗尽".equals(p.getName()))
                .findFirst().orElseThrow();
        assertThat(stillOld.getPattern()).isEqualTo("old-pattern-text");
        assertThat(stillOld.getEnabled()).isFalse();

        // 用户自定义保留
        assertThat(all.stream().anyMatch(p -> "用户自定义模式".equals(p.getName()))).isTrue();

        // 默认列表中此前缺失的项被补齐
        assertThat(all.stream().anyMatch(p -> "认证失败（英文）".equals(p.getName()))).isTrue();
        assertThat(all.stream().anyMatch(p -> "JSON 解析失败".equals(p.getName()))).isTrue();
    }

    @Test
    void initBuiltInPatterns_noopWhenAllDefaultsPresent() {
        // setUp 已写入全部默认；再次触发（模拟重启）不应有任何新增
        int before = service.list().size();
        service.initBuiltInPatterns();
        assertThat(service.list().size()).isEqualTo(before);
    }

    // ========== CRUD ==========

    @Test
    void create_savesCustomPattern() {
        HermesErrorPattern p = new HermesErrorPattern();
        p.setName("自定义模式");
        p.setPattern("custom error");
        p.setEnabled(true);
        HermesErrorPattern saved = service.create(p);

        assertThat(saved.getId()).isNotNull();
        assertThat(saved.getIsBuiltIn()).isFalse();
        assertThat(saved.getCreatedAt()).isNotNull();
    }

    @Test
    void create_duplicateNameRejected() {
        HermesErrorPattern p1 = new HermesErrorPattern();
        p1.setName("唯一名");
        p1.setPattern("pat1");
        service.create(p1);

        HermesErrorPattern p2 = new HermesErrorPattern();
        p2.setName("唯一名");
        p2.setPattern("pat2");
        assertThatThrownBy(() -> service.create(p2))
                .isInstanceOf(BusinessException.class)
                .hasMessageContaining("已存在");
    }

    @Test
    void create_emptyFieldsRejected() {
        HermesErrorPattern empty = new HermesErrorPattern();
        assertThatThrownBy(() -> service.create(empty)).isInstanceOf(BusinessException.class);

        HermesErrorPattern noPattern = new HermesErrorPattern();
        noPattern.setName("test");
        assertThatThrownBy(() -> service.create(noPattern)).isInstanceOf(BusinessException.class);
    }

    @Test
    void delete_builtinRejected() {
        HermesErrorPattern builtin = service.list().stream()
                .filter(p -> Boolean.TRUE.equals(p.getIsBuiltIn())).findFirst().orElseThrow();
        assertThatThrownBy(() -> service.delete(builtin.getId()))
                .isInstanceOf(BusinessException.class)
                .hasMessageContaining("内置");
    }

    @Test
    void delete_customOk() {
        HermesErrorPattern p = new HermesErrorPattern();
        p.setName("待删");
        p.setPattern("to-delete");
        HermesErrorPattern saved = service.create(p);
        service.delete(saved.getId());
        assertThat(repository.findById(saved.getId())).isEmpty();
    }

    @Test
    void update_builtinCanEditPatternButNotRename() {
        HermesErrorPattern builtin = service.list().stream()
                .filter(p -> Boolean.TRUE.equals(p.getIsBuiltIn())).findFirst().orElseThrow();
        String originalName = builtin.getName();

        HermesErrorPattern req = new HermesErrorPattern();
        req.setName("试图改名");
        req.setPattern("new-pattern-text");
        req.setDescription("改描述");
        req.setEnabled(false);

        HermesErrorPattern updated = service.update(builtin.getId(), req);
        assertThat(updated.getPattern()).isEqualTo("new-pattern-text");
        assertThat(updated.getDescription()).isEqualTo("改描述");
        assertThat(updated.getEnabled()).isFalse();
        // 名称不变
        assertThat(updated.getName()).isEqualTo(originalName);
    }

    @Test
    void toggle_flipsEnabled() {
        HermesErrorPattern p = new HermesErrorPattern();
        p.setName("toggle test");
        p.setPattern("pat");
        p.setEnabled(true);
        HermesErrorPattern saved = service.create(p);

        HermesErrorPattern toggled = service.toggle(saved.getId());
        assertThat(toggled.getEnabled()).isFalse();

        HermesErrorPattern toggledAgain = service.toggle(saved.getId());
        assertThat(toggledAgain.getEnabled()).isTrue();
    }

    @Test
    void resetToDefaults_removesCustomAndReEnablesBuiltin() {
        // 禁用一条内置
        HermesErrorPattern builtin = service.list().stream()
                .filter(p -> Boolean.TRUE.equals(p.getIsBuiltIn())).findFirst().orElseThrow();
        service.update(builtin.getId(),
                buildUpdate(builtin.getName(), builtin.getPattern(), builtin.getDescription(), false));

        // 新增一条自定义
        HermesErrorPattern custom = new HermesErrorPattern();
        custom.setName("custom");
        custom.setPattern("custom-pat");
        service.create(custom);

        service.resetToDefaults();

        List<HermesErrorPattern> all = service.list();
        // 自定义被清空
        assertThat(all.stream().noneMatch(p -> "custom".equals(p.getName()))).isTrue();
        // 内置全部恢复启用
        assertThat(all.stream()
                .filter(p -> Boolean.TRUE.equals(p.getIsBuiltIn()))
                .allMatch(p -> Boolean.TRUE.equals(p.getEnabled()))).isTrue();
    }

    // ========== 排序：内置在前 ==========

    @Test
    void list_builtinFirst() {
        // 加几条自定义
        for (int i = 0; i < 3; i++) {
            HermesErrorPattern p = new HermesErrorPattern();
            p.setName("自定义" + i);
            p.setPattern("pat" + i);
            service.create(p);
        }
        List<HermesErrorPattern> all = service.list();
        // 内置在前
        boolean seenCustom = false;
        for (HermesErrorPattern p : all) {
            if (Boolean.TRUE.equals(p.getIsBuiltIn())) {
                assertThat(seenCustom).as("内置不应出现在自定义之后").isFalse();
            } else {
                seenCustom = true;
            }
        }
    }

    // ========== 辅助 ==========

    private HermesErrorPattern buildUpdate(String name, String pattern, String description, Boolean enabled) {
        HermesErrorPattern p = new HermesErrorPattern();
        p.setName(name);
        p.setPattern(pattern);
        p.setDescription(description);
        p.setEnabled(enabled);
        return p;
    }
}
