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

import com.cameleer.server.app.AbstractPostgresIT;
import com.cameleer.server.app.TestSecurityHelper;
import com.cameleer.server.app.search.ClickHouseLogStore;
import com.cameleer.server.core.alerting.AlertInstance;
import com.cameleer.server.core.alerting.AlertInstanceRepository;
import com.cameleer.server.core.alerting.AlertReadRepository;
import com.cameleer.server.core.alerting.AlertSeverity;
import com.cameleer.server.core.alerting.AlertState;
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.boot.test.mock.mockito.MockBean;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

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

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

class AlertControllerIT extends AbstractPostgresIT {

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

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

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

    @BeforeEach
    void setUp() {
        operatorJwt = securityHelper.operatorToken();
        viewerJwt   = securityHelper.viewerToken();
        seedUser("test-operator");
        seedUser("test-viewer");

        envSlugA = "alert-env-a-" + UUID.randomUUID().toString().substring(0, 6);
        envSlugB = "alert-env-b-" + UUID.randomUUID().toString().substring(0, 6);
        envIdA = UUID.randomUUID();
        envIdB = UUID.randomUUID();
        jdbcTemplate.update(
                "INSERT INTO environments (id, slug, display_name) VALUES (?, ?, ?) ON CONFLICT (id) DO NOTHING",
                envIdA, envSlugA, envSlugA);
        jdbcTemplate.update(
                "INSERT INTO environments (id, slug, display_name) VALUES (?, ?, ?) ON CONFLICT (id) DO NOTHING",
                envIdB, envSlugB, envSlugB);
    }

    @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 IN ('test-operator','test-viewer')");
    }

    @Test
    void listReturnsAlertsForEnv() throws Exception {
        AlertInstance instance = seedInstance(envIdA);

        ResponseEntity<String> resp = restTemplate.exchange(
                "/api/v1/environments/" + envSlugA + "/alerts",
                HttpMethod.GET,
                new HttpEntity<>(securityHelper.authHeadersNoBody(operatorJwt)),
                String.class);

        assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
        JsonNode body = objectMapper.readTree(resp.getBody());
        assertThat(body.isArray()).isTrue();
        // The alert we seeded should be present
        boolean found = false;
        for (JsonNode node : body) {
            if (node.path("id").asText().equals(instance.id().toString())) {
                found = true;
                break;
            }
        }
        assertThat(found).as("seeded alert must appear in env-A inbox").isTrue();
    }

    @Test
    void envIsolation() throws Exception {
        // Seed an alert in env-A
        AlertInstance instanceA = seedInstance(envIdA);

        // env-B inbox should NOT see env-A's alert
        ResponseEntity<String> resp = restTemplate.exchange(
                "/api/v1/environments/" + envSlugB + "/alerts",
                HttpMethod.GET,
                new HttpEntity<>(securityHelper.authHeadersNoBody(operatorJwt)),
                String.class);

        assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
        JsonNode body = objectMapper.readTree(resp.getBody());
        for (JsonNode node : body) {
            assertThat(node.path("id").asText())
                    .as("env-A alert must not appear in env-B inbox")
                    .isNotEqualTo(instanceA.id().toString());
        }
    }

    @Test
    void unreadCountReturnsNumber() {
        ResponseEntity<String> resp = restTemplate.exchange(
                "/api/v1/environments/" + envSlugA + "/alerts/unread-count",
                HttpMethod.GET,
                new HttpEntity<>(securityHelper.authHeadersNoBody(operatorJwt)),
                String.class);
        assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
    }

    @Test
    void ackFlow() throws Exception {
        AlertInstance instance = seedInstance(envIdA);

        ResponseEntity<String> ack = restTemplate.exchange(
                "/api/v1/environments/" + envSlugA + "/alerts/" + instance.id() + "/ack",
                HttpMethod.POST,
                new HttpEntity<>(securityHelper.authHeaders(operatorJwt)),
                String.class);

        assertThat(ack.getStatusCode()).isEqualTo(HttpStatus.OK);
        JsonNode body = objectMapper.readTree(ack.getBody());
        assertThat(body.path("state").asText()).isEqualTo("FIRING");
    }

    @Test
    void readMarksSingleAlert() throws Exception {
        AlertInstance instance = seedInstance(envIdA);

        ResponseEntity<String> read = restTemplate.exchange(
                "/api/v1/environments/" + envSlugA + "/alerts/" + instance.id() + "/read",
                HttpMethod.POST,
                new HttpEntity<>(securityHelper.authHeaders(operatorJwt)),
                String.class);

        assertThat(read.getStatusCode()).isEqualTo(HttpStatus.OK);
    }

    @Test
    void bulkRead() throws Exception {
        AlertInstance i1 = seedInstance(envIdA);
        AlertInstance i2 = seedInstance(envIdA);

        String body = """
                {"instanceIds":["%s","%s"]}
                """.formatted(i1.id(), i2.id());

        ResponseEntity<String> resp = restTemplate.exchange(
                "/api/v1/environments/" + envSlugA + "/alerts/bulk-read",
                HttpMethod.POST,
                new HttpEntity<>(body, securityHelper.authHeaders(operatorJwt)),
                String.class);

        assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
    }

    @Test
    void viewerCanRead() {
        ResponseEntity<String> resp = restTemplate.exchange(
                "/api/v1/environments/" + envSlugA + "/alerts",
                HttpMethod.GET,
                new HttpEntity<>(securityHelper.authHeadersNoBody(viewerJwt)),
                String.class);
        assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);
    }

    // -------------------------------------------------------------------------
    // Helpers
    // -------------------------------------------------------------------------

    private AlertInstance seedInstance(UUID envId) {
        // target by userId so the inbox SQL (? = ANY(target_user_ids)) matches the test-operator JWT
        // (JWT subject is "user:test-operator", stripped to "test-operator" by currentUserId())
        AlertInstance instance = new AlertInstance(
                UUID.randomUUID(), null, null, envId,
                AlertState.FIRING, AlertSeverity.WARNING,
                Instant.now(), null, null, null, null, null, null, false,
                42.0, 1000.0, null, "Test alert", "Something happened",
                List.of("test-operator"), List.of(), List.of());
        return instanceRepo.save(instance);
    }

    private void seedUser(String userId) {
        jdbcTemplate.update(
                "INSERT INTO users (user_id, provider, email, display_name) VALUES (?, 'test', ?, ?) ON CONFLICT (user_id) DO NOTHING",
                userId, userId + "@example.com", userId);
    }
}
