package com.cameleer.server.core.alerting;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Map;

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

class AlertConditionJsonTest {

    private final ObjectMapper om = new ObjectMapper();

    @Test
    void roundtripRouteMetric() throws Exception {
        var c = new RouteMetricCondition(
            new AlertScope("orders", "route-1", null),
            RouteMetric.P99_LATENCY_MS, Comparator.GT, 2000.0, 300);
        String json = om.writeValueAsString((AlertCondition) c);
        AlertCondition parsed = om.readValue(json, AlertCondition.class);
        assertThat(parsed).isInstanceOf(RouteMetricCondition.class);
        assertThat(parsed.kind()).isEqualTo(ConditionKind.ROUTE_METRIC);
    }

    @Test
    void roundtripExchangeMatchPerExchange() throws Exception {
        var c = new ExchangeMatchCondition(
            new AlertScope("orders", null, null),
            new ExchangeMatchCondition.ExchangeFilter("FAILED", Map.of("type","payment")),
            FireMode.PER_EXCHANGE, null, null);
        String json = om.writeValueAsString((AlertCondition) c);
        AlertCondition parsed = om.readValue(json, AlertCondition.class);
        assertThat(parsed).isInstanceOf(ExchangeMatchCondition.class);
    }

    @Test
    void roundtripExchangeMatchCountInWindow() throws Exception {
        var c = new ExchangeMatchCondition(
            new AlertScope("orders", null, null),
            new ExchangeMatchCondition.ExchangeFilter("FAILED", Map.of()),
            FireMode.COUNT_IN_WINDOW, 5, 900);
        AlertCondition parsed = om.readValue(om.writeValueAsString((AlertCondition) c), AlertCondition.class);
        assertThat(((ExchangeMatchCondition) parsed).threshold()).isEqualTo(5);
    }

    @Test
    void exchangeMatchRejectsNullFireMode() {
        assertThatThrownBy(() -> new ExchangeMatchCondition(
                new AlertScope(null, null, null),
                new ExchangeMatchCondition.ExchangeFilter("FAILED", Map.of()),
                null, null, null))
            .isInstanceOf(IllegalArgumentException.class)
            .hasMessageContaining("fireMode");
    }

    @Test
    void exchangeMatchRejectsNullFireModeOnDeserialization() throws Exception {
        String json = """
            {
              "kind": "EXCHANGE_MATCH",
              "scope": {},
              "filter": {"status": "FAILED", "attributes": {}},
              "fireMode": null,
              "threshold": null,
              "windowSeconds": null
            }
            """;
        assertThatThrownBy(() -> om.readValue(json, AlertCondition.class))
            .hasRootCauseInstanceOf(IllegalArgumentException.class)
            .hasMessageContaining("fireMode");
    }

    @Test
    void roundtripAgentState() throws Exception {
        var c = new AgentStateCondition(new AlertScope("orders", null, null), "DEAD", 60);
        AlertCondition parsed = om.readValue(om.writeValueAsString((AlertCondition) c), AlertCondition.class);
        assertThat(parsed).isInstanceOf(AgentStateCondition.class);
    }

    @Test
    void roundtripDeploymentState() throws Exception {
        var c = new DeploymentStateCondition(new AlertScope("orders", null, null), List.of("FAILED","DEGRADED"));
        AlertCondition parsed = om.readValue(om.writeValueAsString((AlertCondition) c), AlertCondition.class);
        assertThat(parsed).isInstanceOf(DeploymentStateCondition.class);
    }

    @Test
    void roundtripLogPattern() throws Exception {
        var c = new LogPatternCondition(new AlertScope("orders", null, null),
            "ERROR", "TimeoutException", 5, 900);
        AlertCondition parsed = om.readValue(om.writeValueAsString((AlertCondition) c), AlertCondition.class);
        assertThat(parsed).isInstanceOf(LogPatternCondition.class);
    }

    @Test
    void roundtripJvmMetric() throws Exception {
        var c = new JvmMetricCondition(new AlertScope("orders", null, null),
            "heap_used_percent", AggregationOp.MAX, Comparator.GT, 90.0, 300);
        AlertCondition parsed = om.readValue(om.writeValueAsString((AlertCondition) c), AlertCondition.class);
        assertThat(parsed).isInstanceOf(JvmMetricCondition.class);
    }

    @Test
    void roundtripAgentLifecycle() throws Exception {
        var c = new AgentLifecycleCondition(
                new AlertScope("orders", null, null),
                List.of(AgentLifecycleEventType.WENT_DEAD, AgentLifecycleEventType.DEREGISTERED),
                300);
        AlertCondition parsed = om.readValue(om.writeValueAsString((AlertCondition) c), AlertCondition.class);
        assertThat(parsed).isInstanceOf(AgentLifecycleCondition.class);
        var alc = (AgentLifecycleCondition) parsed;
        assertThat(alc.eventTypes()).containsExactly(
                AgentLifecycleEventType.WENT_DEAD, AgentLifecycleEventType.DEREGISTERED);
        assertThat(alc.withinSeconds()).isEqualTo(300);
        assertThat(alc.kind()).isEqualTo(ConditionKind.AGENT_LIFECYCLE);
    }

    @Test
    void agentLifecycleRejectsEmptyEventTypes() {
        assertThatThrownBy(() -> new AgentLifecycleCondition(
                new AlertScope(null, null, null), List.of(), 60))
            .isInstanceOf(IllegalArgumentException.class)
            .hasMessageContaining("eventTypes");
    }

    @Test
    void agentLifecycleRejectsZeroWindow() {
        assertThatThrownBy(() -> new AgentLifecycleCondition(
                new AlertScope(null, null, null),
                List.of(AgentLifecycleEventType.WENT_DEAD), 0))
            .isInstanceOf(IllegalArgumentException.class)
            .hasMessageContaining("withinSeconds");
    }

    @Test
    void agentLifecycleRejectsUnknownEventTypeOnDeserialization() {
        String json = """
            {
              "kind": "AGENT_LIFECYCLE",
              "scope": {},
              "eventTypes": ["REGISTERED", "BOGUS_EVENT"],
              "withinSeconds": 60
            }
            """;
        assertThatThrownBy(() -> om.readValue(json, AlertCondition.class))
            .hasMessageContaining("BOGUS_EVENT");
    }
}
