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.net.http.HttpClient;
import java.time.ZonedDateTime;
import java.util.List;

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

class MosquitoClientTest {

    private TestHttpClient httpClient;
    private MosquitoClient client;
    private ObjectMapper objectMapper;

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

    @Test
    void client_shouldCallEndpointsAndParseResponses() throws Exception {
        ZonedDateTime start = ZonedDateTime.parse("2025-01-01T00:00:00Z");
        ZonedDateTime end = ZonedDateTime.parse("2025-01-02T00:00:00Z");

        MosquitoClient.Activity activity = new MosquitoClient.Activity();
        activity.setId(1L);
        activity.setName("Activity A");
        activity.setStartTime(start);
        activity.setEndTime(end);

        MosquitoClient.Activity updated = new MosquitoClient.Activity();
        updated.setId(1L);
        updated.setName("Activity B");
        updated.setStartTime(start);
        updated.setEndTime(end);

        MosquitoClient.DailyStats daily = new MosquitoClient.DailyStats();
        daily.setDate("2025-01-01");
        daily.setParticipants(5);
        daily.setShares(3);
        MosquitoClient.ActivityStats stats = new MosquitoClient.ActivityStats();
        stats.setTotalParticipants(5);
        stats.setTotalShares(3);
        stats.setDaily(List.of(daily));

        MosquitoClient.ShortenResponse shorten = new MosquitoClient.ShortenResponse();
        shorten.setCode("abc");
        shorten.setPath("/r/abc");
        shorten.setOriginalUrl("https://example.com");

        MosquitoClient.ShareMeta shareMeta = new MosquitoClient.ShareMeta();
        shareMeta.setTitle("title");
        shareMeta.setDescription("desc");
        shareMeta.setImage("img");
        shareMeta.setUrl("url");

        MosquitoClient.PosterConfig posterConfig = new MosquitoClient.PosterConfig();
        posterConfig.setTemplate("default");
        posterConfig.setImageUrl("/poster.png");
        posterConfig.setHtmlUrl("/poster.html");

        MosquitoClient.LeaderboardEntry entry = new MosquitoClient.LeaderboardEntry();
        entry.setUserId(7L);
        entry.setUserName("user");
        entry.setScore(100);

        MosquitoClient.RewardInfo reward = new MosquitoClient.RewardInfo();
        reward.setType("points");
        reward.setPoints(10);
        reward.setCreatedAt("2025-01-01T00:00:00Z");

        MosquitoClient.CreateApiKeyResponse createApiKeyResponse = new MosquitoClient.CreateApiKeyResponse();
        createApiKeyResponse.setApiKey("raw-key");

        MosquitoClient.RevealApiKeyResponse revealApiKeyResponse = new MosquitoClient.RevealApiKeyResponse();
        revealApiKeyResponse.setApiKey("revealed-key");
        revealApiKeyResponse.setMessage("one-time");

        httpClient.register("POST", "/api/v1/activities", 200, objectMapper.writeValueAsString(ApiResponse.success(activity)));
        httpClient.register("GET", "/api/v1/activities/1", 200, objectMapper.writeValueAsString(ApiResponse.success(activity)));
        httpClient.register("PUT", "/api/v1/activities/1", 200, objectMapper.writeValueAsString(ApiResponse.success(updated)));
        httpClient.register("GET", "/api/v1/activities/1/stats", 200, objectMapper.writeValueAsString(ApiResponse.success(stats)));
        httpClient.register("GET", "/api/v1/me/invitation-info", 200, objectMapper.writeValueAsString(ApiResponse.success(shorten)));
        httpClient.register("GET", "/api/v1/me/share-meta", 200, objectMapper.writeValueAsString(ApiResponse.success(shareMeta)));
        httpClient.register("GET", "/api/v1/me/poster/image", 200, new byte[]{9, 8, 7});
        httpClient.register("GET", "/api/v1/me/poster/html", 200, "<html>ok</html>");
        httpClient.register("GET", "/api/v1/me/poster/config", 200, objectMapper.writeValueAsString(ApiResponse.success(posterConfig)));
        httpClient.register("GET", "/api/v1/activities/1/leaderboard", 200, objectMapper.writeValueAsString(ApiResponse.success(List.of(entry))));
        httpClient.register("GET", "/api/v1/activities/1/leaderboard/export", 200, "csv-data");
        httpClient.register("GET", "/api/v1/me/rewards", 200, objectMapper.writeValueAsString(ApiResponse.success(List.of(reward))));
        httpClient.register("POST", "/api/v1/api-keys", 200, objectMapper.writeValueAsString(ApiResponse.success(createApiKeyResponse)));
        httpClient.register("GET", "/api/v1/api-keys/1/reveal", 200, objectMapper.writeValueAsString(ApiResponse.success(revealApiKeyResponse)));
        httpClient.register("DELETE", "/api/v1/api-keys/1", 204, "");
        httpClient.register("GET", "/actuator/health", 200, "ok");

        MosquitoClient.Activity created = client.createActivity("Activity A", start, end);
        MosquitoClient.Activity fetched = client.getActivity(1L);
        MosquitoClient.Activity changed = client.updateActivity(1L, "Activity B", end);
        MosquitoClient.ActivityStats fetchedStats = client.getActivityStats(1L);
        String shareUrl = client.getShareUrl(1L, 2L);
        MosquitoClient.ShareMeta meta = client.getShareMeta(1L, 2L);
        byte[] posterImage = client.getPosterImage(1L, 2L);
        String posterHtml = client.getPosterHtml(1L, 2L);
        MosquitoClient.PosterConfig config = client.getPosterConfig("default");
        List<MosquitoClient.LeaderboardEntry> leaderboard = client.getLeaderboard(1L);
        List<MosquitoClient.LeaderboardEntry> leaderboardPaged = client.getLeaderboard(1L, 1, 2);
        String csv = client.exportLeaderboardCsv(1L);
        String csvTop = client.exportLeaderboardCsv(1L, 5);
        List<MosquitoClient.RewardInfo> rewards = client.getUserRewards(1L, 2L);
        String apiKey = client.createApiKey(1L, "key");
        client.revokeApiKey(1L);
        String revealed = client.revealApiKey(1L);
        boolean healthy = client.isHealthy();

        assertEquals("Activity A", created.getName());
        assertEquals("Activity A", fetched.getName());
        assertEquals("Activity B", changed.getName());
        assertEquals(5, fetchedStats.getTotalParticipants());
        assertTrue(shareUrl.endsWith("/r/abc"));
        assertEquals("title", meta.getTitle());
        assertArrayEquals(new byte[]{9, 8, 7}, posterImage);
        assertEquals("<html>ok</html>", posterHtml);
        assertEquals("default", config.getTemplate());
        assertEquals(1, leaderboard.size());
        assertEquals(1, leaderboardPaged.size());
        assertEquals("csv-data", csv);
        assertEquals("csv-data", csvTop);
        assertEquals(1, rewards.size());
        assertEquals("raw-key", apiKey);
        assertEquals("revealed-key", revealed);
        assertTrue(healthy);
    }

    @Test
    void isHealthy_shouldReturnFalse_whenEndpointFails() {
        MosquitoClient unreachable = new MosquitoClient("http://localhost:1", "test-key");

        assertFalse(unreachable.isHealthy());
    }

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