package com.ruoyi.app.pay;

import com.ruoyi.system.service.IPaymentGatewayLogService;
import com.ruoyi.system.domain.PaymentGatewayLog;
import com.ruoyi.app.utils.linepay.LinePayResponse;
import com.alibaba.fastjson2.JSONObject;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.verify;
import org.mockito.ArgumentCaptor;

class LinePayGatewayAuditServiceTest {

    @Test
    void auditFailureNeverChangesGatewayResultOrEscapes() throws Exception {
        IPaymentGatewayLogService logs = mock(IPaymentGatewayLogService.class);
        when(logs.append(any())).thenThrow(new IllegalStateException("log database unavailable"));
        LinePayGatewayAuditService audit = new LinePayGatewayAuditService(logs);

        String result = audit.execute("REQUEST", "APP", 1L, null, 5L,
                9L, "DD-1", "LP-1", "TX-1", () -> "gateway-result");

        assertEquals("gateway-result", result);
    }

    @Test
    void responseAuditRemovesPaymentTokenAndPaymentUrls() throws Exception {
        IPaymentGatewayLogService logs = mock(IPaymentGatewayLogService.class);
        LinePayGatewayAuditService audit = new LinePayGatewayAuditService(logs);
        String raw = "{\"returnCode\":\"0000\",\"info\":{\"paymentAccessToken\":\"token\","
                + "\"paymentUrl\":{\"web\":\"https://secret\",\"app\":\"line://secret\"}}}";
        LinePayResponse response = new LinePayResponse(200, "0000", "Success", null,
                JSONObject.parseObject(raw).getJSONObject("info"), raw);

        audit.execute("REQUEST", "APP", 1L, null, 5L,
                9L, "DD-1", "LP-1", null, () -> response);

        ArgumentCaptor<PaymentGatewayLog> captor = ArgumentCaptor.forClass(PaymentGatewayLog.class);
        verify(logs, org.mockito.Mockito.times(2)).append(captor.capture());
        String payload = captor.getAllValues().get(1).getPayload();
        assertEquals("{\"returnCode\":\"0000\",\"info\":{\"paymentUrl\":{}}}", payload);
    }

    @Test
    void offlineAuditNeverPersistsOneTimeKeyOnSuccessOrFailure() throws Exception {
        IPaymentGatewayLogService logs = mock(IPaymentGatewayLogService.class);
        LinePayGatewayAuditService audit = new LinePayGatewayAuditService(logs);
        String oneTimeKey = "123456789012345678";

        audit.executeOffline("APP", 1L, 5L, 9L, "DD-1", "LPOFF-1",
                () -> new LinePayResponse(200, "1299", oneTimeKey,
                        null, null, "{\"returnCode\":\"1299\",\"oneTimeKey\":\""
                        + oneTimeKey + "\"}"));
        try {
            audit.executeOffline("APP", 2L, 5L, 9L, "DD-2", "LPOFF-2",
                    () -> { throw new IllegalStateException("timeout"); });
        } catch (IllegalStateException expected) {
            // expected
        }

        ArgumentCaptor<PaymentGatewayLog> captor = ArgumentCaptor.forClass(PaymentGatewayLog.class);
        verify(logs, org.mockito.Mockito.times(4)).append(captor.capture());
        captor.getAllValues().forEach(log -> {
            assertFalse(String.valueOf(log.getPayload()).contains(oneTimeKey));
            assertFalse(String.valueOf(log.getReturnMessage()).contains(oneTimeKey));
        });
    }
}
