package com.mosquito.project.sdk;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.mosquito.project.dto.ApiResponse;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.net.http.HttpClient;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

class ApiClientTest {

    private TestHttpClient httpClient;
    private ApiClient client;
    private ObjectMapper objectMapper;

    @BeforeEach
    void setUp() {
        httpClient = new TestHttpClient();
        client = new ApiClient("http://localhost", "test-key");
        setHttpClient(client, httpClient.client());
        objectMapper = new ObjectMapper().registerModule(new JavaTimeModule());
    }

    @Test
    void get_shouldUnwrapData() throws Exception {
        Payload payload = new Payload();
        payload.setValue("ok");
        String json = objectMapper.writeValueAsString(ApiResponse.success(payload));
        httpClient.register("GET", "/ok", 200, json);

        Payload result = client.get("/ok", Payload.class);

        assertEquals("ok", result.getValue());
    }

    @Test
    void getList_shouldReturnEmptyWhenNullData() throws Exception {
        String json = objectMapper.writeValueAsString(ApiResponse.success(null));
        httpClient.register("GET", "/list", 200, json);

        List<Payload> result = client.getList("/list", Payload.class);

        assertTrue(result.isEmpty());
    }

    @Test
    void getStringAndBytes_shouldReturnBody() {
        httpClient.register("GET", "/text", 200, "hello");
        httpClient.register("GET", "/bytes", 200, new byte[]{1, 2, 3});

        assertEquals("hello", client.getString("/text"));
        assertArrayEquals(new byte[]{1, 2, 3}, client.getBytes("/bytes"));
    }

    @Test
    void postAndPut_shouldReturnPayload() throws Exception {
        Payload payload = new Payload();
        payload.setValue("posted");
        String json = objectMapper.writeValueAsString(ApiResponse.success(payload));
        httpClient.register("POST", "/post", 200, json);
        httpClient.register("PUT", "/put", 200, json);

        Payload postResult = client.post("/post", Map.of("name", "value"), Payload.class);
        Payload putResult = client.put("/put", Map.of("name", "value"), Payload.class);

        assertEquals("posted", postResult.getValue());
        assertEquals("posted", putResult.getValue());
    }

    @Test
    void delete_shouldThrow_whenStatusNotOk() {
        httpClient.register("DELETE", "/delete", 500, "fail");

        assertThrows(RuntimeException.class, () -> client.delete("/delete"));
    }

    @Test
    void get_shouldThrow_whenApiResponseCodeError() throws Exception {
        String json = objectMapper.writeValueAsString(ApiResponse.error(400, "bad"));
        httpClient.register("GET", "/error", 200, json);

        assertThrows(RuntimeException.class, () -> client.get("/error", Payload.class));
    }

    private static void setHttpClient(ApiClient apiClient, HttpClient httpClient) {
        try {
            Field field = ApiClient.class.getDeclaredField("httpClient");
            field.setAccessible(true);
            field.set(apiClient, httpClient);
        } catch (Exception e) {
            throw new RuntimeException("Failed to set HttpClient", e);
        }
    }

    static class Payload {
        private String value;

        public String getValue() { return value; }
        public void setValue(String value) { this.value = value; }
    }
}
