package com.cameleer.server.app.alerting;

import com.cameleer.server.app.AbstractPostgresIT;
import com.cameleer.server.app.TestSecurityHelper;
import com.cameleer.server.app.search.ClickHouseLogStore;
import com.cameleer.server.core.agent.AgentRegistryService;
import com.cameleer.server.core.alerting.*;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.*;

import java.util.List;
import java.util.UUID;

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

/**
 * Verifies that alert instances from env-A are invisible from env-B's inbox endpoint.
 */
class AlertingEnvIsolationIT extends AbstractPostgresIT {

    @MockBean AgentRegistryService agentRegistryService;
    @MockBean(name = "clickHouseLogStore") ClickHouseLogStore clickHouseLogStore;

    @Autowired private TestRestTemplate restTemplate;
    @Autowired private TestSecurityHelper securityHelper;
    @Autowired private ObjectMapper objectMapper;
    @Autowired private AlertInstanceRepository instanceRepo;

    @Value("${cameleer.server.tenant.id:default}")
    private String tenantId;

    private String operatorJwt;
    private UUID envIdA;
    private UUID envIdB;
    private String envSlugA;
    private String envSlugB;

    @BeforeEach
    void setUp() {
        when(agentRegistryService.findAll()).thenReturn(List.of());

        operatorJwt = securityHelper.operatorToken();
        jdbcTemplate.update(
            "INSERT INTO users (user_id, provider, email) VALUES ('test-operator', 'test', 'op@test.lc') ON CONFLICT (user_id) DO NOTHING");

        envSlugA = "iso-env-a-" + UUID.randomUUID().toString().substring(0, 6);
        envSlugB = "iso-env-b-" + UUID.randomUUID().toString().substring(0, 6);
        envIdA = UUID.randomUUID();
        envIdB = UUID.randomUUID();

        jdbcTemplate.update("INSERT INTO environments (id, slug, display_name) VALUES (?, ?, ?)", envIdA, envSlugA, "ISO A");
        jdbcTemplate.update("INSERT INTO environments (id, slug, display_name) VALUES (?, ?, ?)", envIdB, envSlugB, "ISO B");
    }

    @AfterEach
    void cleanUp() {
        jdbcTemplate.update("DELETE FROM alert_notifications WHERE alert_instance_id IN (SELECT id FROM alert_instances WHERE environment_id IN (?, ?))", envIdA, envIdB);
        jdbcTemplate.update("DELETE FROM alert_instances WHERE environment_id IN (?, ?)", envIdA, envIdB);
        jdbcTemplate.update("DELETE FROM environments WHERE id IN (?, ?)", envIdA, envIdB);
        jdbcTemplate.update("DELETE FROM users WHERE user_id = 'test-operator'");
    }

    @Test
    void alertInEnvA_isInvisibleFromEnvB() throws Exception {
        // Seed a FIRING instance in env-A targeting the operator user
        UUID instanceA = seedFiringInstance(envIdA, "test-operator");

        // GET inbox for env-A — should see it
        ResponseEntity<String> respA = restTemplate.exchange(
            "/api/v1/environments/" + envSlugA + "/alerts",
            HttpMethod.GET,
            new HttpEntity<>(securityHelper.authHeadersNoBody(operatorJwt)),
            String.class);
        assertThat(respA.getStatusCode()).isEqualTo(HttpStatus.OK);
        JsonNode bodyA = objectMapper.readTree(respA.getBody());
        boolean foundInA = false;
        for (JsonNode node : bodyA) {
            if (instanceA.toString().equals(node.path("id").asText())) {
                foundInA = true;
            }
        }
        assertThat(foundInA).as("instance from env-A should appear in env-A inbox").isTrue();

        // GET inbox for env-B — should NOT see env-A's instance
        ResponseEntity<String> respB = restTemplate.exchange(
            "/api/v1/environments/" + envSlugB + "/alerts",
            HttpMethod.GET,
            new HttpEntity<>(securityHelper.authHeadersNoBody(operatorJwt)),
            String.class);
        assertThat(respB.getStatusCode()).isEqualTo(HttpStatus.OK);
        JsonNode bodyB = objectMapper.readTree(respB.getBody());
        for (JsonNode node : bodyB) {
            assertThat(node.path("id").asText())
                .as("env-A instance must not appear in env-B inbox")
                .isNotEqualTo(instanceA.toString());
        }
    }

    // ─────────────────────────────────────────────────────────────────────────

    private UUID seedFiringInstance(UUID envId, String userId) {
        UUID ruleId = UUID.randomUUID();
        jdbcTemplate.update(
            "INSERT INTO users (user_id, provider, email) VALUES (?, 'test', ?) ON CONFLICT (user_id) DO NOTHING",
            userId, userId + "@test.lc");
        jdbcTemplate.update("""
            INSERT INTO alert_rules
              (id, environment_id, name, severity, condition_kind, condition,
               notification_title_tmpl, notification_message_tmpl, created_by, updated_by)
            VALUES (?, ?, 'iso-rule', 'WARNING', 'AGENT_STATE', '{}'::jsonb, 't', 'm', ?, ?)
            """, ruleId, envId, userId, userId);

        UUID instanceId = UUID.randomUUID();
        jdbcTemplate.update("""
            INSERT INTO alert_instances
              (id, rule_id, rule_snapshot, environment_id, state, severity,
               fired_at, silenced, context, title, message,
               target_user_ids, target_group_ids, target_role_names)
            VALUES (?, ?, ?::jsonb, ?, 'FIRING'::alert_state_enum, 'WARNING'::severity_enum,
                    now(), false, '{}'::jsonb, 'T', 'M',
                    ARRAY[?]::text[], '{}'::uuid[], '{}'::text[])
            """,
            instanceId, ruleId,
            "{\"name\":\"iso-rule\",\"id\":\"" + ruleId + "\"}",
            envId, userId);
        return instanceId;
    }
}
