package com.agent.management.engine;

import com.agent.management.config.WorkflowProperties;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;

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

/**
 * {@link RetryPolicy} 单元测试。
 *
 * <p>覆盖：默认策略、节点级覆盖、全局回退、字段读取优先级、legacy maxRetries 向后兼容。</p>
 */
class RetryPolicyTest {

    private static final ObjectMapper MAPPER = new ObjectMapper();

    private static JsonNode data(String json) {
        try {
            return MAPPER.readTree(json);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Test
    void defaultPolicyHasExpectedValues() {
        RetryPolicy policy = RetryPolicy.defaultPolicy();

        assertThat(policy.runtimeMaxRetries).isEqualTo(2);
        assertThat(policy.formatMaxRetries).isEqualTo(2);
        assertThat(policy.retryOnParseError).isTrue();
        assertThat(policy.retryOnMissingRequired).isTrue();
        assertThat(policy.retryOnTypeMismatch).isTrue();
        assertThat(policy.retryOnKeyNameMismatch).isTrue();
    }

    @Test
    void fromNodeDataWithNullDataReturnsGlobalDefaults() {
        WorkflowProperties props = new WorkflowProperties();

        RetryPolicy policy = RetryPolicy.fromNodeData(null, props);

        assertThat(policy.runtimeMaxRetries).isEqualTo(2);
        assertThat(policy.formatMaxRetries).isEqualTo(2);
        assertThat(policy.retryOnParseError).isTrue();
    }

    @Test
    void fromNodeDataWithNullPropsReturnsCodeDefaults() {
        RetryPolicy policy = RetryPolicy.fromNodeData(null, null);

        assertThat(policy.runtimeMaxRetries).isEqualTo(2);
        assertThat(policy.formatMaxRetries).isEqualTo(2);
        assertThat(policy.retryOnParseError).isTrue();
        assertThat(policy.retryOnMissingRequired).isTrue();
        assertThat(policy.retryOnTypeMismatch).isTrue();
        assertThat(policy.retryOnKeyNameMismatch).isTrue();
    }

    @Test
    void nodeLevelRuntimeMaxRetriesOverridesGlobal() {
        WorkflowProperties props = new WorkflowProperties();

        JsonNode data = data("{\"runtimeMaxRetries\": 5}");

        RetryPolicy policy = RetryPolicy.fromNodeData(data, props);

        assertThat(policy.runtimeMaxRetries).isEqualTo(5);
        assertThat(policy.formatMaxRetries).isEqualTo(2);
        assertThat(policy.retryOnParseError).isTrue();
    }

    @Test
    void nodeLevelFormatMaxRetriesOverridesGlobal() {
        WorkflowProperties props = new WorkflowProperties();

        JsonNode data = data("{\"formatMaxRetries\": 4}");

        RetryPolicy policy = RetryPolicy.fromNodeData(data, props);

        assertThat(policy.formatMaxRetries).isEqualTo(4);
        assertThat(policy.runtimeMaxRetries).isEqualTo(2);
    }

    @Test
    void nodeLevelBooleanOverridesGlobal() {
        WorkflowProperties props = new WorkflowProperties();

        JsonNode data = data("{\"retryOnParseError\": false, \"retryOnTypeMismatch\": false, \"retryOnKeyNameMismatch\": false}");

        RetryPolicy policy = RetryPolicy.fromNodeData(data, props);

        assertThat(policy.retryOnParseError).isFalse();
        assertThat(policy.retryOnTypeMismatch).isFalse();
        assertThat(policy.retryOnKeyNameMismatch).isFalse();
        // 未在节点级覆盖的字段使用全局默认
        assertThat(policy.retryOnMissingRequired).isTrue();
    }

    @Test
    void globalValuesAppliedWhenNodeMissingFields() {
        WorkflowProperties props = new WorkflowProperties();
        props.getLlm().getRetry().setRuntimeMaxRetries(10);
        props.getLlm().getRetry().setFormatMaxRetries(7);
        props.getLlm().getRetry().setRetryOnParseError(false);
        props.getLlm().getRetry().setRetryOnMissingRequired(false);
        props.getLlm().getRetry().setRetryOnTypeMismatch(false);
        props.getLlm().getRetry().setRetryOnKeyNameMismatch(false);

        JsonNode data = data("{}"); // 节点级未声明任何字段

        RetryPolicy policy = RetryPolicy.fromNodeData(data, props);

        assertThat(policy.runtimeMaxRetries).isEqualTo(10);
        assertThat(policy.formatMaxRetries).isEqualTo(7);
        assertThat(policy.retryOnParseError).isFalse();
        assertThat(policy.retryOnMissingRequired).isFalse();
        assertThat(policy.retryOnTypeMismatch).isFalse();
        assertThat(policy.retryOnKeyNameMismatch).isFalse();
    }

    @Test
    void legacyMaxRetriesInNodeDataFallsBackToRuntime() {
        // 节点声明 legacy 字段 maxRetries（向后兼容），应被识别为 runtimeMaxRetries
        JsonNode data = data("{\"maxRetries\": 5}");

        RetryPolicy policy = RetryPolicy.fromNodeData(data, null);

        assertThat(policy.runtimeMaxRetries).isEqualTo(5);
    }

    @Test
    void runtimeMaxRetriesPreferredOverLegacyMaxRetries() {
        // 同时声明 runtimeMaxRetries 与 maxRetries，前者优先
        JsonNode data = data("{\"runtimeMaxRetries\": 8, \"maxRetries\": 3}");

        RetryPolicy policy = RetryPolicy.fromNodeData(data, null);

        assertThat(policy.runtimeMaxRetries).isEqualTo(8);
    }

    @Test
    void stringNumberInMaxRetriesParsed() {
        JsonNode data = data("{\"runtimeMaxRetries\": \"4\"}");

        RetryPolicy policy = RetryPolicy.fromNodeData(data, null);

        assertThat(policy.runtimeMaxRetries).isEqualTo(4);
    }

    @Test
    void stringBooleanInRetryOnParsed() {
        JsonNode data = data("{\"retryOnParseError\": \"false\"}");

        RetryPolicy policy = RetryPolicy.fromNodeData(data, null);

        assertThat(policy.retryOnParseError).isFalse();
    }

    @Test
    void invalidStringMaxRetriesFallsBackToGlobal() {
        WorkflowProperties props = new WorkflowProperties();
        props.getLlm().getRetry().setRuntimeMaxRetries(7);

        JsonNode data = data("{\"runtimeMaxRetries\": \"not-a-number\"}");

        RetryPolicy policy = RetryPolicy.fromNodeData(data, props);

        assertThat(policy.runtimeMaxRetries).isEqualTo(7);
    }

    @Test
    void runtimeMaxRetriesLessThanOneClampedToOne() {
        JsonNode data = data("{\"runtimeMaxRetries\": 0}");

        RetryPolicy policy = RetryPolicy.fromNodeData(data, null);

        assertThat(policy.runtimeMaxRetries).isEqualTo(1);
    }

    @Test
    void formatMaxRetriesNegativeClampedToOne() {
        JsonNode data = data("{\"formatMaxRetries\": -5}");

        RetryPolicy policy = RetryPolicy.fromNodeData(data, null);

        assertThat(policy.formatMaxRetries).isEqualTo(1);
    }

    @Test
    void nullFieldsUseGlobalFallback() {
        WorkflowProperties props = new WorkflowProperties();
        props.getLlm().getRetry().setRuntimeMaxRetries(6);

        JsonNode data = data("{\"runtimeMaxRetries\": null, \"retryOnParseError\": null}");

        RetryPolicy policy = RetryPolicy.fromNodeData(data, props);

        assertThat(policy.runtimeMaxRetries).isEqualTo(6);
        assertThat(policy.retryOnParseError).isTrue();
    }

    @Test
    void equalsAndHashCodeWork() {
        RetryPolicy a = new RetryPolicy(3, 2, true, false, true, true);
        RetryPolicy b = new RetryPolicy(3, 2, true, false, true, true);
        RetryPolicy c = new RetryPolicy(2, 2, true, false, true, true);

        assertThat(a).isEqualTo(b);
        assertThat(a.hashCode()).isEqualTo(b.hashCode());
        assertThat(a).isNotEqualTo(c);
    }

    @Test
    void toStringContainsKeyFields() {
        RetryPolicy policy = new RetryPolicy(3, 2, true, false, true, true);

        String str = policy.toString();

        assertThat(str).contains("runtimeMaxRetries=3");
        assertThat(str).contains("formatMaxRetries=2");
        assertThat(str).contains("parseError=true");
        assertThat(str).contains("missingRequired=false");
        assertThat(str).contains("typeMismatch=true");
        assertThat(str).contains("keyNameMismatch=true");
    }

    // ========== forHermes 静态工厂 ==========

    @Test
    void forHermes_capsRuntimeTo1_whenNodeDataAbsent() {
        // 节点未显式配置 runtimeMaxRetries，全局默认 2 → forHermes 强制降为 1
        WorkflowProperties props = new WorkflowProperties();

        RetryPolicy policy = RetryPolicy.forHermes(data("{}"), props);

        assertThat(policy.runtimeMaxRetries).isEqualTo(1);
        // 其他字段保留默认
        assertThat(policy.formatMaxRetries).isEqualTo(2);
        assertThat(policy.retryOnParseError).isTrue();
        assertThat(policy.retryOnKeyNameMismatch).isTrue();
    }

    @Test
    void forHermes_respectsExplicitNodeLevelRuntime() {
        // 节点显式配置 runtimeMaxRetries=3 → forHermes 保留用户选择
        WorkflowProperties props = new WorkflowProperties();
        JsonNode data = data("{\"runtimeMaxRetries\": 3}");

        RetryPolicy policy = RetryPolicy.forHermes(data, props);

        assertThat(policy.runtimeMaxRetries).isEqualTo(3);
    }

    @Test
    void forHermes_respectsLegacyMaxRetries() {
        // 节点使用 legacy maxRetries=5 → 视为用户显式配置，保留
        WorkflowProperties props = new WorkflowProperties();
        JsonNode data = data("{\"maxRetries\": 5}");

        RetryPolicy policy = RetryPolicy.forHermes(data, props);

        assertThat(policy.runtimeMaxRetries).isEqualTo(5);
    }

    @Test
    void forHermes_preservesGlobalFormatMaxRetries() {
        // 全局 formatMaxRetries=7 应保留（forHermes 只动 runtime）
        WorkflowProperties props = new WorkflowProperties();
        props.getLlm().getRetry().setFormatMaxRetries(7);

        RetryPolicy policy = RetryPolicy.forHermes(data("{}"), props);

        assertThat(policy.runtimeMaxRetries).isEqualTo(1);
        assertThat(policy.formatMaxRetries).isEqualTo(7);
    }

    @Test
    void forHermes_capsTo1_whenGlobalRuntimeIs2() {
        // 全局 runtimeMaxRetries=2 时，forHermes 仍降为 1
        WorkflowProperties props = new WorkflowProperties();
        props.getLlm().getRetry().setRuntimeMaxRetries(2);

        RetryPolicy policy = RetryPolicy.forHermes(null, props);

        assertThat(policy.runtimeMaxRetries).isEqualTo(1);
    }

    @Test
    void forHermes_noOpWhenGlobalRuntimeAlreadyLow() {
        // 全局 runtimeMaxRetries=1 时，forHermes 不需要降级
        WorkflowProperties props = new WorkflowProperties();
        props.getLlm().getRetry().setRuntimeMaxRetries(1);

        RetryPolicy policy = RetryPolicy.forHermes(null, props);

        // runtimeMaxRetries 应保持为 1（不需要再降）
        assertThat(policy.runtimeMaxRetries).isEqualTo(1);
        // 其他字段保留全局默认
        assertThat(policy.formatMaxRetries).isEqualTo(2);
        assertThat(policy.retryOnParseError).isTrue();
    }

    @Test
    void forHermes_keepsBooleansFromGlobal() {
        // 全局把所有 retryOn 都关掉，forHermes 应保留这些用户选择
        WorkflowProperties props = new WorkflowProperties();
        props.getLlm().getRetry().setRetryOnParseError(false);
        props.getLlm().getRetry().setRetryOnMissingRequired(false);
        props.getLlm().getRetry().setRetryOnTypeMismatch(false);
        props.getLlm().getRetry().setRetryOnKeyNameMismatch(false);

        RetryPolicy policy = RetryPolicy.forHermes(data("{}"), props);

        assertThat(policy.runtimeMaxRetries).isEqualTo(1);
        assertThat(policy.retryOnParseError).isFalse();
        assertThat(policy.retryOnMissingRequired).isFalse();
        assertThat(policy.retryOnTypeMismatch).isFalse();
        assertThat(policy.retryOnKeyNameMismatch).isFalse();
    }
}
