package com.ruoyi.app.pay;

import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.system.domain.PosOrderLinePayment;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.http.ResponseEntity;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.doThrow;

class LinePayControllerTest {
    private LinePayService linePayService;
    private LinePayAsyncService asyncService;
    private LinePayController controller;

    @BeforeEach
    void setUp() {
        linePayService = mock(LinePayService.class);
        asyncService = mock(LinePayAsyncService.class);
        controller = new LinePayController(linePayService, asyncService,
                new LinePayReturnPageRenderer());
    }

    @Test
    void confirmReturnsSecurePageWithoutWaitingForGateway() {
        PosOrderLinePayment payment = payment();
        when(linePayService.findByLineOrderId("LP-1", "TX-1")).thenReturn(payment);

        ResponseEntity<String> response = controller.confirm("LP-1", "TX-1");

        assertEquals(200, response.getStatusCode().value());
        assertEquals("no-store, no-cache, must-revalidate",
                response.getHeaders().getFirst("Cache-Control"));
        assertTrue(response.getHeaders().getFirst("Content-Security-Policy")
                .contains("frame-ancestors 'none'"));
        String csp = response.getHeaders().getFirst("Content-Security-Policy");
        assertTrue(!csp.contains("script-src 'unsafe-inline'"));
        String nonce = csp.substring(csp.indexOf("script-src 'nonce-") + 18,
                csp.indexOf("'; img-src"));
        assertTrue(response.getBody().contains("<script nonce=\"" + nonce + "\">"));
        assertTrue(response.getBody().contains("com.twanmsdyh.app://payment/result?orderId=DD-1"));
        verify(asyncService).reconcile(9L, "CONFIRM_RETURN");
    }

    @Test
    void cancelDoesNotWriteTerminalStatusAndOnlySchedulesReconcile() {
        PosOrderLinePayment payment = payment();
        when(linePayService.findByLineOrderId("LP-1", "TX-1")).thenReturn(payment);

        ResponseEntity<String> response = controller.cancel("LP-1", "TX-1");

        assertEquals(200, response.getStatusCode().value());
        assertEquals("WAITING_AUTH", payment.getStatus());
        verify(asyncService).reconcile(9L, "CANCEL_RETURN");
    }

    @Test
    void unknownReturnParametersStillRenderSafePage() {
        ResponseEntity<String> response = controller.cancel(null, null);

        assertEquals(200, response.getStatusCode().value());
        assertTrue(response.getBody().contains("Payment is being confirmed"));
    }

    @Test
    void saturatedExecutorStillReturnsTheSafetyPage() {
        PosOrderLinePayment payment = payment();
        when(linePayService.findByLineOrderId("LP-1", "TX-1")).thenReturn(payment);
        doThrow(new java.util.concurrent.RejectedExecutionException("full"))
                .when(asyncService).reconcile(9L, "CONFIRM_RETURN");

        ResponseEntity<String> response = controller.confirm("LP-1", "TX-1");

        assertEquals(200, response.getStatusCode().value());
        assertTrue(response.getBody().contains("Payment is being confirmed"));
    }

    private static PosOrderLinePayment payment() {
        PosOrderLinePayment payment = new PosOrderLinePayment();
        payment.setId(9L);
        payment.setDdId("DD-1");
        payment.setStatus("WAITING_AUTH");
        return payment;
    }
}
