package com.ruoyi.system.service.impl;

import com.ruoyi.system.domain.PosStoreLinePay;
import com.ruoyi.system.domain.dto.StoreLinePayCredentialDto;
import com.ruoyi.system.mapper.PosStoreLinePayMapper;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

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

@ExtendWith(MockitoExtension.class)
class PosStoreLinePayServiceImplTest {

    @Mock
    private PosStoreLinePayMapper credentialMapper;

    @InjectMocks
    private PosStoreLinePayServiceImpl credentialService;

    @Test
    void savingSameCurrentCredentialIsIdempotent() {
        StoreLinePayCredentialDto dto = credential(9L, "1234567890", "same-secret");
        PosStoreLinePay current = current(41L, 9L, 3, "1234567890", "same-secret");
        when(credentialMapper.lockStore(9L)).thenReturn(9L);
        when(credentialMapper.selectCurrentByStoreId(9L)).thenReturn(current);

        PosStoreLinePay saved = credentialService.saveVerifiedCredential(
                dto, "SANDBOX", "1150", "No transaction history");

        assertSame(current, saved);
        verify(credentialMapper, never()).insert(any(PosStoreLinePay.class));
        verify(credentialMapper, never()).clearCurrent(any(), any(), any());
    }

    @Test
    void rotatingCredentialCreatesNewVersionWithoutOverwritingOldRow() {
        StoreLinePayCredentialDto dto = credential(9L, "1234567890", "new-secret");
        PosStoreLinePay current = current(41L, 9L, 3, "1234567890", "old-secret");
        when(credentialMapper.lockStore(9L)).thenReturn(9L);
        when(credentialMapper.selectCurrentByStoreId(9L)).thenReturn(current);
        when(credentialMapper.selectMaxVersionByStoreId(9L)).thenReturn(3);
        when(credentialMapper.clearCurrent(any(), any(), any())).thenReturn(1);
        when(credentialMapper.insert(any(PosStoreLinePay.class))).thenAnswer(invocation -> {
            PosStoreLinePay row = invocation.getArgument(0);
            row.setId(42L);
            return 1;
        });

        PosStoreLinePay saved = credentialService.saveVerifiedCredential(
                dto, "SANDBOX", "1150", "No transaction history");

        ArgumentCaptor<PosStoreLinePay> captor = ArgumentCaptor.forClass(PosStoreLinePay.class);
        verify(credentialMapper).insert(captor.capture());
        assertEquals(4, captor.getValue().getCredentialVersion());
        assertEquals(9L, captor.getValue().getCurrentStoreId());
        assertEquals("old-secret", current.getChannelSecret());
        assertEquals(42L, saved.getId());
    }

    private static StoreLinePayCredentialDto credential(Long storeId, String channelId, String secret) {
        StoreLinePayCredentialDto dto = new StoreLinePayCredentialDto();
        dto.setStoreId(storeId);
        dto.setChannelId(channelId);
        dto.setChannelSecret(secret);
        return dto;
    }

    private static PosStoreLinePay current(Long id, Long storeId, int version,
                                           String channelId, String secret) {
        PosStoreLinePay row = new PosStoreLinePay();
        row.setId(id);
        row.setStoreId(storeId);
        row.setCredentialVersion(version);
        row.setChannelId(channelId);
        row.setChannelSecret(secret);
        row.setEnvironment("SANDBOX");
        row.setCredentialStatus("VERIFIED");
        row.setIsEnabled(1);
        row.setCurrentStoreId(storeId);
        return row;
    }
}

