package de.assecutor.votianlt.service;

import de.assecutor.votianlt.model.invoices.CustomerInvoice;
import de.assecutor.votianlt.model.invoices.CustomerInvoiceItem;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;

/**
 * Wandelt Nutzer-Rechnungen (Rechnungen der Systemnutzer an ihre eigenen
 * Kunden) über das externe PDF Tool in signierte ZUGFeRD-E-Rechnungen um —
 * dasselbe Verfahren, das {@code AdminCreateInvoicesView} für die
 * System-Rechnungen verwendet. Die EN16931-Metadaten werden vollständig aus
 * der persistierten {@link CustomerInvoice} aufgebaut (Absender, Empfänger,
 * Positionen), die beim Erstellen der Rechnung in {@code CreateInvoiceView}
 * befüllt werden.
 */
@Service
public class UserInvoiceZugferdService {

    private final PdfToolService pdfToolService;

    public UserInvoiceZugferdService(PdfToolService pdfToolService) {
        this.pdfToolService = pdfToolService;
    }

    /**
     * Signiert die Rechnung über das PDF Tool. Wirft eine
     * {@link IllegalStateException} mit verständlicher Meldung, wenn der
     * Rechnung Daten fehlen, die für eine EN16931-E-Rechnung zwingend sind.
     */
    public PdfToolService.SignedInvoice sign(CustomerInvoice invoice) throws Exception {
        if (invoice.getPdfData() == null || invoice.getPdfData().length == 0) {
            throw new IllegalStateException("Für diese Rechnung ist kein PDF gespeichert.");
        }
        return pdfToolService.signInvoice(invoice.getPdfData(), buildMetadata(invoice));
    }

    private PdfToolService.InvoiceMetadata buildMetadata(CustomerInvoice invoice) {
        if (isBlank(invoice.getSenderName()) || isBlank(invoice.getRecipientName())) {
            throw new IllegalStateException(
                    "Absender- oder Empfängerdaten fehlen — diese Rechnung wurde vor der E-Rechnungs-Umstellung "
                            + "erstellt und kann nicht als E-Rechnung aufbereitet werden.");
        }

        PdfToolService.Party sender = new PdfToolService.Party(invoice.getSenderName(),
                safe(invoice.getSenderAddress()), safe(invoice.getSenderPostcode()), safe(invoice.getSenderCity()),
                firstNonBlank(invoice.getSenderCountry(), "DE"), blankToNull(invoice.getSenderVatId()),
                safe(invoice.getSenderEmail()), safe(invoice.getSenderPhone()));

        PdfToolService.Party recipient = new PdfToolService.Party(
                firstNonBlank(invoice.getRecipientCompany(), invoice.getRecipientName()),
                safe(invoice.getRecipientAddress()), safe(invoice.getRecipientPostcode()),
                safe(invoice.getRecipientCity()), firstNonBlank(invoice.getRecipientCountry(), "DE"),
                blankToNull(invoice.getRecipientVatId()), safe(invoice.getRecipientEmail()), null);

        List<PdfToolService.LineItem> items = buildLineItems(invoice);
        if (items.isEmpty()) {
            throw new IllegalStateException("Die Rechnung enthält keine Positionen mit Betrag.");
        }

        LocalDate issueDate = invoice.getInvoiceDate() != null ? invoice.getInvoiceDate() : LocalDate.now();
        LocalDate deliveryDate = invoice.getDeliveryDate() != null ? invoice.getDeliveryDate() : issueDate;
        LocalDate dueDate = invoice.getPaymentDueDate() != null ? invoice.getPaymentDueDate() : issueDate.plusDays(14);

        return new PdfToolService.InvoiceMetadata(invoice.getInvoiceNumber(), issueDate, deliveryDate, dueDate, "EUR",
                safe(invoice.getPaymentTerms()), null, blankToNull(invoice.getIban()), blankToNull(invoice.getBic()),
                sender, recipient, items);
    }

    private List<PdfToolService.LineItem> buildLineItems(CustomerInvoice invoice) {
        BigDecimal vatPercent = invoice.getVatRate() != null
                ? invoice.getVatRate().multiply(new BigDecimal("100")).setScale(0, RoundingMode.HALF_UP)
                : new BigDecimal("19");

        List<PdfToolService.LineItem> items = new ArrayList<>();
        if (invoice.getItems() != null) {
            for (CustomerInvoiceItem item : invoice.getItems()) {
                if (item == null || item.getQuantity() == null || item.getUnitPrice() == null) {
                    continue;
                }
                BigDecimal itemVatPercent = item.getVatRate() != null
                        ? item.getVatRate().multiply(new BigDecimal("100")).setScale(0, RoundingMode.HALF_UP)
                        : vatPercent;
                items.add(new PdfToolService.LineItem(safe(item.getDescription()), item.getQuantity(),
                        item.getUnitPrice(), itemVatPercent));
            }
        }
        // Ältere Rechnungen ohne Positionsliste: eine Sammelposition aus dem
        // Rechnungs-Netto, damit sie trotzdem als E-Rechnung versendbar bleiben.
        if (items.isEmpty() && invoice.getNetAmount() != null && invoice.getNetAmount().signum() > 0) {
            items.add(new PdfToolService.LineItem(firstNonBlank(invoice.getDescription(), "Leistung gemäß Rechnung"),
                    BigDecimal.ONE, invoice.getNetAmount(), vatPercent));
        }
        return items;
    }

    private String safe(String value) {
        return value != null ? value : "";
    }

    private String blankToNull(String value) {
        return value == null || value.isBlank() ? null : value;
    }

    private String firstNonBlank(String... values) {
        for (String value : values) {
            if (value != null && !value.isBlank()) {
                return value;
            }
        }
        return "";
    }

    private boolean isBlank(String value) {
        return value == null || value.isBlank();
    }
}
