package de.assecutor.votianlt.pages.view;

import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.dialog.Dialog;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.html.Anchor;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.H3;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.NumberField;
import com.vaadin.flow.component.textfield.TextArea;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.router.HasDynamicTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.StreamRegistration;
import com.vaadin.flow.server.StreamResource;
import de.assecutor.votianlt.model.User;
import de.assecutor.votianlt.model.invoices.CustomerInvoice;
import de.assecutor.votianlt.model.invoices.InvoiceAuditEntry;
import de.assecutor.votianlt.model.invoices.InvoiceStatus;
import de.assecutor.votianlt.model.invoices.InvoiceType;
import de.assecutor.votianlt.pages.base.ui.component.DialogStylingHelper;
import de.assecutor.votianlt.pages.base.ui.component.ViewToolbar;
import de.assecutor.votianlt.pages.service.UserInvoiceDataService;
import de.assecutor.votianlt.repository.CustomerInvoiceRepository;
import de.assecutor.votianlt.repository.UserRepository;
import de.assecutor.votianlt.security.SecurityService;
import de.assecutor.votianlt.service.CustomerInvoiceService;
import de.assecutor.votianlt.service.EmailService;
import de.assecutor.votianlt.service.InvoiceExportService;
import de.assecutor.votianlt.service.InvoiceLifecycleException;
import de.assecutor.votianlt.service.InvoiceLifecycleService;
import de.assecutor.votianlt.service.InvoicePermissionService;
import de.assecutor.votianlt.service.PdfToolService;
import de.assecutor.votianlt.service.UserInvoiceZugferdService;
import jakarta.annotation.security.RolesAllowed;
import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayInputStream;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.Optional;

@Route(value = "invoices", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class)
@RolesAllowed({ "USER", "ADMIN" })
@Slf4j
public class InvoicesView extends VerticalLayout implements HasDynamicTitle {

