package de.assecutor.votianlt.service;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import de.assecutor.votianlt.model.invoices.CustomerInvoice;
import de.assecutor.votianlt.model.invoices.CustomerInvoiceItem;
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.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
class UserInvoiceZugferdServiceTest {

    @Mock
    private PdfToolService pdfToolService;

    @Captor
    private ArgumentCaptor<PdfToolService.InvoiceMetadata> metadataCaptor;

    private UserInvoiceZugferdService service;

    @BeforeEach
    void setUp() {
        service = new UserInvoiceZugferdService(pdfToolService);
    }

    private CustomerInvoice fullyPopulatedInvoice() {
        CustomerInvoice invoice = new CustomerInvoice();
        invoice.setInvoiceNumber("RE-2026-0001");
        invoice.setInvoiceDate(LocalDate.of(2026, 7, 14));
        invoice.setDeliveryDate(LocalDate.of(2026, 7, 10));
        invoice.setPaymentDueDate(LocalDate.of(2026, 7, 28));
        invoice.setSenderName("Spedition Muster GmbH");
        invoice.setSenderAddress("Musterstraße 1");
        invoice.setSenderPostcode("20095");
        invoice.setSenderCity("Hamburg");
        invoice.setSenderVatId("DE123456789");
        invoice.setSenderEmail("info@muster.de");
        invoice.setSenderPhone("040 123456");
        invoice.setRecipientCompany("Kunde AG");
        invoice.setRecipientName("Erika Beispiel");
        invoice.setRecipientAddress("Beispielweg 2");
        invoice.setRecipientPostcode("10115");
        invoice.setRecipientCity("Berlin");
        invoice.setRecipientEmail("rechnung@kunde-ag.de");
        invoice.setVatRate(new BigDecimal("0.19"));
        invoice.setNetAmount(new BigDecimal("100.00"));
        invoice.setIban("DE02120300000000202051");
        invoice.setPaymentTerms("Zahlbar innerhalb von 14 Tagen");
        invoice.setItems(List.of(
                new CustomerInvoiceItem(new BigDecimal("12"), "km", "Transport", new BigDecimal("5.00"),
                        new BigDecimal("0.19")),
                new CustomerInvoiceItem(BigDecimal.ONE, "pauschal", "Grundpreis", new BigDecimal("40.00"),
                        new BigDecimal("0.19"))));
        invoice.setPdfData(new byte[] { 1, 2, 3 });
        return invoice;
    }

    @Test
    void signBuildsMetadataFromInvoice() throws Exception {
        CustomerInvoice invoice = fullyPopulatedInvoice();
        PdfToolService.SignedInvoice signed = new PdfToolService.SignedInvoice(new byte[] { 9 }, "re.zip", true);
        when(pdfToolService.signInvoice(eq(invoice.getPdfData()), any())).thenReturn(signed);

        PdfToolService.SignedInvoice result = service.sign(invoice);

        assertThat(result).isSameAs(signed);
        verify(pdfToolService).signInvoice(eq(invoice.getPdfData()), metadataCaptor.capture());
        PdfToolService.InvoiceMetadata metadata = metadataCaptor.getValue();
        assertThat(metadata.invoiceNumber()).isEqualTo("RE-2026-0001");
        assertThat(metadata.issueDate()).isEqualTo(LocalDate.of(2026, 7, 14));
        assertThat(metadata.deliveryDate()).isEqualTo(LocalDate.of(2026, 7, 10));
        assertThat(metadata.dueDate()).isEqualTo(LocalDate.of(2026, 7, 28));
        assertThat(metadata.currency()).isEqualTo("EUR");
        assertThat(metadata.iban()).isEqualTo("DE02120300000000202051");
        assertThat(metadata.sender().name()).isEqualTo("Spedition Muster GmbH");
        assertThat(metadata.sender().vatId()).isEqualTo("DE123456789");
        assertThat(metadata.recipient().name()).isEqualTo("Kunde AG");
        assertThat(metadata.recipient().zip()).isEqualTo("10115");
        assertThat(metadata.recipient().email()).isEqualTo("rechnung@kunde-ag.de");
        assertThat(metadata.items()).hasSize(2);
        assertThat(metadata.items().get(0).description()).isEqualTo("Transport");
        assertThat(metadata.items().get(0).quantity()).isEqualByComparingTo("12");
        assertThat(metadata.items().get(0).unitPriceNet()).isEqualByComparingTo("5.00");
        assertThat(metadata.items().get(0).vatPercent()).isEqualByComparingTo("19");
    }

    @Test
    void signFallsBackToSingleLineForLegacyInvoiceWithoutItems() throws Exception {
        CustomerInvoice invoice = fullyPopulatedInvoice();
        invoice.setItems(null);
        invoice.setDescription("Kurierfahrt Hamburg–Berlin");
        when(pdfToolService.signInvoice(any(), any()))
                .thenReturn(new PdfToolService.SignedInvoice(new byte[] { 9 }, "re.zip", true));

        service.sign(invoice);

        verify(pdfToolService).signInvoice(any(), metadataCaptor.capture());
        List<PdfToolService.LineItem> items = metadataCaptor.getValue().items();
        assertThat(items).hasSize(1);
        assertThat(items.get(0).description()).isEqualTo("Kurierfahrt Hamburg–Berlin");
        assertThat(items.get(0).quantity()).isEqualByComparingTo("1");
        assertThat(items.get(0).unitPriceNet()).isEqualByComparingTo("100.00");
    }

    @Test
    void signUsesDefaultDatesWhenMissing() throws Exception {
        CustomerInvoice invoice = fullyPopulatedInvoice();
        invoice.setDeliveryDate(null);
        invoice.setPaymentDueDate(null);
        when(pdfToolService.signInvoice(any(), any()))
                .thenReturn(new PdfToolService.SignedInvoice(new byte[] { 9 }, "re.zip", true));

        service.sign(invoice);

        verify(pdfToolService).signInvoice(any(), metadataCaptor.capture());
        PdfToolService.InvoiceMetadata metadata = metadataCaptor.getValue();
        assertThat(metadata.deliveryDate()).isEqualTo(invoice.getInvoiceDate());
        assertThat(metadata.dueDate()).isEqualTo(invoice.getInvoiceDate().plusDays(14));
    }

    @Test
    void signRejectsInvoiceWithoutPdf() {
        CustomerInvoice invoice = fullyPopulatedInvoice();
        invoice.setPdfData(null);

        assertThatThrownBy(() -> service.sign(invoice)).isInstanceOf(IllegalStateException.class)
                .hasMessageContaining("kein PDF");
    }

    @Test
    void signRejectsInvoiceWithoutParties() {
        CustomerInvoice invoice = fullyPopulatedInvoice();
        invoice.setSenderName(null);

        assertThatThrownBy(() -> service.sign(invoice)).isInstanceOf(IllegalStateException.class)
                .hasMessageContaining("Absender");
    }

    @Test
    void signRejectsInvoiceWithoutAnyAmount() {
        CustomerInvoice invoice = fullyPopulatedInvoice();
        invoice.setItems(null);
        invoice.setNetAmount(null);

        assertThatThrownBy(() -> service.sign(invoice)).isInstanceOf(IllegalStateException.class)
                .hasMessageContaining("Positionen");
    }
}
