package cn.nianxx.thhotel.workflows.reservation.service.impl;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.catchThrowableOfType;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import cn.nianxx.thhotel.integrations.storage.aliyunoss.common.request.ObjectStoragePutRequest;
import cn.nianxx.thhotel.integrations.storage.aliyunoss.common.result.ObjectStoragePutResult;
import cn.nianxx.thhotel.integrations.storage.aliyunoss.service.ObjectStorageService;
import cn.nianxx.thhotel.platform.documentconversion.common.dto.ExcelToPdfConvertedDocument;
import cn.nianxx.thhotel.platform.documentconversion.service.ExcelToPdfConverter;
import cn.nianxx.thhotel.platform.hotel.service.HotelContextService;
import cn.nianxx.thhotel.platform.security.common.dto.AuthenticatedUserContext;
import cn.nianxx.thhotel.workflows.reservation.common.request.ReservationInvoiceBookingRequest;
import cn.nianxx.thhotel.workflows.reservation.common.request.ReservationInvoiceChargeRequest;
import cn.nianxx.thhotel.workflows.reservation.common.request.ReservationInvoiceDocumentRequest;
import cn.nianxx.thhotel.workflows.reservation.common.request.ReservationInvoiceManualGenerationRequest;
import cn.nianxx.thhotel.workflows.reservation.common.request.ReservationInvoicePayloadRequest;
import cn.nianxx.thhotel.workflows.reservation.common.request.ReservationInvoiceRecipientRequest;
import cn.nianxx.thhotel.workflows.reservation.repository.ReservationAiWorkflowRepository;
import cn.nianxx.thhotel.workflows.reservation.repository.ReservationInvoiceGenerationRepository;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.MediaType;

@ExtendWith(MockitoExtension.class)
class ReservationInvoiceGenerationServiceImplTest {

    @Mock
    private HotelContextService hotelContextService;
    @Mock
    private ReservationAiWorkflowRepository workflowRepository;
    @Mock
    private ReservationInvoiceGenerationRepository invoiceGenerationRepository;
    @Mock
    private ReservationInvoiceExcelTemplateRenderer templateRenderer;
    @Mock
    private ExcelToPdfConverter excelToPdfConverter;
    @Mock
    private ObjectStorageService objectStorageService;

    private ReservationInvoiceGenerationServiceImpl service;

    @BeforeEach
    void setUp() {
        service = new ReservationInvoiceGenerationServiceImpl(
                new ObjectMapper().findAndRegisterModules(),
                hotelContextService,
                workflowRepository,
                invoiceGenerationRepository,
                templateRenderer,
                excelToPdfConverter,
                objectStorageService);
    }

    @Test
    void shouldNotMarkGenerationSucceededBeforeAuditIsWritten() {
        when(hotelContextService.requireAccessibleHotel("HOTEL-TEST")).thenReturn("HOTEL-TEST");
        when(invoiceGenerationRepository.insertGeneration(any())).thenReturn(2080020000000000001L);
        when(templateRenderer.render(any())).thenReturn(new byte[]{0x50, 0x4B, 0x03, 0x04});
        when(objectStorageService.putObject(any())).thenAnswer(invocation -> {
            ObjectStoragePutRequest request = invocation.getArgument(0);
            return new ObjectStoragePutResult(
                    request.objectKey(),
                    "https://oss.example.test/" + request.objectKey(),
                    request.contentType(),
                    request.sizeBytes());
        });
        when(excelToPdfConverter.convert(any())).thenReturn(new ExcelToPdfConvertedDocument(
                "proforma-invoice.pdf",
                MediaType.APPLICATION_PDF_VALUE,
                8L,
                "%PDF-1.7".getBytes(),
                10L));
        when(workflowRepository.insertAuditLog(any())).thenThrow(new RuntimeException("audit insert failed"));

        ReservationInvoiceGenerationException exception = catchThrowableOfType(
                () -> service.generateManualInvoice(validRequest(), actor()),
                ReservationInvoiceGenerationException.class);

        assertThat(exception.getErrorCode()).isEqualTo("RESERVATION_INVOICE_GENERATION_FAILED");
        verify(invoiceGenerationRepository, never()).markSucceeded(
                anyLong(), any(), any(), any(), any(), any());
        verify(invoiceGenerationRepository).markFailed(
                anyLong(), any(), any(), any());
    }

    private AuthenticatedUserContext actor() {
        return new AuthenticatedUserContext(
                1L,
                "invoice-admin",
                "系统管理员",
                true,
                "HOTEL-TEST",
                List.of("HOTEL-TEST"),
                List.of("RESERVATION_INVOICE_GENERATE"));
    }

    private ReservationInvoiceManualGenerationRequest validRequest() {
        return new ReservationInvoiceManualGenerationRequest(
                "HOTEL-TEST",
                "MANUAL",
                null,
                null,
                "PROFORMA_INVOICE_V1",
                new ReservationInvoicePayloadRequest(
                        new ReservationInvoiceDocumentRequest(
                                LocalDate.of(2026, 7, 17),
                                LocalDate.of(2026, 7, 12),
                                LocalDate.of(2026, 7, 22)),
                        new ReservationInvoiceRecipientRequest(
                                "LIAN_TAI",
                                "LIAN_TAI_KHUN_ANN",
                                "LIAN TAI TRAVEL (THAILAND) CO., LTD.",
                                "Khun Ann",
                                "2/86 Rajpattana Road, Rajpattana, Sapansoong, Bangkok, TH, 10240",
                                "061-397-2675",
                                "op.liantaitravel@gmail.com"),
                        new ReservationInvoiceBookingRequest(
                                "GRP-DEMO-0802",
                                LocalDate.of(2026, 8, 2),
                                LocalDate.of(2026, 8, 5),
                                "includingBF",
                                new BigDecimal("1200")),
                        List.of(new ReservationInvoiceChargeRequest(
                                "GRP-DEMO-0802",
                                "Deluxe Room",
                                new BigDecimal("2"),
                                new BigDecimal("3000"),
                                new BigDecimal("3")))));
    }
}