    private static final DateTimeFormatter DATE_TIME_FMT = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm",
            Locale.GERMANY);

    private final Grid<CustomerInvoice> invoiceGrid;
    private final CustomerInvoiceRepository customerInvoiceRepository;
    private final SecurityService securityService;
    private final InvoiceLifecycleService invoiceLifecycleService;
    private final CustomerInvoiceService customerInvoiceService;
    private final InvoiceExportService invoiceExportService;
    private final InvoicePermissionService invoicePermissionService;
    private final UserInvoiceDataService userInvoiceDataService;
    private final UserRepository userRepository;
    private final UserInvoiceZugferdService userInvoiceZugferdService;
    private final EmailService emailService;

    public InvoicesView(CustomerInvoiceRepository customerInvoiceRepository, SecurityService securityService,
            InvoiceLifecycleService invoiceLifecycleService, CustomerInvoiceService customerInvoiceService,
            InvoiceExportService invoiceExportService, InvoicePermissionService invoicePermissionService,
            UserInvoiceDataService userInvoiceDataService,
            UserRepository userRepository, UserInvoiceZugferdService userInvoiceZugferdService,
            EmailService emailService) {
        this.customerInvoiceRepository = customerInvoiceRepository;
        this.securityService = securityService;
        this.invoiceLifecycleService = invoiceLifecycleService;
        this.customerInvoiceService = customerInvoiceService;
        this.invoiceExportService = invoiceExportService;
        this.invoicePermissionService = invoicePermissionService;
        this.userInvoiceDataService = userInvoiceDataService;
        this.userRepository = userRepository;
        this.userInvoiceZugferdService = userInvoiceZugferdService;
        this.emailService = emailService;

        setSizeFull();
        setPadding(true);
        setSpacing(true);
        setJustifyContentMode(FlexComponent.JustifyContentMode.CENTER);
        setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.CENTER);
        addClassName("data-view");

        add(new ViewToolbar(getTranslation("invoices.title")));
        add(buildLegalDisclaimer());

        invoiceGrid = new Grid<>(CustomerInvoice.class, false);
        invoiceGrid.setWidthFull();
        invoiceGrid.addClassName("data-grid");
        invoiceGrid.addColumn(invoice -> firstNonBlank(invoice.getInvoiceNumber(), invoice.getId()))
                .setHeader(getTranslation("invoices.column.number")).setAutoWidth(true);
        invoiceGrid.addComponentColumn(this::renderTypeBadge)
                .setHeader(getTranslation("invoices.column.type")).setAutoWidth(true);
        invoiceGrid.addComponentColumn(this::renderStatusBadge)
                .setHeader(getTranslation("invoices.column.status")).setAutoWidth(true);
        invoiceGrid.addColumn(this::getRecipientLabel).setHeader(getTranslation("invoices.column.customer"))
                .setAutoWidth(true);
        invoiceGrid.addColumn(invoice -> Optional.ofNullable(invoice.getInvoiceDate()).map(Object::toString).orElse(""))
                .setHeader(getTranslation("invoices.column.date")).setAutoWidth(true);
        invoiceGrid.addColumn(this::formatAmount).setHeader(getTranslation("invoices.column.amount"))
                .setAutoWidth(true);
        invoiceGrid.addComponentColumn(this::renderActions)
                .setHeader(getTranslation("invoices.column.actions")).setAutoWidth(true).setFlexGrow(0);

        invoiceGrid.setSelectionMode(Grid.SelectionMode.NONE);

        Div gridPanel = new Div(invoiceGrid);
        gridPanel.addClassNames("surface-panel", "data-grid-panel");
        gridPanel.setWidthFull();

        add(gridPanel);

        loadInvoices();
    }

    private Component buildLegalDisclaimer() {
        Div banner = new Div();
        banner.addClassName("surface-panel");
        banner.getStyle().set("padding", "12px 16px").set("border-left", "4px solid var(--lumo-primary-color)")
                .set("background", "var(--lumo-contrast-5pct)");
        Span text = new Span(getTranslation("invoices.disclaimer"));
        text.getStyle().set("font-size", "var(--lumo-font-size-s)");
        banner.add(text);
        return banner;
    }

    private void loadInvoices() {
        String currentUserId = securityService.getCurrentUserId().toHexString();
        List<CustomerInvoice> invoices = customerInvoiceRepository.findByUserId(currentUserId).stream()
                .sorted(Comparator
                        .comparing((CustomerInvoice i) -> i.getInvoiceDate() == null ? LocalDate.MIN
                                : i.getInvoiceDate())
                        .reversed())
                .toList();
        invoiceGrid.setItems(invoices);
    }

    private Component renderStatusBadge(CustomerInvoice invoice) {
        InvoiceStatus status = invoice.getStatus() != null ? invoice.getStatus() : InvoiceStatus.ISSUED;
        Span badge = new Span(getTranslation("invoices.status." + status.name().toLowerCase(Locale.ROOT)));
        badge.getElement().getThemeList().add("badge");
        switch (status) {
        case DRAFT -> badge.getElement().getThemeList().add("contrast");
        case SENT -> badge.getElement().getThemeList().add("success");
        case CANCELLED -> badge.getElement().getThemeList().add("error");
        case CORRECTED -> badge.getElement().getThemeList().add("warning");
        default -> {
        }
        }
        return badge;
    }

    private Component renderTypeBadge(CustomerInvoice invoice) {
        InvoiceType type = invoice.getType() != null ? invoice.getType() : InvoiceType.INVOICE;
        HorizontalLayout layout = new HorizontalLayout();
        layout.setSpacing(true);
        layout.setPadding(false);

        Span badge = new Span(getTranslation("invoices.type." + type.name().toLowerCase(Locale.ROOT)));
        badge.getElement().getThemeList().add("badge");
        if (type == InvoiceType.CANCELLATION) {
            badge.getElement().getThemeList().add("error");
        } else if (type == InvoiceType.CORRECTION) {
            badge.getElement().getThemeList().add("warning");
        }
        layout.add(badge);
        return layout;
    }

    private Component renderActions(CustomerInvoice invoice) {
        HorizontalLayout actions = new HorizontalLayout();
        actions.setSpacing(true);
        actions.setPadding(false);

        Button viewBtn = new Button(getTranslation("invoices.action.view"), e -> downloadInvoicePdf(invoice));
        viewBtn.addThemeVariants(ButtonVariant.LUMO_TERTIARY, ButtonVariant.LUMO_SMALL);
        viewBtn.setEnabled(invoice.getPdfData() != null && invoice.getPdfData().length > 0);
        actions.add(viewBtn);

        Button historyBtn = new Button(getTranslation("invoices.action.history"), e -> openHistoryDialog(invoice));
        historyBtn.addThemeVariants(ButtonVariant.LUMO_TERTIARY, ButtonVariant.LUMO_SMALL);
        actions.add(historyBtn);

        InvoiceStatus status = invoice.getStatus() != null ? invoice.getStatus() : InvoiceStatus.ISSUED;
        InvoiceType type = invoice.getType() != null ? invoice.getType() : InvoiceType.INVOICE;
        User currentUser = invoicePermissionService.currentUser();

        // Aktionen nur für reguläre, noch aktive Rechnungen anbieten
        boolean isLiveInvoice = type == InvoiceType.INVOICE
                && (status == InvoiceStatus.ISSUED || status == InvoiceStatus.SENT);

        if (type == InvoiceType.INVOICE && status == InvoiceStatus.ISSUED
                && invoicePermissionService.canMarkAsSent(currentUser)) {
            Button sentBtn = new Button(getTranslation("invoices.action.marksent"),
                    e -> markAsSent(invoice));
            sentBtn.addThemeVariants(ButtonVariant.LUMO_SMALL);
            actions.add(sentBtn);
        }

        // E-Rechnung (ZUGFeRD) über das externe PDF Tool — analog zu den
        // System-Rechnungen der Admin-Seite
        if (type == InvoiceType.INVOICE && invoice.getPdfData() != null && invoice.getPdfData().length > 0) {
            Button zugferdBtn = new Button(getTranslation("invoices.action.zugferd"),
                    e -> downloadZugferdZip(invoice));
            zugferdBtn.addThemeVariants(ButtonVariant.LUMO_TERTIARY, ButtonVariant.LUMO_SMALL);
            actions.add(zugferdBtn);

            if (isLiveInvoice && invoicePermissionService.canMarkAsSent(currentUser)) {
                Button sendBtn = new Button(getTranslation("invoices.action.sendinvoice"),
                        e -> sendAsEInvoice(invoice));
                sendBtn.addThemeVariants(ButtonVariant.LUMO_SMALL, ButtonVariant.LUMO_PRIMARY);
                actions.add(sendBtn);
            }
        }

        if (isLiveInvoice) {
            if (invoicePermissionService.canCorrect(currentUser)) {
                Button correctBtn = new Button(getTranslation("invoices.action.correct"),
                        e -> openCorrectionDialog(invoice));
                correctBtn.addThemeVariants(ButtonVariant.LUMO_SMALL);
                actions.add(correctBtn);
            }

            if (invoicePermissionService.canCancel(currentUser)) {
                Button cancelBtn = new Button(getTranslation("invoices.action.cancel"),
                        e -> openCancellationDialog(invoice));
                cancelBtn.addThemeVariants(ButtonVariant.LUMO_SMALL, ButtonVariant.LUMO_ERROR);
                actions.add(cancelBtn);
            }
        }

        // Zahlung erfassen: nur für reguläre Rechnungen (R-25)
        if (type == InvoiceType.INVOICE && status != InvoiceStatus.DRAFT
                && invoicePermissionService.canRecordPayment(currentUser)) {
            Button payBtn = new Button(getTranslation("invoices.action.payment"),
                    e -> openPaymentDialog(invoice));
            payBtn.addThemeVariants(ButtonVariant.LUMO_SMALL);
            actions.add(payBtn);
        }

        // Belegpaket exportieren (R-33/R-34)
        Button exportBtn = new Button(getTranslation("invoices.action.export"), e -> exportPackage(invoice));
        exportBtn.addThemeVariants(ButtonVariant.LUMO_TERTIARY, ButtonVariant.LUMO_SMALL);
        actions.add(exportBtn);

        return actions;
    }

    private void openPaymentDialog(CustomerInvoice invoice) {
        Dialog dialog = DialogStylingHelper.createStyledDialog(
                getTranslation("invoices.payment.title", invoice.getInvoiceNumber()), "480px");

        VerticalLayout content = new VerticalLayout();
        content.setSpacing(true);
        content.setPadding(false);

        java.math.BigDecimal outstanding = invoiceLifecycleService.computeOutstandingAmount(invoice);
        Span hint = new Span(getTranslation("invoices.payment.hint",
                java.text.NumberFormat.getCurrencyInstance(Locale.GERMANY).format(outstanding)));
        hint.getStyle().set("color", "var(--lumo-secondary-text-color)")
                .set("font-size", "var(--lumo-font-size-s)");
        content.add(hint);

        NumberField amountField = new NumberField(getTranslation("invoices.payment.amount"));
        amountField.setStep(0.01);
        amountField.setValue(outstanding.doubleValue());
        amountField.setRequiredIndicatorVisible(true);
        amountField.setWidthFull();
        content.add(amountField);

        TextField referenceField = new TextField(getTranslation("invoices.payment.reference"));
        referenceField.setWidthFull();
        content.add(referenceField);

        TextArea reasonField = new TextArea(getTranslation("invoices.payment.reason"));
        reasonField.setWidthFull();
        reasonField.setMinHeight("80px");
        content.add(reasonField);

        dialog.add(DialogStylingHelper.wrapContent(content));

        Button cancelBtn = new Button(getTranslation("button.cancel"), e -> dialog.close());
        cancelBtn.addThemeVariants(ButtonVariant.LUMO_TERTIARY);

        Button confirmBtn = new Button(getTranslation("invoices.payment.confirm"), e -> {
            Double amount = amountField.getValue();
            if (amount == null || amount == 0d) {
                amountField.setInvalid(true);
                amountField.setErrorMessage(getTranslation("invoices.payment.amount.required"));
                return;
            }
            performPayment(invoice, java.math.BigDecimal.valueOf(amount), referenceField.getValue(),
                    reasonField.getValue(), dialog);
        });
        confirmBtn.addThemeVariants(ButtonVariant.LUMO_PRIMARY);

        dialog.getFooter().add(cancelBtn, confirmBtn);
        dialog.open();
    }

    private void performPayment(CustomerInvoice invoice, java.math.BigDecimal amount, String reference, String reason,
            Dialog dialog) {
        try {
            invoicePermissionService.requirePayment(invoicePermissionService.currentUser());
            invoiceLifecycleService.registerPayment(invoice.getId(), amount, reference, reason);
            dialog.close();
            Notification.show(getTranslation("invoices.notification.payment"), 3000, Notification.Position.BOTTOM_END);
            loadInvoices();
        } catch (InvoiceLifecycleException ex) {
            Notification.show(ex.getMessage(), 6000, Notification.Position.MIDDLE);
        } catch (Exception ex) {
            Notification.show(getTranslation("invoices.notification.error", ex.getMessage()), 6000,
                    Notification.Position.MIDDLE);
        }
    }

    private void exportPackage(CustomerInvoice invoice) {
        try {
            byte[] zipBytes = invoiceExportService.exportInvoicePackage(invoice);
            String fileName = invoiceExportService.suggestFilename(invoice);
            StreamResource resource = new StreamResource(fileName, () -> new ByteArrayInputStream(zipBytes));
            resource.setContentType("application/zip");
            resource.setCacheTime(0);
            StreamRegistration registration = UI.getCurrent().getSession().getResourceRegistry()
                    .registerResource(resource);
            UI.getCurrent().getPage().open(registration.getResourceUri().toString());
        } catch (Exception ex) {
            Notification.show(getTranslation("invoices.notification.error", ex.getMessage()), 6000,
                    Notification.Position.MIDDLE);
        }
    }

    /**
     * Wandelt die Rechnung über das externe PDF Tool in eine signierte
     * ZUGFeRD-E-Rechnung um und bietet das ZIP (PDF/A-3, factur-x.xml,
     * Prüfbericht) als Download an.
     */
    private void downloadZugferdZip(CustomerInvoice invoice) {
        try {
            PdfToolService.SignedInvoice signed = userInvoiceZugferdService.sign(invoice);
            triggerZipDownload(signed.zip(), signed.fileName());
            if (signed.valid()) {
                Notification.show(getTranslation("invoices.notification.zugferd.created", invoice.getInvoiceNumber()),
                        5000, Notification.Position.BOTTOM_END);
            } else {
                Notification.show(getTranslation("invoices.notification.zugferd.invalid"), 7000,
                        Notification.Position.MIDDLE);
            }
        } catch (Exception ex) {
            log.error("Fehler beim Erstellen der ZUGFeRD-E-Rechnung {}: {}", invoice.getInvoiceNumber(),
                    ex.getMessage(), ex);
            Notification.show(getTranslation("invoices.notification.zugferd.error", ex.getMessage()), 7000,
                    Notification.Position.MIDDLE);
        }
    }

    /**
     * Versendet die Rechnung als signierte ZUGFeRD-E-Rechnung (ZIP) per E-Mail
     * an den Kunden. Besteht die Rechnung die Validierung nicht (HTTP 422 des
     * PDF Tools), wird der Versand abgebrochen — wie beim Sammelversand der
     * System-Rechnungen. Nach erfolgreichem Versand wird der Status auf
     * "Versendet" gesetzt.
     */
    private void sendAsEInvoice(CustomerInvoice invoice) {
        String recipientEmail = invoice.getRecipientEmail();
        if (recipientEmail == null || recipientEmail.isBlank()) {
            Notification.show(getTranslation("invoices.notification.email.missing"), 6000,
                    Notification.Position.MIDDLE);
            return;
        }
        try {
            invoicePermissionService.requireSend(invoicePermissionService.currentUser());

            PdfToolService.SignedInvoice signed = userInvoiceZugferdService.sign(invoice);
            if (!signed.valid()) {
                log.warn("Versand der Rechnung {} abgebrochen, ZUGFeRD-Validierung fehlgeschlagen",
                        invoice.getInvoiceNumber());
                Notification.show(
                        getTranslation("invoices.notification.zugferd.invalidsend", invoice.getInvoiceNumber()), 7000,
                        Notification.Position.MIDDLE);
                return;
            }

            emailService.sendEmailWithAttachment(recipientEmail,
                    getTranslation("invoices.mail.subject", invoice.getInvoiceNumber()),
                    getTranslation("invoices.mail.body", invoice.getInvoiceNumber(),
                            firstNonBlank(invoice.getSenderName(), "")),
                    signed.zip(), signed.fileName());

            if (invoice.getStatus() == InvoiceStatus.ISSUED) {
                invoiceLifecycleService.markAsSent(invoice.getId(),
                        "Als E-Rechnung per E-Mail versendet an " + recipientEmail);
            }
            Notification.show(
                    getTranslation("invoices.notification.emailsent", invoice.getInvoiceNumber(), recipientEmail),
                    5000, Notification.Position.BOTTOM_END);
            loadInvoices();
        } catch (InvoiceLifecycleException ex) {
            Notification.show(ex.getMessage(), 6000, Notification.Position.MIDDLE);
        } catch (Exception ex) {
            log.error("Fehler beim Versand der Rechnung {} an {}: {}", invoice.getInvoiceNumber(), recipientEmail,
                    ex.getMessage(), ex);
            Notification.show(getTranslation("invoices.notification.zugferd.error", ex.getMessage()), 7000,
                    Notification.Position.MIDDLE);
        }
    }

    /** Bietet die ZIP-Bytes als Browser-Download an. */
    private void triggerZipDownload(byte[] zip, String fileName) {
        StreamResource resource = new StreamResource(fileName, () -> new ByteArrayInputStream(zip));
        resource.setContentType("application/zip");
        resource.setCacheTime(0);
        StreamRegistration registration = UI.getCurrent().getSession().getResourceRegistry()
                .registerResource(resource);
        UI.getCurrent().getPage().open(registration.getResourceUri().toString());
    }

    private void markAsSent(CustomerInvoice invoice) {
        try {
            invoicePermissionService.requireSend(invoicePermissionService.currentUser());
            invoiceLifecycleService.markAsSent(invoice.getId(), "Manuell als versendet markiert");
            Notification.show(getTranslation("invoices.notification.sent"), 3000, Notification.Position.BOTTOM_END);
            loadInvoices();
        } catch (InvoiceLifecycleException ex) {
            Notification.show(ex.getMessage(), 5000, Notification.Position.MIDDLE);
        }
    }

    private void openCancellationDialog(CustomerInvoice invoice) {
        Dialog dialog = DialogStylingHelper.createStyledDialog(
                getTranslation("invoices.cancel.title", invoice.getInvoiceNumber()), "560px");

        VerticalLayout content = new VerticalLayout();
        content.setSpacing(true);
        content.setPadding(false);

        Span hint = new Span(getTranslation("invoices.cancel.hint"));
        hint.getStyle().set("color", "var(--lumo-secondary-text-color)")
                .set("font-size", "var(--lumo-font-size-s)");
        content.add(hint);

        TextArea reasonField = new TextArea(getTranslation("invoices.cancel.reason"));
        reasonField.setWidthFull();
        reasonField.setMinHeight("100px");
        reasonField.setRequired(true);
        content.add(reasonField);

        dialog.add(DialogStylingHelper.wrapContent(content));

        Button cancelBtn = new Button(getTranslation("button.cancel"), e -> dialog.close());
        cancelBtn.addThemeVariants(ButtonVariant.LUMO_TERTIARY);

        Button confirmBtn = new Button(getTranslation("invoices.cancel.confirm"), e -> {
            String reason = reasonField.getValue();
            if (reason == null || reason.isBlank()) {
                reasonField.setInvalid(true);
                reasonField.setErrorMessage(getTranslation("invoices.cancel.reason.required"));
                return;
            }
            performCancellation(invoice, reason, dialog);
        });
        confirmBtn.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_ERROR);

        dialog.getFooter().add(cancelBtn, confirmBtn);
        dialog.open();
    }

    private void performCancellation(CustomerInvoice invoice, String reason, Dialog dialog) {
        User currentUser = invoicePermissionService.currentUser();
        try {
            invoicePermissionService.requireCancel(currentUser);
            User issuer = resolveIssuer(invoice);
            String number = userInvoiceDataService.generateNextInvoiceNumber(issuer.getId());
            LocalDate today = LocalDate.now();
            byte[] pdf = customerInvoiceService.generateCancellationPdf(invoice, number, today, reason);
            invoiceLifecycleService.cancel(invoice.getId(), number, today, pdf, reason);
            dialog.close();
            Notification.show(getTranslation("invoices.notification.cancelled", number), 4000,
                    Notification.Position.BOTTOM_END);
            loadInvoices();
        } catch (InvoiceLifecycleException ex) {
            Notification.show(ex.getMessage(), 6000, Notification.Position.MIDDLE);
        } catch (Exception ex) {
            Notification.show(getTranslation("invoices.notification.error", ex.getMessage()), 6000,
                    Notification.Position.MIDDLE);
        }
    }

    private void openCorrectionDialog(CustomerInvoice invoice) {
        Dialog dialog = DialogStylingHelper.createStyledDialog(
                getTranslation("invoices.correct.title", invoice.getInvoiceNumber()), "560px");

        VerticalLayout content = new VerticalLayout();
        content.setSpacing(true);
        content.setPadding(false);

        Span hint = new Span(getTranslation("invoices.correct.hint"));
        hint.getStyle().set("color", "var(--lumo-secondary-text-color)")
                .set("font-size", "var(--lumo-font-size-s)");
        content.add(hint);

        TextArea fieldsField = new TextArea(getTranslation("invoices.correct.fields"));
        fieldsField.setWidthFull();
        fieldsField.setMinHeight("100px");
        fieldsField.setHelperText(getTranslation("invoices.correct.fields.helper"));
        fieldsField.setRequired(true);
        content.add(fieldsField);

        TextArea reasonField = new TextArea(getTranslation("invoices.correct.reason"));
        reasonField.setWidthFull();
        reasonField.setMinHeight("80px");
        content.add(reasonField);

        dialog.add(DialogStylingHelper.wrapContent(content));

        Button cancelBtn = new Button(getTranslation("button.cancel"), e -> dialog.close());
        cancelBtn.addThemeVariants(ButtonVariant.LUMO_TERTIARY);

        Button confirmBtn = new Button(getTranslation("invoices.correct.confirm"), e -> {
            String fields = fieldsField.getValue();
            if (fields == null || fields.isBlank()) {
                fieldsField.setInvalid(true);
                fieldsField.setErrorMessage(getTranslation("invoices.correct.fields.required"));
                return;
            }
            performCorrection(invoice, fields, reasonField.getValue(), dialog);
        });
        confirmBtn.addThemeVariants(ButtonVariant.LUMO_PRIMARY);

        dialog.getFooter().add(cancelBtn, confirmBtn);
        dialog.open();
    }

    private void performCorrection(CustomerInvoice invoice, String correctedFields, String reason, Dialog dialog) {
        User currentUser = invoicePermissionService.currentUser();
        try {
            invoicePermissionService.requireCorrect(currentUser);
            User issuer = resolveIssuer(invoice);
            String number = userInvoiceDataService.generateNextInvoiceNumber(issuer.getId());
            LocalDate today = LocalDate.now();
            byte[] pdf = customerInvoiceService.generateCorrectionPdf(invoice, number, today, reason, correctedFields);
            invoiceLifecycleService.correct(invoice.getId(), number, today, pdf, correctedFields, reason);
            dialog.close();
            Notification.show(getTranslation("invoices.notification.corrected", number), 4000,
                    Notification.Position.BOTTOM_END);
            loadInvoices();
        } catch (InvoiceLifecycleException ex) {
            Notification.show(ex.getMessage(), 6000, Notification.Position.MIDDLE);
        } catch (Exception ex) {
            Notification.show(getTranslation("invoices.notification.error", ex.getMessage()), 6000,
                    Notification.Position.MIDDLE);
        }
    }

    private User resolveIssuer(CustomerInvoice invoice) {
        if (invoice.getUserId() != null && !invoice.getUserId().isBlank()) {
            try {
                return userRepository.findById(new org.bson.types.ObjectId(invoice.getUserId()))
                        .orElseGet(securityService::getCurrentDatabaseUser);
            } catch (IllegalArgumentException ex) {
                // userId ist kein gültiger ObjectId – Fallback auf eingeloggten Nutzer
            }
        }
        return securityService.getCurrentDatabaseUser();
    }

    private void openHistoryDialog(CustomerInvoice invoice) {
        Dialog dialog = DialogStylingHelper.createStyledDialog(
                getTranslation("invoices.history.title", invoice.getInvoiceNumber()), "640px");

        VerticalLayout content = new VerticalLayout();
        content.setSpacing(true);
        content.setPadding(false);

        // Verkettung anzeigen, falls vorhanden
        Div linksBlock = renderRelatedInvoiceLinks(invoice);
        if (linksBlock != null) {
            content.add(linksBlock);
        }

        H3 logTitle = new H3(getTranslation("invoices.history.log"));
        content.add(logTitle);

        List<InvoiceAuditEntry> log = invoice.getAuditLog();
        if (log == null || log.isEmpty()) {
            content.add(new Span(getTranslation("invoices.history.empty")));
        } else {
            log.stream()
                    .sorted(Comparator
                            .comparing((InvoiceAuditEntry e) -> e.getTimestamp() == null
                                    ? java.time.LocalDateTime.MIN
                                    : e.getTimestamp())
                            .reversed())
                    .forEach(entry -> content.add(renderAuditEntry(entry)));
        }

        dialog.add(DialogStylingHelper.wrapContent(content, true));

        Button closeBtn = new Button(getTranslation("button.close"), e -> dialog.close());
        closeBtn.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
        dialog.getFooter().add(closeBtn);
        dialog.open();
    }

    private Div renderRelatedInvoiceLinks(CustomerInvoice invoice) {
        Div block = new Div();
        boolean hasContent = false;
        if (invoice.getOriginalInvoiceId() != null) {
            block.add(buildLinkRow(getTranslation("invoices.history.original"),
                    invoice.getOriginalInvoiceNumber(), invoice.getOriginalInvoiceId()));
            hasContent = true;
        }
        if (invoice.getCancellationInvoiceId() != null) {
            block.add(buildLinkRow(getTranslation("invoices.history.cancellation"),
                    null, invoice.getCancellationInvoiceId()));
            hasContent = true;
        }
        if (invoice.getCorrectionInvoiceId() != null) {
            block.add(buildLinkRow(getTranslation("invoices.history.correction"),
                    null, invoice.getCorrectionInvoiceId()));
            hasContent = true;
        }
        if (invoice.getReplacementInvoiceId() != null) {
            block.add(buildLinkRow(getTranslation("invoices.history.replacement"),
                    null, invoice.getReplacementInvoiceId()));
            hasContent = true;
        }
        return hasContent ? block : null;
    }

    private HorizontalLayout buildLinkRow(String label, String fallbackNumber, String invoiceId) {
        HorizontalLayout row = new HorizontalLayout();
        row.setSpacing(true);
        row.setPadding(false);
        Span lbl = new Span(label);
        lbl.getStyle().set("min-width", "180px").set("color", "var(--lumo-secondary-text-color)");
        row.add(lbl);

        CustomerInvoice related = invoiceLifecycleService.findById(invoiceId).orElse(null);
        String number = related != null && related.getInvoiceNumber() != null ? related.getInvoiceNumber()
                : fallbackNumber != null ? fallbackNumber : invoiceId;
        if (related != null && related.getPdfData() != null && related.getPdfData().length > 0) {
            Anchor link = new Anchor("javascript:void(0)", number);
            link.getElement().addEventListener("click", e -> downloadInvoicePdf(related));
            row.add(link);
        } else {
            row.add(new Span(number));
        }
        return row;
    }

    private Div renderAuditEntry(InvoiceAuditEntry entry) {
        Div container = new Div();
        container.getStyle().set("padding", "8px 12px").set("margin-bottom", "6px")
                .set("border-left", "3px solid var(--lumo-contrast-30pct)")
                .set("background", "var(--lumo-contrast-5pct)");

        String timestamp = entry.getTimestamp() != null ? entry.getTimestamp().format(DATE_TIME_FMT) : "—";
        String actionLabel = entry.getAction() != null
                ? getTranslation("invoices.audit.action." + entry.getAction().name().toLowerCase(Locale.ROOT))
                : "?";
        String userLabel = entry.getUserDisplayName() != null ? entry.getUserDisplayName() : "system";

        Span header = new Span(timestamp + " · " + actionLabel + " · " + userLabel);
        header.getStyle().set("font-weight", "600");
        container.add(header);

        if (entry.getReason() != null && !entry.getReason().isBlank()) {
            Div reason = new Div();
            reason.setText(entry.getReason());
            reason.getStyle().set("margin-top", "4px");
            container.add(reason);
        }
        if (entry.getResultingInvoiceNumber() != null) {
            Div link = new Div();
            link.setText(getTranslation("invoices.audit.resulting", entry.getResultingInvoiceNumber()));
            link.getStyle().set("margin-top", "4px").set("color", "var(--lumo-secondary-text-color)")
                    .set("font-size", "var(--lumo-font-size-s)");
            container.add(link);
        }
        return container;
    }

    private void downloadInvoicePdf(CustomerInvoice invoice) {
        try {
            if (invoice.getPdfData() == null || invoice.getPdfData().length == 0) {
                Notification.show(getTranslation("invoices.notification.pdf.missing"), 4000,
                        Notification.Position.MIDDLE);
                return;
            }

            StreamResource resource = new StreamResource(
                    firstNonBlank(invoice.getInvoiceNumber(), invoice.getId()) + ".pdf",
                    () -> new ByteArrayInputStream(invoice.getPdfData()));
            resource.setContentType("application/pdf");
            resource.setCacheTime(0);

            StreamRegistration registration = UI.getCurrent().getSession().getResourceRegistry()
                    .registerResource(resource);
            UI.getCurrent().getPage().open(registration.getResourceUri().toString());

        } catch (Exception e) {
            Notification.show(getTranslation("invoices.notification.pdf.error", e.getMessage()), 5000,
                    Notification.Position.MIDDLE);
        }
    }

    private String getRecipientLabel(CustomerInvoice invoice) {
        return firstNonBlank(invoice.getRecipientCompany(), invoice.getRecipientName(), "");
    }

    private String formatAmount(CustomerInvoice invoice) {
        var amount = invoice.getTotalAmount() != null ? invoice.getTotalAmount() : invoice.getNetAmount();
        if (amount == null) {
            return "";
        }
        return java.text.NumberFormat.getCurrencyInstance(Locale.GERMANY).format(amount);
    }

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

    @Override
    public String getPageTitle() {
        return getTranslation("page.title.invoices");
    }
}
