package com.cameleer.server.app.alerting.eval;

import com.cameleer.server.core.agent.AgentInfo;
import com.cameleer.server.core.agent.AgentRegistryService;
import com.cameleer.server.core.agent.AgentState;
import com.cameleer.server.core.alerting.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

class AgentStateEvaluatorTest {

    private AgentRegistryService registry;
    private AgentStateEvaluator eval;

    private static final UUID ENV_ID  = UUID.fromString("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb");
    private static final UUID RULE_ID = UUID.fromString("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa");
    private static final Instant NOW  = Instant.parse("2026-04-19T10:00:00Z");

    @BeforeEach
    void setUp() {
        registry = mock(AgentRegistryService.class);
        eval = new AgentStateEvaluator(registry);
    }

    private AlertRule ruleWith(AlertCondition condition) {
        return new AlertRule(RULE_ID, ENV_ID, "test", null,
                AlertSeverity.WARNING, true, condition.kind(), condition,
                60, 0, 0, null, null, List.of(), List.of(),
                null, null, null, Map.of(), null, null, null, null);
    }

    @Test
    void firesWhenAgentInTargetStateForScope() {
        when(registry.findAll()).thenReturn(List.of(
                new AgentInfo("a1", "Agent1", "orders", ENV_ID.toString(), "1.0",
                        List.of(), Map.of(), AgentState.DEAD,
                        NOW.minusSeconds(200), NOW.minusSeconds(120), null)
        ));
        var condition = new AgentStateCondition(new AlertScope("orders", null, null), "DEAD", 60);
        var rule = ruleWith(condition);
        EvalResult r = eval.evaluate(condition, rule, new EvalContext("default", NOW, new TickCache()));
        assertThat(r).isInstanceOf(EvalResult.Firing.class);
        var firing = (EvalResult.Firing) r;
        assertThat(firing.currentValue()).isEqualTo(1.0);
    }

    @Test
    void clearWhenNoMatchingAgents() {
        when(registry.findAll()).thenReturn(List.of(
                new AgentInfo("a1", "Agent1", "orders", ENV_ID.toString(), "1.0",
                        List.of(), Map.of(), AgentState.LIVE,
                        NOW.minusSeconds(200), NOW.minusSeconds(10), null)
        ));
        var condition = new AgentStateCondition(new AlertScope("orders", null, null), "DEAD", 60);
        var rule = ruleWith(condition);
        EvalResult r = eval.evaluate(condition, rule, new EvalContext("default", NOW, new TickCache()));
        assertThat(r).isEqualTo(EvalResult.Clear.INSTANCE);
    }

    @Test
    void clearWhenAgentInStateBelowForSecondsCutoff() {
        // Agent is DEAD but only 30s ago — forSeconds=60 → not yet long enough
        when(registry.findAll()).thenReturn(List.of(
                new AgentInfo("a1", "Agent1", "orders", ENV_ID.toString(), "1.0",
                        List.of(), Map.of(), AgentState.DEAD,
                        NOW.minusSeconds(200), NOW.minusSeconds(30), null)
        ));
        var condition = new AgentStateCondition(new AlertScope("orders", null, null), "DEAD", 60);
        var rule = ruleWith(condition);
        EvalResult r = eval.evaluate(condition, rule, new EvalContext("default", NOW, new TickCache()));
        assertThat(r).isEqualTo(EvalResult.Clear.INSTANCE);
    }

    @Test
    void kindIsAgentState() {
        assertThat(eval.kind()).isEqualTo(ConditionKind.AGENT_STATE);
    }

    @Test
    void scopeFilterByAgentId() {
        when(registry.findAll()).thenReturn(List.of(
                new AgentInfo("a1", "Agent1", "orders", ENV_ID.toString(), "1.0",
                        List.of(), Map.of(), AgentState.DEAD,
                        NOW.minusSeconds(200), NOW.minusSeconds(120), null),
                new AgentInfo("a2", "Agent2", "orders", ENV_ID.toString(), "1.0",
                        List.of(), Map.of(), AgentState.DEAD,
                        NOW.minusSeconds(200), NOW.minusSeconds(120), null)
        ));
        // filter to only a1
        var condition = new AgentStateCondition(new AlertScope("orders", null, "a1"), "DEAD", 60);
        EvalResult r = eval.evaluate(condition, ruleWith(condition), new EvalContext("default", NOW, new TickCache()));
        assertThat(r).isInstanceOf(EvalResult.Firing.class);
        assertThat(((EvalResult.Firing) r).currentValue()).isEqualTo(1.0);
    }
}
