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.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.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

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

class AgentRegistrationControllerIT extends AbstractPostgresIT {

    @Autowired
    private TestRestTemplate restTemplate;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private TestSecurityHelper securityHelper;

    private String jwt;
    private String viewerJwt;

    @BeforeEach
    void setUp() {
        jwt = securityHelper.registerTestAgent("test-agent-registration-it");
        viewerJwt = securityHelper.viewerToken();
    }

    private ResponseEntity<String> registerAgent(String agentId, String name) {
        String json = """
                {
                  "instanceId": "%s",
                  "applicationId": "test-group",
                  "version": "1.0.0",
                  "routeIds": ["route-1", "route-2"],
                  "capabilities": {"tracing": true}
                }
                """.formatted(agentId);

        return restTemplate.postForEntity(
                "/api/v1/agents/register",
                new HttpEntity<>(json, securityHelper.bootstrapHeaders()),
                String.class);
    }

    @Test
    void registerNewAgent_returns200WithAgentIdAndSseEndpoint() throws Exception {
        ResponseEntity<String> response = registerAgent("agent-it-1", "IT Agent 1");

        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);

        JsonNode body = objectMapper.readTree(response.getBody());
        assertThat(body.get("instanceId").asText()).isEqualTo("agent-it-1");
        assertThat(body.get("sseEndpoint").asText()).isEqualTo("/api/v1/agents/agent-it-1/events");
        assertThat(body.get("heartbeatIntervalMs").asLong()).isGreaterThan(0);
        assertThat(body.has("serverPublicKey")).isTrue();
        assertThat(body.get("serverPublicKey").asText()).isNotEmpty();
        assertThat(body.has("accessToken")).isTrue();
        assertThat(body.get("accessToken").asText()).isNotEmpty();
        assertThat(body.has("refreshToken")).isTrue();
        assertThat(body.get("refreshToken").asText()).isNotEmpty();
    }

    @Test
    void reRegisterSameAgent_returns200WithLiveState() throws Exception {
        registerAgent("agent-it-reregister", "First Registration");

        ResponseEntity<String> response = registerAgent("agent-it-reregister", "Second Registration");

        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);

        JsonNode body = objectMapper.readTree(response.getBody());
        assertThat(body.get("instanceId").asText()).isEqualTo("agent-it-reregister");
    }

    @Test
    void heartbeatKnownAgent_returns200() {
        registerAgent("agent-it-hb", "HB Agent");

        ResponseEntity<Void> response = restTemplate.exchange(
                "/api/v1/agents/agent-it-hb/heartbeat",
                HttpMethod.POST,
                new HttpEntity<>(securityHelper.authHeadersNoBody(jwt)),
                Void.class);

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

    @Test
    void heartbeatUnknownAgent_returns404() {
        ResponseEntity<Void> response = restTemplate.exchange(
                "/api/v1/agents/unknown-agent-xyz/heartbeat",
                HttpMethod.POST,
                new HttpEntity<>(securityHelper.authHeadersNoBody(jwt)),
                Void.class);

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

    @Test
    void listAllAgents_returnsBothAgents() throws Exception {
        registerAgent("agent-it-list-1", "List Agent 1");
        registerAgent("agent-it-list-2", "List Agent 2");

        ResponseEntity<String> response = restTemplate.exchange(
                "/api/v1/agents",
                HttpMethod.GET,
                new HttpEntity<>(securityHelper.authHeadersNoBody(viewerJwt)),
                String.class);

        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);

        JsonNode body = objectMapper.readTree(response.getBody());
        assertThat(body.isArray()).isTrue();
        assertThat(body.size()).isGreaterThanOrEqualTo(2);
    }

    @Test
    void listAgentsByStatus_filtersCorrectly() throws Exception {
        registerAgent("agent-it-filter", "Filter Agent");

        ResponseEntity<String> response = restTemplate.exchange(
                "/api/v1/agents?status=LIVE",
                HttpMethod.GET,
                new HttpEntity<>(securityHelper.authHeadersNoBody(viewerJwt)),
                String.class);

        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);

        JsonNode body = objectMapper.readTree(response.getBody());
        assertThat(body.isArray()).isTrue();
        for (JsonNode agent : body) {
            assertThat(agent.get("status").asText()).isEqualTo("LIVE");
        }
    }

    @Test
    void listAgentsWithInvalidStatus_returns400() {
        ResponseEntity<String> response = restTemplate.exchange(
                "/api/v1/agents?status=INVALID",
                HttpMethod.GET,
                new HttpEntity<>(securityHelper.authHeadersNoBody(viewerJwt)),
                String.class);

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