package dev.lions.unionflow.server.payment.pispi;

import dev.lions.unionflow.server.api.payment.PaymentException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.lang.reflect.Field;
import java.time.Instant;

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

class PispiAuthTest {

    private PispiAuth auth;

    @BeforeEach
    void setUp() {
        auth = new PispiAuth();
        // clientId and clientSecret default to ""
    }

    @Test
    void getAccessToken_whenCacheValid_returnsCachedToken() throws Exception {
        Field tokenField = PispiAuth.class.getDeclaredField("cachedToken");
        Field expiryField = PispiAuth.class.getDeclaredField("cacheExpiry");
        tokenField.setAccessible(true);
        expiryField.setAccessible(true);
        tokenField.set(auth, "mock-token-123");
        expiryField.set(auth, Instant.now().plusSeconds(300));

        String token = auth.getAccessToken();

        assertThat(token).isEqualTo("mock-token-123");
    }

    @Test
    void getAccessToken_whenCacheExpired_attemptsNewToken() throws Exception {
        Field tokenField = PispiAuth.class.getDeclaredField("cachedToken");
        Field expiryField = PispiAuth.class.getDeclaredField("cacheExpiry");
        tokenField.setAccessible(true);
        expiryField.setAccessible(true);
        tokenField.set(auth, "old-token");
        expiryField.set(auth, Instant.now().minusSeconds(60)); // expired

        // With empty clientId/clientSecret, the HTTP call will fail
        // (connection refused or malformed URL) — wrapped as PaymentException
        assertThatThrownBy(() -> auth.getAccessToken())
                .isInstanceOf(PaymentException.class);
    }

    @Test
    void getAccessToken_whenNoCache_andCredentialsEmpty_throwsPaymentException() {
        // cachedToken is null (default), so it tries HTTP call which will fail
        assertThatThrownBy(() -> auth.getAccessToken())
                .isInstanceOf(PaymentException.class);
    }

    @Test
    void getAccessToken_whenCacheIsNullButExpiryFuture_attemptsNewToken() throws Exception {
        Field tokenField = PispiAuth.class.getDeclaredField("cachedToken");
        Field expiryField = PispiAuth.class.getDeclaredField("cacheExpiry");
        tokenField.setAccessible(true);
        expiryField.setAccessible(true);
        tokenField.set(auth, null); // null token
        expiryField.set(auth, Instant.now().plusSeconds(300));

        // null cachedToken means the condition `cachedToken != null` fails — goes to HTTP
        assertThatThrownBy(() -> auth.getAccessToken())
                .isInstanceOf(PaymentException.class);
    }

    @Test
    void getAccessToken_whenBaseUrlInvalid_throwsPaymentException() throws Exception {
        Field baseUrlField = PispiAuth.class.getDeclaredField("baseUrl");
        baseUrlField.setAccessible(true);
        baseUrlField.set(auth, "http://localhost:1"); // unreachable

        Field clientIdField = PispiAuth.class.getDeclaredField("clientId");
        clientIdField.setAccessible(true);
        clientIdField.set(auth, "test-client");

        Field clientSecretField = PispiAuth.class.getDeclaredField("clientSecret");
        clientSecretField.setAccessible(true);
        clientSecretField.set(auth, "test-secret");

        assertThatThrownBy(() -> auth.getAccessToken())
                .isInstanceOf(PaymentException.class)
                .hasMessageContaining("OAuth2");
    }
}
