package ai.sentryagent.idp;

import ai.sentryagent.idp.models.Agent;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.net.http.HttpClient;
import java.time.Duration;
import java.util.concurrent.atomic.AtomicInteger;

import static org.junit.jupiter.api.Assertions.*;

class AgentIdPClientTest {

    private MockServer srv;

    private static final String TOKEN_BODY =
        "{\"access_token\":\"integration-token\",\"token_type\":\"Bearer\",\"expires_in\":3600,\"scope\":\"agents:read agents:write tokens:read audit:read\"}";

    private static final String AGENT_JSON =
        "{\"agentId\":\"uuid-1\",\"email\":\"a@b.ai\",\"agentType\":\"screener\",\"version\":\"1.0.0\"," +
        "\"capabilities\":[\"read\"],\"owner\":\"team\",\"deploymentEnv\":\"production\"," +
        "\"status\":\"active\",\"createdAt\":\"2026-01-01T00:00:00Z\",\"updatedAt\":\"2026-01-01T00:00:00Z\"}";

    @BeforeEach
    void setUp() throws IOException {
        srv = new MockServer();
        // Register token endpoint for every test (each test gets a fresh MockServer)
        srv.addHandler("/api/v1/token", 200, TOKEN_BODY);
    }

    @AfterEach
    void tearDown() { srv.stop(); }

    private AgentIdPClient makeClient() {
        HttpClient httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(5)).build();
        return new AgentIdPClient(srv.baseUrl(), "cid", "secret", "agents:read", httpClient);
    }

    @Test
    void getAgent_endToEnd() {
        srv.addHandler("/api/v1/agents/uuid-1", 200, AGENT_JSON);
        AgentIdPClient client = makeClient();
        Agent agent = client.agents().getAgent("uuid-1");
        assertEquals("uuid-1", agent.getAgentId());
        assertEquals("screener", agent.getAgentType());
    }

    @Test
    void serviceClients_areAccessible() {
        AgentIdPClient client = makeClient();
        assertNotNull(client.agents());
        assertNotNull(client.credentials());
        assertNotNull(client.tokens());
        assertNotNull(client.audit());
    }

    @Test
    void clearTokenCache_forcesRefetch() throws IOException {
        // Dedicated MockServer so we control the token counter from scratch
        MockServer dedicated = new MockServer();
        AtomicInteger tokenCalls = new AtomicInteger(0);
        dedicated.addHandler("/api/v1/token", exchange -> {
            tokenCalls.incrementAndGet();
            try {
                byte[] body = TOKEN_BODY.getBytes();
                exchange.getResponseHeaders().set("Content-Type", "application/json");
                exchange.sendResponseHeaders(200, body.length);
                exchange.getResponseBody().write(body);
                exchange.getResponseBody().close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });
        dedicated.addHandler("/api/v1/agents/uuid-1", 200, AGENT_JSON);

        try {
            HttpClient httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(5)).build();
            AgentIdPClient client = new AgentIdPClient(dedicated.baseUrl(), "cid", "secret", "agents:read", httpClient);
            client.agents().getAgent("uuid-1");
            client.clearTokenCache();
            client.agents().getAgent("uuid-1");
            assertEquals(2, tokenCalls.get(), "Token should be refetched after clearTokenCache");
        } finally {
            dedicated.stop();
        }
    }

    @Test
    void defaultScope_containsAllFourScopes() throws IOException {
        MockServer dedicated = new MockServer();
        StringBuilder capturedBody = new StringBuilder();
        dedicated.addHandler("/api/v1/token", exchange -> {
            try {
                String body = new String(exchange.getRequestBody().readAllBytes());
                capturedBody.append(body);
                byte[] resp = TOKEN_BODY.getBytes();
                exchange.getResponseHeaders().set("Content-Type", "application/json");
                exchange.sendResponseHeaders(200, resp.length);
                exchange.getResponseBody().write(resp);
                exchange.getResponseBody().close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        });
        dedicated.addHandler("/api/v1/agents/uuid-1", 200, AGENT_JSON);

        try {
            HttpClient httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(5)).build();
            // Two-arg constructor → default scope applied
            AgentIdPClient client = new AgentIdPClient(dedicated.baseUrl(), "cid", "secret",
                "agents:read agents:write tokens:read audit:read", httpClient);
            client.agents().getAgent("uuid-1");
            String captured = capturedBody.toString();
            assertTrue(captured.contains("agents"), "Scope should be present in token request body: " + captured);
        } finally {
            dedicated.stop();
        }
    }
}
