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.AlertNotification;
import com.cameleer.server.core.alerting.AlertNotificationRepository;
import com.cameleer.server.core.alerting.AlertSeverity;
import com.cameleer.server.core.alerting.AlertState;
import com.cameleer.server.core.alerting.NotificationStatus;
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 AlertNotificationControllerIT 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 AlertNotificationRepository notificationRepo;

    private String operatorJwt;
    private String viewerJwt;
    private String envSlug;
    private UUID envId;

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

        envSlug = "notif-env-" + UUID.randomUUID().toString().substring(0, 6);
        envId   = UUID.randomUUID();
        jdbcTemplate.update(
                "INSERT INTO environments (id, slug, display_name) VALUES (?, ?, ?) ON CONFLICT (id) DO NOTHING",
                envId, envSlug, envSlug);
    }

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

    @Test
    void listNotificationsForInstance() throws Exception {
        AlertInstance instance = seedInstance();
        AlertNotification notification = seedNotification(instance.id());

        ResponseEntity<String> resp = restTemplate.exchange(
                "/api/v1/environments/" + envSlug + "/alerts/" + instance.id() + "/notifications",
                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();
        assertThat(body.size()).isGreaterThanOrEqualTo(1);
    }

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

        ResponseEntity<String> resp = restTemplate.exchange(
                "/api/v1/environments/" + envSlug + "/alerts/" + instance.id() + "/notifications",
                HttpMethod.GET,
                new HttpEntity<>(securityHelper.authHeadersNoBody(viewerJwt)),
                String.class);

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

    @Test
    void retryNotification() throws Exception {
        AlertInstance instance = seedInstance();
        AlertNotification notification = seedNotification(instance.id());

        // Mark as failed first
        notificationRepo.markFailed(notification.id(), 500, "Internal Server Error");

        ResponseEntity<String> resp = restTemplate.exchange(
                "/api/v1/alerts/notifications/" + notification.id() + "/retry",
                HttpMethod.POST,
                new HttpEntity<>(securityHelper.authHeaders(operatorJwt)),
                String.class);

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

    @Test
    void retryResetsAttemptsToZero() throws Exception {
        // Verify Fix I-1: retry endpoint resets attempts to 0, not attempts+1
        AlertInstance instance = seedInstance();
        AlertNotification notification = seedNotification(instance.id());

        // Mark as failed with attempts at max (simulate exhausted retries)
        notificationRepo.markFailed(notification.id(), 500, "server error");
        notificationRepo.markFailed(notification.id(), 500, "server error");
        notificationRepo.markFailed(notification.id(), 500, "server error");

        // Verify attempts > 0 before retry
        AlertNotification before = notificationRepo.findById(notification.id()).orElseThrow();
        assertThat(before.attempts()).isGreaterThan(0);

        // Operator retries
        ResponseEntity<String> resp = restTemplate.exchange(
                "/api/v1/alerts/notifications/" + notification.id() + "/retry",
                HttpMethod.POST,
                new HttpEntity<>(securityHelper.authHeaders(operatorJwt)),
                String.class);
        assertThat(resp.getStatusCode()).isEqualTo(HttpStatus.OK);

        // After retry: attempts must be 0 and status PENDING (not attempts+1)
        AlertNotification after = notificationRepo.findById(notification.id()).orElseThrow();
        assertThat(after.attempts()).as("retry must reset attempts to 0").isEqualTo(0);
        assertThat(after.status()).isEqualTo(NotificationStatus.PENDING);
    }

    @Test
    void viewerCannotRetry() throws Exception {
        AlertInstance instance = seedInstance();
        AlertNotification notification = seedNotification(instance.id());

        ResponseEntity<String> resp = restTemplate.exchange(
                "/api/v1/alerts/notifications/" + notification.id() + "/retry",
                HttpMethod.POST,
                new HttpEntity<>(securityHelper.authHeaders(viewerJwt)),
                String.class);

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

    @Test
    void retryUnknownNotificationReturns404() {
        ResponseEntity<String> resp = restTemplate.exchange(
                "/api/v1/alerts/notifications/" + UUID.randomUUID() + "/retry",
                HttpMethod.POST,
                new HttpEntity<>(securityHelper.authHeaders(operatorJwt)),
                String.class);

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

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

    private AlertInstance seedInstance() {
        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(), List.of(), List.of("OPERATOR"));
        return instanceRepo.save(instance);
    }

    private AlertNotification seedNotification(UUID instanceId) {
        // webhookId is a local UUID (not FK-constrained), outboundConnectionId is null
        // (FK to outbound_connections ON DELETE SET NULL - null is valid)
        AlertNotification notification = new AlertNotification(
                UUID.randomUUID(), instanceId,
                UUID.randomUUID(), null,
                NotificationStatus.PENDING,
                0, Instant.now(),
                null, null,
                null, null,
                null, null, Instant.now());
        return notificationRepo.save(notification);
    }

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