package com.cameleer.server.app.controller;

import com.cameleer.server.app.AbstractPostgresIT;
import com.cameleer.server.app.TestSecurityHelper;
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.web.client.TestRestTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

import java.util.Map;

import static java.util.concurrent.TimeUnit.SECONDS;
import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;

class MetricsControllerIT extends AbstractPostgresIT {

    @Autowired
    private TestRestTemplate restTemplate;

    @Autowired
    private TestSecurityHelper securityHelper;

    private final ObjectMapper objectMapper = new ObjectMapper();

    private HttpHeaders authHeaders;
    private HttpHeaders viewerHeaders;
    private String agentId;

    @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));
        agentId = "test-agent-metrics-it";
        String jwt = securityHelper.registerTestAgent(agentId);
        authHeaders = securityHelper.authHeaders(jwt);
        viewerHeaders = securityHelper.authHeadersNoBody(securityHelper.viewerToken());
    }

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

    @Test
    void postMetrics_returns202() {
        String json = """
                [{
                  "instanceId": "agent-1",
                  "collectedAt": "2026-03-11T10:00:00Z",
                  "metricName": "cpu.usage",
                  "metricValue": 75.5,
                  "tags": {"host": "server-1"}
                }]
                """;

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

        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.ACCEPTED);
    }

    @Test
    void postMetrics_dataAppearsAfterFlush() {
        // Post fresh now-stamped metrics so the default 1h lookback window of
        // GET /agents/{id}/metrics sees them deterministically.
        java.time.Instant now = java.time.Instant.now();
        String json = """
                [{
                  "instanceId": "%s",
                  "collectedAt": "%s",
                  "metricName": "memory.used",
                  "metricValue": 1024.0,
                  "tags": {}
                }]
                """.formatted(agentId, now.toString());

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

        // agent_metrics lives in ClickHouse; drive the visibility check through
        // the env-scoped REST metrics endpoint, never through raw SQL.
        await().atMost(15, SECONDS).untilAsserted(() -> {
            ResponseEntity<String> r = restTemplate.exchange(
                    "/api/v1/environments/default/agents/" + agentId
                            + "/metrics?names=memory.used",
                    HttpMethod.GET,
                    new HttpEntity<>(viewerHeaders),
                    String.class);
            assertThat(r.getStatusCode()).isEqualTo(HttpStatus.OK);
            JsonNode body = objectMapper.readTree(r.getBody());
            JsonNode series = body.path("metrics").path("memory.used");
            assertThat(series.isArray()).isTrue();
            long nonZero = 0;
            for (JsonNode bucket : series) {
                if (bucket.get("value").asDouble() > 0) nonZero++;
            }
            assertThat(nonZero).isGreaterThanOrEqualTo(1);
        });
    }
}
