package com.cameleer.server.app.controller;

import com.cameleer.server.app.AbstractPostgresIT;
import com.cameleer.server.app.TestSecurityHelper;
import com.cameleer.server.core.ingestion.IngestionService;
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.web.client.TestRestTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.TestPropertySource;

import java.util.Map;

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

/**
 * Tests backpressure behavior when the metrics write buffer is full.
 * <p>
 * Execution and diagram ingestion are now synchronous (no buffers).
 * Only the metrics pipeline still uses a write buffer with backpressure.
 */
@TestPropertySource(properties = {
        // Property keys must match the IngestionConfig @ConfigurationProperties
        // prefix (cameleer.server.ingestion). MetricsFlushScheduler now binds
        // its flush interval via SpEL on IngestionConfig, so a single override
        // controls both the buffer config and the flush cadence.
        "cameleer.server.ingestion.buffercapacity=5",
        "cameleer.server.ingestion.batchsize=5",
        "cameleer.server.ingestion.flushintervalms=60000"
})
class BackpressureIT extends AbstractPostgresIT {

    @Autowired
    private TestRestTemplate restTemplate;

    @Autowired
    private IngestionService ingestionService;

    @Autowired
    private TestSecurityHelper securityHelper;

    private HttpHeaders authHeaders;

    @BeforeEach
    void setUp() {
        // Lift max_agents cap so this IT (which registers an agent) isn't gated by license
        // enforcement. Cap behaviour itself is exercised by AgentCapEnforcementIT.
        securityHelper.installSyntheticUnsignedLicense(Map.of("max_agents", 100));
        String jwt = securityHelper.registerTestAgent("test-agent-backpressure-it");
        authHeaders = securityHelper.authHeaders(jwt);
    }

    @AfterEach
    void tearDown() {
        securityHelper.clearTestLicense();
    }

    @Test
    void whenMetricsBufferFull_returns503WithRetryAfter() {
        // Fill the metrics buffer completely with a batch of 5
        String batchJson = """
                [
                  {"instanceId":"bp-agent","collectedAt":"2026-03-11T10:00:00Z","metricName":"test.metric","metricValue":1.0,"tags":{}},
                  {"instanceId":"bp-agent","collectedAt":"2026-03-11T10:00:01Z","metricName":"test.metric","metricValue":2.0,"tags":{}},
                  {"instanceId":"bp-agent","collectedAt":"2026-03-11T10:00:02Z","metricName":"test.metric","metricValue":3.0,"tags":{}},
                  {"instanceId":"bp-agent","collectedAt":"2026-03-11T10:00:03Z","metricName":"test.metric","metricValue":4.0,"tags":{}},
                  {"instanceId":"bp-agent","collectedAt":"2026-03-11T10:00:04Z","metricName":"test.metric","metricValue":5.0,"tags":{}}
                ]
                """;

        ResponseEntity<String> batchResponse = restTemplate.postForEntity(
                "/api/v1/data/metrics",
                new HttpEntity<>(batchJson, authHeaders),
                String.class);
        assertThat(batchResponse.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED);

        // Now buffer should be full -- next POST should get 503
        String overflowJson = """
                [{"instanceId":"bp-agent","collectedAt":"2026-03-11T10:00:05Z","metricName":"test.metric","metricValue":6.0,"tags":{}}]
                """;

        ResponseEntity<String> response = restTemplate.postForEntity(
                "/api/v1/data/metrics",
                new HttpEntity<>(overflowJson, authHeaders),
                String.class);

        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.SERVICE_UNAVAILABLE);
        assertThat(response.getHeaders().getFirst("Retry-After")).isNotNull();
    }

    @Test
    void executionIngestion_isSynchronous_returnsAccepted() {
        String json = """
                {
                  "exchangeId": "bp-sync-e",
                  "applicationId": "test-group",
                  "instanceId": "test-agent-backpressure-it",
                  "routeId": "bp-sync",
                  "status": "COMPLETED",
                  "startTime": "2026-03-11T10:00:00Z",
                  "endTime": "2026-03-11T10:00:00.100Z",
                  "durationMs": 100,
                  "chunkSeq": 0,
                  "final": true,
                  "processors": []
                }
                """;

        ResponseEntity<String> response = restTemplate.postForEntity(
                "/api/v1/data/executions",
                new HttpEntity<>(json, authHeaders),
                String.class);

        // Synchronous ingestion always returns 202 (no buffer to overflow)
        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED);
    }
}
