package com.vaessl.app.sync;

import static com.vaessl.app.shared.Endpoint.HOMEBOX_LOGIN;
import static com.vaessl.app.shared.Endpoint.HOMEBOX_QUERY_ALL_ITEMS;
import static com.vaessl.app.shared.Endpoint.LOGIN;
import static com.vaessl.app.shared.Endpoint.SYNC;
import static com.vaessl.app.Mockdata.MOCK_USER;
import static com.vaessl.app.Mockdata.MOCK_SERVICE_TYPE;
import static com.vaessl.app.Mockdata.MOCK_PASS;
import static com.vaessl.app.Mockdata.VALID_HOMEBOX_ALL_ITEMS_QUERY_RESPONSE;
import static com.vaessl.app.Mockdata.VALID_HOMEBOX_LOGIN_RESPONSE;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc;
import org.springframework.http.MediaType;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo;
import com.github.tomakehurst.wiremock.junit5.WireMockTest;
import jakarta.servlet.http.Cookie;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;

/**
 * Integration tests for {@code POST /api/sync}, verifying the session-gated contract against a
 * mocked Homebox backend (via WireMock) and a real Spring MVC dispatch chain (via {@link MockMvc}).
 */
@SpringBootTest
@AutoConfigureMockMvc
@WireMockTest
class SyncControllerTest {

    @Autowired
    MockMvc mockMvc;

    private static final String SYNC_PATH = SYNC.getValue();

    /**
     * Logs in against a stubbed Homebox instance, then performs a sync using the resulting session
     * cookie. Expects a {@code 204 No Content} response once the mocked item catalog has been paged
     * through and embedded.
     *
     * @param wm WireMock runtime info, injected by {@link WireMockTest}, used to point the client
     *        at the stub server
     */
    @Test
    void shouldReturn204NoContentOnSuccessfulSync(WireMockRuntimeInfo wm) throws Exception {

        WireMock.stubFor(WireMock.post(HOMEBOX_LOGIN.getValue())
                .willReturn(WireMock.okJson(VALID_HOMEBOX_LOGIN_RESPONSE)));

        WireMock.stubFor(WireMock.get(WireMock.urlPathEqualTo(HOMEBOX_QUERY_ALL_ITEMS.getValue()))
                .willReturn(WireMock.okJson(VALID_HOMEBOX_ALL_ITEMS_QUERY_RESPONSE)));

        MvcResult loginResult =
                mockMvc.perform(post(LOGIN.getValue()).contentType(MediaType.APPLICATION_JSON)
                        .content(connectionRequestBody(wm))).andExpect(status().isOk()).andReturn();

        Cookie sessionCookie = loginResult.getResponse().getCookie("SESSION");

        mockMvc.perform(post(SYNC_PATH).cookie(sessionCookie)
                .contentType(MediaType.APPLICATION_JSON).content(syncRequestBody(wm)))
                .andExpect(status().isNoContent());
    }

    /**
     * Calls {@code /api/sync} with no session cookie attached and expects {@code 401 Unauthorized},
     * confirming the endpoint is session-gated.
     *
     * @param wm WireMock runtime info, injected by {@link WireMockTest}, used only to build a
     *        well-formed request body
     */
    @Test
    void shouldReturn401UnauthorizedWhenSessionIsNull(WireMockRuntimeInfo wm) throws Exception {
        mockMvc.perform(post(SYNC_PATH).contentType(MediaType.APPLICATION_JSON)
                .content(syncRequestBody(wm))).andExpect(status().isUnauthorized());

    }

    private String connectionRequestBody(WireMockRuntimeInfo wm) {
        return """
                {
                    "appUrl": "%s",
                    "serviceType": "%s",
                    "username": "%s",
                    "password": "%s"
                }
                """.formatted(wm.getHttpBaseUrl(), MOCK_SERVICE_TYPE, MOCK_USER, MOCK_PASS);
    }

    private String syncRequestBody(WireMockRuntimeInfo wm) {
        return """
                {
                    "appUrl": "%s",
                    "serviceType": "%s",
                    "username": "%s"
                }
                """.formatted(wm.getHttpBaseUrl(), MOCK_SERVICE_TYPE, MOCK_USER);
    }
}
