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

import com.cameleer.server.core.alerting.*;
import com.cameleer.server.core.runtime.*;
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.Optional;
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 DeploymentStateEvaluatorTest {

    private AppRepository appRepo;
    private DeploymentRepository deploymentRepo;
    private DeploymentStateEvaluator 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 UUID APP_ID  = UUID.fromString("cccccccc-cccc-cccc-cccc-cccccccccccc");
    private static final UUID DEP_ID  = UUID.fromString("dddddddd-dddd-dddd-dddd-dddddddddddd");
    private static final Instant NOW  = Instant.parse("2026-04-19T10:00:00Z");

    @BeforeEach
    void setUp() {
        appRepo        = mock(AppRepository.class);
        deploymentRepo = mock(DeploymentRepository.class);
        eval = new DeploymentStateEvaluator(appRepo, deploymentRepo);
    }

    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);
    }

    private App app(String slug) {
        return new App(APP_ID, ENV_ID, slug, "Orders", null, NOW.minusSeconds(3600), NOW.minusSeconds(3600));
    }

    private Deployment deployment(DeploymentStatus status) {
        return new Deployment(DEP_ID, APP_ID, UUID.randomUUID(), ENV_ID, status,
                null, null, List.of(), null, null, "orders-0", null,
                Map.of(), null, NOW.minusSeconds(60), null, NOW.minusSeconds(120), "test-user");
    }

    @Test
    void firesWhenDeploymentInWantedState() {
        var condition = new DeploymentStateCondition(new AlertScope("orders", null, null), List.of("FAILED"));
        var rule = ruleWith(condition);
        when(appRepo.findByEnvironmentIdAndSlug(ENV_ID, "orders")).thenReturn(Optional.of(app("orders")));
        when(deploymentRepo.findByAppId(APP_ID)).thenReturn(List.of(deployment(DeploymentStatus.FAILED)));

        EvalResult r = eval.evaluate(condition, rule, new EvalContext("default", NOW, new TickCache()));
        assertThat(r).isInstanceOf(EvalResult.Firing.class);
        assertThat(((EvalResult.Firing) r).currentValue()).isEqualTo(1.0);
    }

    @Test
    void clearWhenDeploymentNotInWantedState() {
        var condition = new DeploymentStateCondition(new AlertScope("orders", null, null), List.of("FAILED"));
        var rule = ruleWith(condition);
        when(appRepo.findByEnvironmentIdAndSlug(ENV_ID, "orders")).thenReturn(Optional.of(app("orders")));
        when(deploymentRepo.findByAppId(APP_ID)).thenReturn(List.of(deployment(DeploymentStatus.RUNNING)));

        EvalResult r = eval.evaluate(condition, rule, new EvalContext("default", NOW, new TickCache()));
        assertThat(r).isEqualTo(EvalResult.Clear.INSTANCE);
    }

    @Test
    void clearWhenAppNotFound() {
        var condition = new DeploymentStateCondition(new AlertScope("unknown-app", null, null), List.of("FAILED"));
        var rule = ruleWith(condition);
        when(appRepo.findByEnvironmentIdAndSlug(ENV_ID, "unknown-app")).thenReturn(Optional.empty());

        EvalResult r = eval.evaluate(condition, rule, new EvalContext("default", NOW, new TickCache()));
        assertThat(r).isEqualTo(EvalResult.Clear.INSTANCE);
    }

    @Test
    void clearWhenNoDeployments() {
        var condition = new DeploymentStateCondition(new AlertScope("orders", null, null), List.of("FAILED"));
        var rule = ruleWith(condition);
        when(appRepo.findByEnvironmentIdAndSlug(ENV_ID, "orders")).thenReturn(Optional.of(app("orders")));
        when(deploymentRepo.findByAppId(APP_ID)).thenReturn(List.of());

        EvalResult r = eval.evaluate(condition, rule, new EvalContext("default", NOW, new TickCache()));
        assertThat(r).isEqualTo(EvalResult.Clear.INSTANCE);
    }

    @Test
    void firesWhenMultipleWantedStates() {
        var condition = new DeploymentStateCondition(new AlertScope("orders", null, null), List.of("FAILED", "DEGRADED"));
        var rule = ruleWith(condition);
        when(appRepo.findByEnvironmentIdAndSlug(ENV_ID, "orders")).thenReturn(Optional.of(app("orders")));
        when(deploymentRepo.findByAppId(APP_ID)).thenReturn(List.of(deployment(DeploymentStatus.DEGRADED)));

        EvalResult r = eval.evaluate(condition, rule, new EvalContext("default", NOW, new TickCache()));
        assertThat(r).isInstanceOf(EvalResult.Firing.class);
    }

    @Test
    void kindIsDeploymentState() {
        assertThat(eval.kind()).isEqualTo(ConditionKind.DEPLOYMENT_STATE);
    }
}
