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

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;

class SilenceMatcherServiceTest {

    private SilenceMatcherService service;

    private static final UUID RULE_ID = UUID.fromString("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa");
    private static final UUID ENV_ID  = UUID.fromString("bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb");
    private static final UUID INST_ID = UUID.fromString("cccccccc-cccc-cccc-cccc-cccccccccccc");

    @BeforeEach
    void setUp() {
        service = new SilenceMatcherService();
    }

    // ---- helpers ----

    private AlertInstance instance() {
        return new AlertInstance(
                INST_ID, RULE_ID, Map.of(), ENV_ID,
                AlertState.FIRING, AlertSeverity.WARNING,
                Instant.now(), null, null, null, null, null, null,
                false, 1.5, 1.0,
                Map.of(), "title", "msg",
                List.of(), List.of(), List.of()
        );
    }

    private AlertRule ruleWithScope(String appSlug, String routeId, String agentId) {
        var scope     = new AlertScope(appSlug, routeId, agentId);
        var condition = new RouteMetricCondition(scope, RouteMetric.ERROR_RATE, Comparator.GT, 0.1, 60);
        return new AlertRule(
                RULE_ID, ENV_ID, "Test rule", null,
                AlertSeverity.WARNING, true,
                ConditionKind.ROUTE_METRIC, condition,
                60, 0, 0, "t", "m",
                List.of(), List.of(),
                Instant.now(), null, Instant.now(),
                Map.of(), Instant.now(), "admin", Instant.now(), "admin"
        );
    }

    // ---- wildcard matcher ----

    @Test
    void wildcardMatcher_matchesAnyInstance() {
        var matcher = new SilenceMatcher(null, null, null, null, null);
        assertThat(service.matches(matcher, instance(), ruleWithScope("my-app", "r1", null))).isTrue();
    }

    @Test
    void wildcardMatcher_matchesWithNullRule() {
        var matcher = new SilenceMatcher(null, null, null, null, null);
        assertThat(service.matches(matcher, instance(), null)).isTrue();
    }

    // ---- ruleId constraint ----

    @Test
    void ruleIdMatcher_matchesWhenEqual() {
        var matcher = new SilenceMatcher(RULE_ID, null, null, null, null);
        assertThat(service.matches(matcher, instance(), ruleWithScope(null, null, null))).isTrue();
    }

    @Test
    void ruleIdMatcher_rejectsWhenDifferent() {
        var matcher = new SilenceMatcher(UUID.randomUUID(), null, null, null, null);
        assertThat(service.matches(matcher, instance(), ruleWithScope(null, null, null))).isFalse();
    }

    @Test
    void ruleIdMatcher_withNullInstanceRuleId_rejects() {
        // Instance where rule was deleted (ruleId = null)
        var inst = new AlertInstance(
                INST_ID, null, Map.of(), ENV_ID,
                AlertState.FIRING, AlertSeverity.WARNING,
                Instant.now(), null, null, null, null, null, null,
                false, null, null,
                Map.of(), "t", "m",
                List.of(), List.of(), List.of()
        );
        var matcher = new SilenceMatcher(RULE_ID, null, null, null, null);
        assertThat(service.matches(matcher, inst, null)).isFalse();
    }

    @Test
    void ruleIdNull_withNullInstanceRuleId_wildcardStillMatches() {
        var inst = new AlertInstance(
                INST_ID, null, Map.of(), ENV_ID,
                AlertState.FIRING, AlertSeverity.WARNING,
                Instant.now(), null, null, null, null, null, null,
                false, null, null,
                Map.of(), "t", "m",
                List.of(), List.of(), List.of()
        );
        var matcher = new SilenceMatcher(null, null, null, null, null);
        // Wildcard ruleId + null rule — scope constraints not needed — should match.
        assertThat(service.matches(matcher, inst, null)).isTrue();
    }

    // ---- severity constraint ----

    @Test
    void severityMatcher_matchesWhenEqual() {
        var matcher = new SilenceMatcher(null, null, null, null, AlertSeverity.WARNING);
        assertThat(service.matches(matcher, instance(), ruleWithScope(null, null, null))).isTrue();
    }

    @Test
    void severityMatcher_rejectsWhenDifferent() {
        var matcher = new SilenceMatcher(null, null, null, null, AlertSeverity.CRITICAL);
        assertThat(service.matches(matcher, instance(), ruleWithScope(null, null, null))).isFalse();
    }

    // ---- appSlug constraint ----

    @Test
    void appSlugMatcher_matchesWhenEqual() {
        var matcher = new SilenceMatcher(null, "my-app", null, null, null);
        assertThat(service.matches(matcher, instance(), ruleWithScope("my-app", null, null))).isTrue();
    }

    @Test
    void appSlugMatcher_rejectsWhenDifferent() {
        var matcher = new SilenceMatcher(null, "other-app", null, null, null);
        assertThat(service.matches(matcher, instance(), ruleWithScope("my-app", null, null))).isFalse();
    }

    @Test
    void appSlugMatcher_rejectsWhenRuleIsNull() {
        var matcher = new SilenceMatcher(null, "my-app", null, null, null);
        assertThat(service.matches(matcher, instance(), null)).isFalse();
    }

    // ---- routeId constraint ----

    @Test
    void routeIdMatcher_matchesWhenEqual() {
        var matcher = new SilenceMatcher(null, null, "route-1", null, null);
        assertThat(service.matches(matcher, instance(), ruleWithScope(null, "route-1", null))).isTrue();
    }

    @Test
    void routeIdMatcher_rejectsWhenDifferent() {
        var matcher = new SilenceMatcher(null, null, "route-99", null, null);
        assertThat(service.matches(matcher, instance(), ruleWithScope(null, "route-1", null))).isFalse();
    }

    // ---- agentId constraint ----

    @Test
    void agentIdMatcher_matchesWhenEqual() {
        var matcher = new SilenceMatcher(null, null, null, "agent-7", null);
        assertThat(service.matches(matcher, instance(), ruleWithScope(null, null, "agent-7"))).isTrue();
    }

    @Test
    void agentIdMatcher_rejectsWhenDifferent() {
        var matcher = new SilenceMatcher(null, null, null, "agent-99", null);
        assertThat(service.matches(matcher, instance(), ruleWithScope(null, null, "agent-7"))).isFalse();
    }

    // ---- AND semantics: multiple fields ----

    @Test
    void multipleFields_allMustMatch() {
        var matcher = new SilenceMatcher(RULE_ID, "my-app", "route-1", null, AlertSeverity.WARNING);
        assertThat(service.matches(matcher, instance(), ruleWithScope("my-app", "route-1", null))).isTrue();
    }

    @Test
    void multipleFields_failsWhenOneDoesNotMatch() {
        // severity mismatch while everything else matches
        var matcher = new SilenceMatcher(RULE_ID, "my-app", "route-1", null, AlertSeverity.CRITICAL);
        assertThat(service.matches(matcher, instance(), ruleWithScope("my-app", "route-1", null))).isFalse();
    }
}
