package com.ruoyi.app.mendian;

import com.ruoyi.app.utils.linepay.LinePayClient;
import com.ruoyi.app.utils.linepay.LinePayResponse;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.system.domain.PosStoreLinePay;
import com.ruoyi.system.domain.dto.StoreLinePayCredentialDto;
import com.ruoyi.system.service.IPosStoreLinePayService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.MockedStatic;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

class PosStoreLinePayControllerTest {

    private IPosStoreLinePayService credentialService;
    private LinePayClient linePayClient;
    private PosStoreLinePayController controller;
    private MockedStatic<MessageUtils> messages;

    @BeforeEach
    void setUp() {
        credentialService = mock(IPosStoreLinePayService.class);
        linePayClient = mock(LinePayClient.class);
        controller = new PosStoreLinePayController(credentialService, linePayClient, "SANDBOX");
        messages = mockStatic(MessageUtils.class);
        messages.when(() -> MessageUtils.message(any(String.class))).thenAnswer(
                invocation -> invocation.getArgument(0));
    }

    @org.junit.jupiter.api.AfterEach
    void tearDown() {
        messages.close();
    }

    @Test
    void expectedNoHistoryResponseSavesAndEnablesImmutableCredentialVersion() throws Exception {
        StoreLinePayCredentialDto dto = credential();
        when(linePayClient.retrieveByOrderId(any(), any())).thenReturn(
                new LinePayResponse(200, "1150", "No transaction history", null, null, "{}"));
        PosStoreLinePay saved = new PosStoreLinePay();
        saved.setId(33L);
        saved.setIsEnabled(1);
        when(credentialService.saveVerifiedCredential(eq(dto), eq("SANDBOX"),
                eq("1150"), eq("No transaction history"))).thenReturn(saved);

        AjaxResult result = controller.saveCredentials(dto);

        assertEquals(200, result.get(AjaxResult.CODE_TAG));
        ArgumentCaptor<com.ruoyi.app.utils.linepay.LinePayCredential> credentialCaptor =
                ArgumentCaptor.forClass(com.ruoyi.app.utils.linepay.LinePayCredential.class);
        verify(linePayClient).retrieveByOrderId(credentialCaptor.capture(), any());
        assertEquals("1234567890", credentialCaptor.getValue().channelId());
        assertEquals("plain-secret", credentialCaptor.getValue().channelSecret());
    }

    @Test
    void authenticationFailureDoesNotReplaceCurrentCredential() throws Exception {
        when(linePayClient.retrieveByOrderId(any(), any())).thenReturn(
                new LinePayResponse(401, "1104", "Invalid signature", null, null, "{}"));

        AjaxResult result = controller.saveCredentials(credential());

        assertEquals(500, result.get(AjaxResult.CODE_TAG));
        verify(credentialService, never()).saveVerifiedCredential(any(), any(), any(), any());
    }

    @Test
    void networkUnknownDoesNotReplaceCurrentCredential() throws Exception {
        when(linePayClient.retrieveByOrderId(any(), any())).thenThrow(new IllegalStateException("timeout"));

        AjaxResult result = controller.saveCredentials(credential());

        assertEquals(500, result.get(AjaxResult.CODE_TAG));
        verify(credentialService, never()).saveVerifiedCredential(any(), any(), any(), any());
    }

    @Test
    void nonEmptySuccessfulRetrieveIsNotAcceptedAsCredentialProbe() throws Exception {
        when(linePayClient.retrieveByOrderId(any(), any())).thenReturn(
                new LinePayResponse(200, "0000", "Success", null, null,
                        "{\"returnCode\":\"0000\",\"info\":[{\"transactionId\":\"1\"}]}"));

        AjaxResult result = controller.saveCredentials(credential());

        assertEquals(500, result.get(AjaxResult.CODE_TAG));
        verify(credentialService, never()).saveVerifiedCredential(any(), any(), any(), any());
    }

    private static StoreLinePayCredentialDto credential() {
        StoreLinePayCredentialDto dto = new StoreLinePayCredentialDto();
        dto.setStoreId(9L);
        dto.setChannelId("1234567890");
        dto.setChannelSecret("plain-secret");
        return dto;
    }
}
