package de.assecutor.pdftool.ui;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.vaadin.flow.component.AttachEvent;
import com.vaadin.flow.component.ClientCallable;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.confirmdialog.ConfirmDialog;
import com.vaadin.flow.component.dependency.CssImport;
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.dialog.Dialog;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.html.IFrame;
import com.vaadin.flow.component.html.Input;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.component.upload.Upload;
import com.vaadin.flow.component.upload.receivers.MemoryBuffer;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import de.assecutor.pdftool.invoice.CapturedInvoiceData;
import de.assecutor.pdftool.invoice.InvoiceMetadata;
import de.assecutor.pdftool.template.InvoiceTemplateService;
import de.assecutor.pdftool.template.TemplatePdfService;
import de.assecutor.pdftool.template.TemplateVariables;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * Canvas-Designer für eigene Rechnungstemplates: Bausteine (Stammdaten,
 * Empfänger, Positionen, freie Elemente) werden per Drag &amp; Drop auf einer
 * A4-Zeichenfläche platziert. Das Template wird als JSON gespeichert und kann
 * als PDF-Vorschau gerendert werden. Portiert aus votianlt
 * (InvoiceGeneratorView); Rechnungssteller-Stammdaten sind hier fest die
 * Assecutor Data Service GmbH.
 */
@Route(value = "rechnungsgenerator", layout = MainLayout.class)
@PageTitle("Templates")
@JsModule("./invoice-generator/profile-invoice-generator.js")
@CssImport("./styles/invoice-generator.css")
public class TemplateGeneratorView extends VerticalLayout {
    private static final Logger log = LoggerFactory.getLogger(TemplateGeneratorView.class);

    private static final BigDecimal VAT_RATE = new BigDecimal("0.19");

    private final TemplatePdfService templatePdfService;
    private final InvoiceTemplateService invoiceTemplateService;
    private final CapturedInvoiceData capturedInvoiceData;

    private final VerticalLayout propertiesPanel;

    /** Auswahl des zu bearbeitenden Templates in der Aktionsleiste. */
    private ComboBox<String> templateSelect;

    /** Name des zuletzt geladenen bzw. gespeicherten Templates. */
    private String currentTemplateName;

    public TemplateGeneratorView(TemplatePdfService templatePdfService,
            InvoiceTemplateService invoiceTemplateService, CapturedInvoiceData capturedInvoiceData) {
        this.templatePdfService = templatePdfService;
        this.invoiceTemplateService = invoiceTemplateService;
        this.capturedInvoiceData = capturedInvoiceData;

        addClassName("invoice-generator-view");

        setSpacing(false);
        setPadding(true);
        setMargin(false);
        setWidth("100%");
        setHeight("100%");
        getStyle().set("overflow", "hidden").set("box-sizing", "border-box").set("display", "flex")
                .set("flex-direction", "column");

        // Hauptlayout: Links (Bausteine) | Mitte (Canvas) | Rechts (Eigenschaften)
        HorizontalLayout mainLayout = new HorizontalLayout();
        mainLayout.setWidth("100%");
        mainLayout.setHeight("calc(100% - 60px)");
        mainLayout.setSpacing(true);
        mainLayout.getStyle().set("overflow", "hidden");
        mainLayout.addClassName("invoice-generator-main");

        VerticalLayout leftPanel = createTemplatesPanel();
        leftPanel.setWidth("280px");
        leftPanel.setHeightFull();
        leftPanel.getStyle().set("flex-shrink", "0").set("min-width", "280px").set("overflow", "auto");

        VerticalLayout centerPanel = createCanvasPanel();
        centerPanel.setWidth("60%");
        centerPanel.setHeightFull();
        centerPanel.getStyle().set("flex-grow", "1").set("min-width", "0");

        propertiesPanel = createPropertiesPanel();
        propertiesPanel.setWidth("300px");
        propertiesPanel.setHeightFull();
        propertiesPanel.getStyle().set("flex-shrink", "0").set("min-width", "300px").set("overflow", "auto");

        mainLayout.add(leftPanel, centerPanel, propertiesPanel);
        mainLayout.expand(centerPanel);

        add(mainLayout);

        HorizontalLayout actionLayout = createActionButtons();
        actionLayout.setHeight("60px");
        actionLayout.getStyle().set("flex-shrink", "0");
        add(actionLayout);
    }

    @Override
    protected void onAttach(AttachEvent attachEvent) {
        super.onAttach(attachEvent);
        getElement().executeJs("window.invoiceGeneratorViewProfile = $0;"
                + "window.profileInvoiceVatRate = " + VAT_RATE.doubleValue() + ";"
                + "window.masterdataValues = JSON.parse($1);"
                + "window.profileInvoiceServiceData = JSON.parse($2);"
                + "setTimeout(function() { "
                + "  if (window.initProfileInvoiceGenerator) { "
                + "    window.initProfileInvoiceGenerator(); "
                + "    setTimeout(function() { $0.$server.onCanvasReady(); }, 200); "
                + "  } else { console.error('initProfileInvoiceGenerator not found'); } "
                + "}, 300);", getElement(), buildMasterdataJson(), buildCanvasPositionsJson());
    }

    /** Wird vom JavaScript gerufen, sobald das Canvas bereit ist. */
    @ClientCallable
    public void onCanvasReady() {
        loadInitialTemplate();
    }

    /** Lädt beim Start das Standard-Template bzw. das erste vorhandene Template. */
    private void loadInitialTemplate() {
        List<String> names = invoiceTemplateService.templateNames();
        templateSelect.setItems(names);
        if (names.isEmpty()) {
            return;
        }
        String initial = names.contains(InvoiceTemplateService.DEFAULT_TEMPLATE_NAME)
                ? InvoiceTemplateService.DEFAULT_TEMPLATE_NAME
                : names.get(0);
        templateSelect.setValue(initial);
        loadTemplateIntoCanvas(initial);
    }

    /** Lädt das Template mit dem Namen auf die Zeichenfläche. */
    private void loadTemplateIntoCanvas(String name) {
        try {
            Optional<String> templateData = invoiceTemplateService.loadTemplate(name);
            if (templateData.isEmpty()) {
                showNotification("Template \"" + name + "\" konnte nicht geladen werden.");
                return;
            }
            currentTemplateName = name;
            getElement().executeJs("setTimeout(function() { "
                    + "  if (window.loadProfileTemplate && document.getElementById('invoice-canvas-container-profile')) { "
                    + "    window.loadProfileTemplate(JSON.parse($0)); "
                    + "  } else { console.error('loadProfileTemplate or canvas not available'); } "
                    + "}, 300);", templateData.get());
        } catch (Exception ex) {
            log.error("Rechnungstemplate konnte nicht geladen werden: {}", ex.getMessage(), ex);
        }
    }

    /**
     * Variablenwerte für Entwurf und Vorschau: Rechnungssteller ist fest die
     * Assecutor Data Service GmbH, Empfänger und Positionen sind Beispieldaten.
     * Beim späteren Einsatz des Templates werden sie durch echte Daten ersetzt.
     */
    private Map<String, String> buildVariables() {
        Map<String, String> variables = new LinkedHashMap<>();
        variables.put("masterdata.company_name", "Assecutor Data Service GmbH");
        variables.put("masterdata.street", "Gerhart-Hauptmann-Weg 14");
        variables.put("masterdata.city", "21502 Geesthacht");
        variables.put("masterdata.phone", "+49 40 18 123 771 0");
        variables.put("masterdata.email", "mail@assecutor.de");
        variables.put("masterdata.website", "www.assecutor.de");
        variables.put("masterdata.sender_line",
                "Assecutor Data Service GmbH · Gerhart-Hauptmann-Weg 14 · 21502 Geesthacht");
        variables.put("masterdata.payment_terms", "Zahlbar innerhalb von 14 Tagen ohne Abzug.");
        variables.put("masterdata.footer",
                "Assecutor Data Service GmbH · Amtsgericht Lübeck HRB 8595 HL · USt-IdNr. DE261094748");
        variables.put("masterdata.invoice_number", "RE-2026-0001");
        variables.put("masterdata.invoice_date",
                LocalDate.now().format(DateTimeFormatter.ofPattern("dd.MM.yyyy")));

        variables.put("customer.company_name", "Musterfirma GmbH");
        variables.put("customer.contact_name", "Max Mustermann");
        variables.put("customer.street", "Musterstraße 1");
        variables.put("customer.city", "12345 Musterstadt");
        variables.put("customer.email", "kunde@beispiel.de");
        variables.put("customer.phone", "0123 456789");

        addPositions(variables);
        return variables;
    }

    /** Beispielpositionen, solange noch keine Positionen erfasst wurden. */
    private static final List<InvoiceMetadata.LineItem> SAMPLE_ITEMS = List.of(
            new InvoiceMetadata.LineItem("Beratungsleistung", BigDecimal.ONE, new BigDecimal("450.00"),
                    new BigDecimal("19")),
            new InvoiceMetadata.LineItem("Softwareentwicklung", BigDecimal.ONE, new BigDecimal("1200.00"),
                    new BigDecimal("19")),
            new InvoiceMetadata.LineItem("Support-Pauschale", BigDecimal.ONE, new BigDecimal("150.00"),
                    new BigDecimal("19")));

    /**
     * Die in der E-Rechnungs-Maske erfassten Positionen; Beispieldaten als
     * Fallback, solange noch nichts erfasst wurde.
     */
    private List<InvoiceMetadata.LineItem> effectiveItems() {
        List<InvoiceMetadata.LineItem> items = capturedInvoiceData.getItems();
        return items.isEmpty() ? SAMPLE_ITEMS : items;
    }

    private void addPositions(Map<String, String> variables) {
        TemplateVariables.addPositions(variables, effectiveItems());
    }

    /** JSON für die Canvas-Darstellung des services.list-Bausteins. */
    private String buildCanvasPositionsJson() {
        return TemplateVariables.canvasPositionsJson(effectiveItems());
    }

    private String buildMasterdataJson() {
        try {
            return new ObjectMapper().writeValueAsString(buildVariables());
        } catch (Exception ex) {
            log.error("Stammdaten konnten nicht serialisiert werden: {}", ex.getMessage(), ex);
            return "{}";
        }
    }

    // ==========================================
    // Linkes Panel: Bausteine
    // ==========================================

    private VerticalLayout createTemplatesPanel() {
        VerticalLayout panel = new VerticalLayout();
        panel.setPadding(true);
        panel.setSpacing(true);
        panel.setHeightFull();
        panel.getStyle().set("overflow", "auto");
        panel.addClassName("invoice-generator-panel");

        Map<String, String> values = buildVariables();

        // Bereich 1: Rechnungssteller (Assecutor Data Service GmbH)
        Span issuerHeader = createSectionHeader("Rechnungssteller");

        Div issuerCompany = createVariableTemplate("Firma", VaadinIcon.OFFICE,
                "masterdata.company_name", values.get("masterdata.company_name"));
        Div issuerStreet = createVariableTemplate("Straße", VaadinIcon.MAP_MARKER,
                "masterdata.street", values.get("masterdata.street"));
        Div issuerCity = createVariableTemplate("PLZ und Ort", VaadinIcon.BUILDING,
                "masterdata.city", values.get("masterdata.city"));
        Div issuerPhone = createVariableTemplate("Telefon", VaadinIcon.PHONE,
                "masterdata.phone", values.get("masterdata.phone"));
        Div issuerEmail = createVariableTemplate("E-Mail", VaadinIcon.ENVELOPE,
                "masterdata.email", values.get("masterdata.email"));
        Div issuerWebsite = createVariableTemplate("Webseite", VaadinIcon.GLOBE,
                "masterdata.website", values.get("masterdata.website"));
        Div issuerSenderLine = createVariableTemplate("Absenderzeile", VaadinIcon.ENVELOPE_OPEN_O,
                "masterdata.sender_line", values.get("masterdata.sender_line"));
        Div issuerPaymentTerms = createVariableTemplate("Zahlungsbedingungen", VaadinIcon.CREDIT_CARD,
                "masterdata.payment_terms", values.get("masterdata.payment_terms"));
        Div issuerFooter = createVariableTemplate("Fußzeile", VaadinIcon.FILE_TEXT,
                "masterdata.footer", values.get("masterdata.footer"));
        Div invoiceNumber = createVariableTemplate("Rechnungsnummer", VaadinIcon.HASH,
                "masterdata.invoice_number", values.get("masterdata.invoice_number"));
        Div invoiceDate = createVariableTemplate("Rechnungsdatum", VaadinIcon.CALENDAR_CLOCK,
                "masterdata.invoice_date", values.get("masterdata.invoice_date"));

        // Bereich 2: Positionen
        Span servicesHeader = createSectionHeader("Positionen");

        Div servicesListBlock = createServicesVariableTemplate("Positionsliste", VaadinIcon.LIST,
                "services.list", "Position 1: 100,00 €\nPosition 2: 50,00 €");

        // Bereich 3: Empfänger
        Span recipientHeader = createSectionHeader("Rechnungsempfänger");

        Div recipientCompany = createCustomerVariableTemplate("Firma", VaadinIcon.OFFICE,
                "customer.company_name", values.get("customer.company_name"));
        Div recipientName = createCustomerVariableTemplate("Ansprechpartner", VaadinIcon.USER,
                "customer.contact_name", values.get("customer.contact_name"));
        Div recipientStreet = createCustomerVariableTemplate("Straße", VaadinIcon.MAP_MARKER,
                "customer.street", values.get("customer.street"));
        Div recipientCity = createCustomerVariableTemplate("PLZ und Ort", VaadinIcon.BUILDING,
                "customer.city", values.get("customer.city"));
        Div recipientEmail = createCustomerVariableTemplate("E-Mail", VaadinIcon.ENVELOPE,
                "customer.email", values.get("customer.email"));

        // Bereich 4: Freie Elemente
        Span freeHeader = createSectionHeader("Freie Elemente");

        Div textBlock = createDraggableTemplate("Text", VaadinIcon.TEXT_LABEL, "text");
        Div headerBlock = createDraggableTemplate("Überschrift", VaadinIcon.HEADER, "header");
        Div dateBlock = createDraggableTemplate("Datum", VaadinIcon.CALENDAR, "date");
        Div lineBlock = createDraggableTemplate("Linie", VaadinIcon.LINE_V, "line");
        Div imageBlock = createDraggableTemplate("Bild", VaadinIcon.PICTURE, "image");

        panel.add(issuerHeader, issuerCompany, issuerStreet, issuerCity, issuerPhone, issuerEmail, issuerWebsite,
                issuerSenderLine, issuerPaymentTerms, issuerFooter, invoiceNumber, invoiceDate, servicesHeader,
                servicesListBlock, recipientHeader, recipientCompany, recipientName, recipientStreet, recipientCity,
                recipientEmail, freeHeader, textBlock, headerBlock, dateBlock, lineBlock, imageBlock);

        return panel;
    }

    private Span createSectionHeader(String text) {
        Span header = new Span(text);
        header.getStyle().set("font-weight", "bold").set("font-size", "var(--lumo-font-size-m)").set("margin-top",
                "var(--lumo-space-s)");
        return header;
    }

    /**
     * Blauer Baustein: Stammdaten des Rechnungsstellers (masterdata.*).
     */
    private Div createVariableTemplate(String label, VaadinIcon icon, String variable, String defaultText) {
        Div template = new Div();
        template.setText(label);
        template.getStyle().set("padding", "var(--lumo-space-s)").set("margin", "var(--lumo-space-xs) 0")
                .set("background-color", "rgba(25, 118, 210, 0.1)").set("border", "1px solid rgba(25, 118, 210, 0.3)")
                .set("border-radius", "var(--lumo-border-radius-m)").set("cursor", "grab").set("display", "flex")
                .set("align-items", "center").set("gap", "var(--lumo-space-s)").set("user-select", "none")
                .set("font-size", "var(--lumo-font-size-s)").set("color", "#1976d2");

        Icon templateIcon = icon.create();
        templateIcon.setSize("var(--lumo-icon-size-s)");
        template.getElement().insertChild(0, templateIcon.getElement());

        template.getElement().setAttribute("draggable", "true");
        template.getElement().setAttribute("data-template-type", "variable");
        template.getElement().setAttribute("data-template-label", label);
        template.getElement().setAttribute("data-variable", variable);
        template.getElement().setAttribute("data-static-text", defaultText != null ? defaultText : "");

        template.getElement()
                .executeJs("this.addEventListener('dragstart', function(e) {"
                        + "  e.dataTransfer.setData('template-type', this.getAttribute('data-template-type'));"
                        + "  e.dataTransfer.setData('template-label', this.getAttribute('data-template-label'));"
                        + "  e.dataTransfer.setData('variable', this.getAttribute('data-variable'));"
                        + "  e.dataTransfer.setData('static-text', this.getAttribute('data-static-text'));"
                        + "  e.dataTransfer.setData('is-static', 'true');" + "  this.style.opacity = '0.5';" + "});"
                        + "this.addEventListener('dragend', function(e) {" + "  this.style.opacity = '1';" + "});");

        return template;
    }

    /**
     * Grüner Baustein: Empfängerdaten (customer.*).
     */
    private Div createCustomerVariableTemplate(String label, VaadinIcon icon, String variable, String defaultText) {
        Div template = new Div();
        template.setText(label);
        template.getStyle().set("padding", "var(--lumo-space-s)").set("margin", "var(--lumo-space-xs) 0")
                .set("background-color", "rgba(46, 204, 113, 0.15)")
                .set("border", "1px solid rgba(46, 204, 113, 0.4)").set("border-radius", "var(--lumo-border-radius-m)")
                .set("cursor", "grab").set("display", "flex").set("align-items", "center")
                .set("gap", "var(--lumo-space-s)").set("user-select", "none")
                .set("font-size", "var(--lumo-font-size-s)").set("color", "#27ae60");

        Icon templateIcon = icon.create();
        templateIcon.setSize("var(--lumo-icon-size-s)");
        template.getElement().insertChild(0, templateIcon.getElement());

        template.getElement().setAttribute("draggable", "true");
        template.getElement().setAttribute("data-template-type", "variable");
        template.getElement().setAttribute("data-template-label", label);
        template.getElement().setAttribute("data-variable", variable);
        template.getElement().setAttribute("data-static-text", defaultText != null ? defaultText : "");
        template.getElement().setAttribute("data-is-customer", "true");

        template.getElement()
                .executeJs("this.addEventListener('dragstart', function(e) {"
                        + "  e.dataTransfer.setData('template-type', this.getAttribute('data-template-type'));"
                        + "  e.dataTransfer.setData('template-label', this.getAttribute('data-template-label'));"
                        + "  e.dataTransfer.setData('variable', this.getAttribute('data-variable'));"
                        + "  e.dataTransfer.setData('static-text', this.getAttribute('data-static-text'));"
                        + "  e.dataTransfer.setData('is-static', 'true');"
                        + "  e.dataTransfer.setData('is-customer', this.getAttribute('data-is-customer'));"
                        + "  this.style.opacity = '0.5';" + "});" + "this.addEventListener('dragend', function(e) {"
                        + "  this.style.opacity = '1';" + "});");

        return template;
    }

    /**
     * Gelber Baustein: Positionen (services.*).
     */
    private Div createServicesVariableTemplate(String label, VaadinIcon icon, String variable, String defaultText) {
        Div template = new Div();
        template.setText(label);
        template.getStyle().set("padding", "var(--lumo-space-s)").set("margin", "var(--lumo-space-xs) 0")
                .set("background-color", "rgba(255, 193, 7, 0.15)")
                .set("border", "1px solid rgba(255, 193, 7, 0.4)").set("border-radius", "var(--lumo-border-radius-m)")
                .set("cursor", "grab").set("display", "flex").set("align-items", "center")
                .set("gap", "var(--lumo-space-s)").set("user-select", "none")
                .set("font-size", "var(--lumo-font-size-s)").set("color", "#f57c00");

        Icon templateIcon = icon.create();
        templateIcon.setSize("var(--lumo-icon-size-s)");
        template.getElement().insertChild(0, templateIcon.getElement());

        template.getElement().setAttribute("draggable", "true");
        template.getElement().setAttribute("data-template-type", "variable");
        template.getElement().setAttribute("data-template-label", label);
        template.getElement().setAttribute("data-variable", variable);
        template.getElement().setAttribute("data-static-text", defaultText != null ? defaultText : "");
        template.getElement().setAttribute("data-is-customer", "false");
        template.getElement().setAttribute("data-is-services", "true");

        template.getElement()
                .executeJs("this.addEventListener('dragstart', function(e) {"
                        + "  e.dataTransfer.setData('template-type', this.getAttribute('data-template-type'));"
                        + "  e.dataTransfer.setData('template-label', this.getAttribute('data-template-label'));"
                        + "  e.dataTransfer.setData('variable', this.getAttribute('data-variable'));"
                        + "  e.dataTransfer.setData('static-text', this.getAttribute('data-static-text'));"
                        + "  e.dataTransfer.setData('is-static', 'true');"
                        + "  e.dataTransfer.setData('is-customer', this.getAttribute('data-is-customer'));"
                        + "  e.dataTransfer.setData('is-services', this.getAttribute('data-is-services'));"
                        + "  this.style.opacity = '0.5';" + "});" + "this.addEventListener('dragend', function(e) {"
                        + "  this.style.opacity = '1';" + "});");

        return template;
    }

    /**
     * Neutraler Baustein: freie Elemente (Text, Überschrift, Datum, Linie, Bild).
     */
    private Div createDraggableTemplate(String label, VaadinIcon icon, String type) {
        Div template = new Div();
        template.setText(label);
        template.addClassName("invoice-generator-template");

        Icon templateIcon = icon.create();
        templateIcon.setSize("var(--lumo-icon-size-s)");
        template.getElement().insertChild(0, templateIcon.getElement());

        template.getElement().setAttribute("draggable", "true");
        template.getElement().setAttribute("data-template-type", type);
        template.getElement().setAttribute("data-template-label", label);

        template.getElement()
                .executeJs("this.addEventListener('dragstart', function(e) {"
                        + "  e.dataTransfer.setData('template-type', this.getAttribute('data-template-type'));"
                        + "  e.dataTransfer.setData('template-label', this.getAttribute('data-template-label'));"
                        + "  e.dataTransfer.setData('is-static', 'false');" + "  this.style.opacity = '0.5';" + "});"
                        + "this.addEventListener('dragend', function(e) {" + "  this.style.opacity = '1';" + "});");

        return template;
    }

    // ==========================================
    // Mitte: Canvas
    // ==========================================

    private VerticalLayout createCanvasPanel() {
        VerticalLayout panel = new VerticalLayout();
        panel.setPadding(false);
        panel.setSpacing(false);
        panel.setHeightFull();

        // Container-ID muss zu profile-invoice-generator.js passen
        Div canvasContainer = new Div();
        canvasContainer.setId("invoice-canvas-container-profile");
        canvasContainer.setWidth("100%");
        canvasContainer.setHeight("100%");
        canvasContainer.addClassName("invoice-generator-canvas");

        panel.add(canvasContainer);
        panel.expand(canvasContainer);

        return panel;
    }

    // ==========================================
    // Rechtes Panel: Eigenschaften
    // ==========================================

    private VerticalLayout createPropertiesPanel() {
        VerticalLayout panel = new VerticalLayout();
        panel.setPadding(true);
        panel.setSpacing(true);
        panel.setHeightFull();
        panel.getStyle().set("overflow", "auto");
        panel.addClassName("invoice-generator-panel");

        panel.add(propertiesHeader(), propertiesInfo());

        return panel;
    }

    private Span propertiesHeader() {
        Span header = new Span("Eigenschaften");
        header.addClassName("invoice-generator-panel-title");
        return header;
    }

    private Div propertiesInfo() {
        Div info = new Div();
        info.setText("Element auf der Zeichenfläche auswählen, um seine Eigenschaften zu bearbeiten.");
        info.addClassName("invoice-generator-info");
        return info;
    }

    @ClientCallable
    public void updatePropertiesPanel(String elementId, String elementType, String text, Double x, Double y,
            Integer fontSize, String color, Double width, Double height, Boolean isStatic, String variable) {
        getUI().ifPresent(ui -> ui.access(() -> {
            propertiesPanel.removeAll();

            String typeDisplay = "Typ: " + elementType;
            if (variable != null && !variable.isEmpty()) {
                typeDisplay += " (Variable)";
            }
            Span typeLabel = new Span(typeDisplay);
            typeLabel.addClassName("invoice-generator-info");

            propertiesPanel.add(propertiesHeader(), typeLabel);

            // Variable anzeigen wenn vorhanden
            if (variable != null && !variable.isEmpty()) {
                TextField variableField = new TextField("Variable");
                variableField.setValue(variable);
                variableField.setReadOnly(true);
                variableField.setWidthFull();
                variableField.setHelperText(getVariableDescription(variable));
                propertiesPanel.add(variableField);
            }

            // Für Bildelemente: Upload anzeigen
            if ("image".equals(elementType)) {
                MemoryBuffer buffer = new MemoryBuffer();
                Upload upload = new Upload(buffer);
                upload.setAcceptedFileTypes("image/png", "image/jpeg", "image/jpg", "image/gif", "image/webp");
                upload.setMaxFileSize(5 * 1024 * 1024); // 5 MB
                upload.setDropLabel(new Span("Bild hierher ziehen oder klicken"));
                upload.setWidthFull();

                upload.addSucceededListener(event -> {
                    try {
                        byte[] bytes = buffer.getInputStream().readAllBytes();
                        String base64 = Base64.getEncoder().encodeToString(bytes);
                        String dataUrl = "data:" + event.getMIMEType() + ";base64," + base64;

                        getElement()
                                .executeJs("if (window.updateProfileElementImage) { window.updateProfileElementImage('"
                                        + elementId + "', $0); }", dataUrl);
                        showNotification("Bild übernommen.");
                    } catch (Exception ex) {
                        showNotification("Bild konnte nicht geladen werden: " + ex.getMessage());
                    }
                });

                upload.addFileRejectedListener(event ->
                        showNotification("Datei abgelehnt: " + event.getErrorMessage()));

                propertiesPanel.add(upload);
            }

            // Textfeld (nur für Text-Elemente)
            if (!"line".equals(elementType) && !"image".equals(elementType)) {
                TextField textField = new TextField("Text");
                textField.setValue(text != null ? text : "");
                textField.setWidthFull();
                if (Boolean.TRUE.equals(isStatic)) {
                    textField.setReadOnly(true);
                    textField.setHelperText("Wert wird aus den Stammdaten befüllt");
                } else {
                    textField.addValueChangeListener(e -> getElement()
                            .executeJs("if (window.updateProfileElementText) { window.updateProfileElementText('"
                                    + elementId + "', $0); }", e.getValue()));
                }
                propertiesPanel.add(textField);
            }

            // X Position
            TextField xField = new TextField("X Position");
            xField.setValue(x != null ? String.valueOf(Math.round(x)) : "0");
            xField.setWidthFull();
            xField.addValueChangeListener(e -> {
                try {
                    double newX = Double.parseDouble(e.getValue());
                    getElement().executeJs(
                            "if (window.updateProfileElementPosition) { window.updateProfileElementPosition('"
                                    + elementId + "', $0, null); }",
                            newX);
                } catch (NumberFormatException ignored) {
                }
            });
            propertiesPanel.add(xField);

            // Y Position
            TextField yField = new TextField("Y Position");
            yField.setValue(y != null ? String.valueOf(Math.round(y)) : "0");
            yField.setWidthFull();
            yField.addValueChangeListener(e -> {
                try {
                    double newY = Double.parseDouble(e.getValue());
                    getElement().executeJs(
                            "if (window.updateProfileElementPosition) { window.updateProfileElementPosition('"
                                    + elementId + "', null, $0); }",
                            newY);
                } catch (NumberFormatException ignored) {
                }
            });
            propertiesPanel.add(yField);

            // Schriftgröße und Farbe (nur für Text-Elemente)
            if (!"line".equals(elementType) && !"image".equals(elementType)) {
                TextField fontSizeField = new TextField("Schriftgröße");
                fontSizeField.setValue(fontSize != null ? String.valueOf(fontSize) : "16");
                fontSizeField.setWidthFull();
                fontSizeField.addValueChangeListener(e -> {
                    try {
                        int newSize = Integer.parseInt(e.getValue());
                        getElement().executeJs(
                                "if (window.updateProfileElementFontSize) { window.updateProfileElementFontSize('"
                                        + elementId + "', $0); }",
                                newSize);
                    } catch (NumberFormatException ignored) {
                    }
                });
                propertiesPanel.add(fontSizeField);

                // Farbe
                Div colorContainer = new Div();
                colorContainer.getStyle().set("display", "flex").set("align-items", "center")
                        .set("gap", "var(--lumo-space-s)").set("margin-top", "var(--lumo-space-s)");

                Span colorLabel = new Span("Farbe");
                colorLabel.getStyle().set("font-size", "var(--lumo-font-size-s)");

                Input colorPicker = new Input();
                colorPicker.setType("color");
                colorPicker.setValue(color != null ? color : "#333333");
                colorPicker.getStyle().set("width", "50px").set("height", "32px").set("padding", "0")
                        .set("border", "1px solid var(--lumo-contrast-20pct)")
                        .set("border-radius", "var(--lumo-border-radius-m)").set("cursor", "pointer");

                TextField colorHexField = new TextField();
                colorHexField.setValue(color != null ? color : "#333333");
                colorHexField.setWidth("100px");
                colorHexField.getStyle().set("margin", "0");

                colorPicker.addValueChangeListener(e -> {
                    String newColor = e.getValue();
                    colorHexField.setValue(newColor);
                    getElement().executeJs("if (window.updateProfileElementColor) { window.updateProfileElementColor('"
                            + elementId + "', $0); }", newColor);
                });

                colorHexField.addValueChangeListener(e -> {
                    String newColor = e.getValue();
                    if (newColor.matches("^#[0-9A-Fa-f]{6}$")) {
                        colorPicker.setValue(newColor);
                        getElement()
                                .executeJs("if (window.updateProfileElementColor) { window.updateProfileElementColor('"
                                        + elementId + "', $0); }", newColor);
                    }
                });

                colorContainer.add(colorLabel, colorPicker, colorHexField);
                propertiesPanel.add(colorContainer);
            }

            // Löschen-Button
            Button deleteButton = new Button("Löschen", new Icon(VaadinIcon.TRASH));
            deleteButton.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY);
            deleteButton.setWidthFull();
            deleteButton.addClickListener(e -> {
                getElement().executeJs(
                        "if (window.deleteProfileElement) { window.deleteProfileElement('" + elementId + "'); }");
                resetPropertiesPanel();
            });
            propertiesPanel.add(deleteButton);
        }));
    }

    @ClientCallable
    public void resetPropertiesPanel() {
        getUI().ifPresent(ui -> ui.access(() -> {
            if (propertiesPanel == null) {
                return;
            }
            propertiesPanel.removeAll();
            propertiesPanel.add(propertiesHeader(), propertiesInfo());
        }));
    }

    private String getVariableDescription(String variable) {
        return switch (variable) {
        case "masterdata.company_name" -> "Firmenname des Rechnungsstellers";
        case "masterdata.street" -> "Straße und Hausnummer des Rechnungsstellers";
        case "masterdata.city" -> "PLZ und Ort des Rechnungsstellers";
        case "masterdata.phone" -> "Telefonnummer des Rechnungsstellers";
        case "masterdata.email" -> "E-Mail-Adresse des Rechnungsstellers";
        case "masterdata.website" -> "Webseite des Rechnungsstellers";
        case "masterdata.sender_line" -> "Absenderzeile über dem Adressfeld";
        case "masterdata.payment_terms" -> "Zahlungsbedingungen";
        case "masterdata.footer" -> "Fußzeile mit Registerdaten und USt-IdNr.";
        case "masterdata.invoice_number" -> "Rechnungsnummer";
        case "masterdata.invoice_date" -> "Rechnungsdatum";
        case "customer.company_name" -> "Firmenname des Rechnungsempfängers";
        case "customer.contact_name" -> "Ansprechpartner des Rechnungsempfängers";
        case "customer.street" -> "Straße des Rechnungsempfängers";
        case "customer.city" -> "PLZ und Ort des Rechnungsempfängers";
        case "customer.email" -> "E-Mail des Rechnungsempfängers";
        case "services.list" -> "Liste aller Rechnungspositionen";
        case "services.net_total" -> "Nettosumme aller Positionen";
        case "services.gross_total" -> "Bruttosumme aller Positionen";
        default -> "Variable: " + variable;
        };
    }

    // ==========================================
    // Aktionen
    // ==========================================

    private HorizontalLayout createActionButtons() {
        HorizontalLayout layout = new HorizontalLayout();
        layout.setWidth("100%");
        layout.setJustifyContentMode(JustifyContentMode.END);
        layout.setPadding(false);
        layout.setSpacing(true);
        layout.setAlignItems(Alignment.CENTER);
        layout.addClassName("invoice-generator-actionbar");

        templateSelect = new ComboBox<>();
        templateSelect.setPlaceholder("Template auswählen");
        templateSelect.setWidth("220px");
        templateSelect.addValueChangeListener(event -> {
            // Nur auf echte Benutzerauswahl reagieren; programmatische
            // setValue-Aufrufe (Initial-Load, nach dem Speichern) laden nicht erneut.
            if (event.isFromClient() && event.getValue() != null) {
                loadTemplateIntoCanvas(event.getValue());
            }
        });

        Button undoButton = new Button("Rückgängig", new Icon(VaadinIcon.ARROW_BACKWARD));
        undoButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
        undoButton.addClickListener(
                e -> getElement().executeJs("if (window.undoProfileCanvas) { window.undoProfileCanvas(); }"));

        Button previewButton = new Button("Vorschau", new Icon(VaadinIcon.EYE));
        previewButton.addThemeVariants(ButtonVariant.LUMO_CONTRAST);
        previewButton.addClickListener(e -> generatePreviewPdf());

        Button saveTemplateButton = new Button("Template speichern", new Icon(VaadinIcon.CHECK));
        saveTemplateButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        saveTemplateButton.addClickListener(e -> saveTemplate());

        layout.add(templateSelect, undoButton, previewButton, saveTemplateButton);

        return layout;
    }

    private void saveTemplate() {
        getElement().executeJs(
                "return window.getProfileCanvasData ? JSON.stringify(window.getProfileCanvasData()) : null;")
                .then(String.class, templateData -> {
                    if (templateData == null) {
                        showNotification("Zeichenfläche ist nicht bereit.");
                        return;
                    }
                    openSaveDialog(templateData);
                });
    }

    /** Fragt den Template-Namen ab; bei vorhandenem Namen wird das Überschreiben bestätigt. */
    private void openSaveDialog(String templateData) {
        Dialog dialog = new Dialog();
        dialog.setHeaderTitle("Template speichern");

        TextField nameField = new TextField("Name des Templates");
        nameField.setValue(currentTemplateName != null ? currentTemplateName : "");
        nameField.setWidth("300px");
        dialog.add(nameField);

        Button cancelButton = new Button("Abbrechen", e -> dialog.close());
        Button saveButton = new Button("Speichern", e -> {
            String name = nameField.getValue() == null ? "" : nameField.getValue().trim();
            if (name.isEmpty()) {
                nameField.setErrorMessage("Bitte einen Namen angeben.");
                nameField.setInvalid(true);
                return;
            }
            dialog.close();
            if (invoiceTemplateService.templateExists(name)) {
                confirmOverwrite(name, templateData);
            } else {
                doSaveTemplate(name, templateData);
            }
        });
        saveButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        dialog.getFooter().add(cancelButton, saveButton);

        dialog.open();
        nameField.focus();
    }

    /** Rückfrage, ob das bereits vorhandene Template gleichen Namens überschrieben werden soll. */
    private void confirmOverwrite(String name, String templateData) {
        ConfirmDialog confirm = new ConfirmDialog();
        confirm.setHeader("Template überschreiben?");
        confirm.setText("Es existiert bereits ein Template mit dem Namen \"" + name
                + "\". Soll es überschrieben werden?");
        confirm.setConfirmText("Überschreiben");
        confirm.setCancelable(true);
        confirm.setCancelText("Abbrechen");
        confirm.addConfirmListener(e -> doSaveTemplate(name, templateData));
        confirm.addCancelListener(e -> openSaveDialog(templateData));
        confirm.open();
    }

    private void doSaveTemplate(String name, String templateData) {
        try {
            invoiceTemplateService.saveTemplate(name, templateData);
            currentTemplateName = name;
            templateSelect.setItems(invoiceTemplateService.templateNames());
            templateSelect.setValue(name);
            showNotification("Template \"" + name + "\" gespeichert.");
        } catch (Exception ex) {
            showNotification("Template konnte nicht gespeichert werden: " + ex.getMessage());
        }
    }

    private void generatePreviewPdf() {
        getElement().executeJs(
                "return window.getProfileCanvasData ? JSON.stringify(window.getProfileCanvasData()) : null;")
                .then(String.class, templateData -> {
                    if (templateData == null) {
                        showNotification("Zeichenfläche ist nicht bereit.");
                        return;
                    }
                    try {
                        byte[] pdfBytes = templatePdfService.generatePdf(templateData, buildVariables(), VAT_RATE);
                        showPdfInDialog(pdfBytes);
                    } catch (Exception ex) {
                        showNotification("Vorschau konnte nicht erzeugt werden: " + ex.getMessage());
                    }
                });
    }

    private void showPdfInDialog(byte[] pdfBytes) {
        Dialog pdfDialog = new Dialog();
        pdfDialog.setHeaderTitle("PDF-Vorschau");
        pdfDialog.setWidth("90vw");
        pdfDialog.setHeight("90vh");

        Div pdfContainer = new Div();
        pdfContainer.setWidth("100%");
        pdfContainer.setHeight("100%");
        pdfContainer.addClassName("invoice-generator-pdf");

        String base64Pdf = Base64.getEncoder().encodeToString(pdfBytes);
        String dataUrl = "data:application/pdf;base64," + base64Pdf;

        IFrame pdfFrame = new IFrame();
        pdfFrame.setWidth("100%");
        pdfFrame.setHeight("100%");
        pdfFrame.getElement().setAttribute("src", dataUrl);
        pdfFrame.addClassName("invoice-generator-frame");

        pdfContainer.add(pdfFrame);

        Button closeButton = new Button("Schließen", e -> pdfDialog.close());
        closeButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);

        Button downloadButton = new Button("Herunterladen", e -> getElement()
                .executeJs("const link = document.createElement('a');"
                        + "link.href = $0;"
                        + "link.download = 'rechnungstemplate-vorschau.pdf';"
                        + "link.click();", dataUrl));
        downloadButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);

        pdfDialog.add(pdfContainer);
        pdfDialog.getFooter().add(downloadButton, closeButton);
        pdfDialog.open();
    }

    private void showNotification(String message) {
        Notification.show(message, 3000, Notification.Position.BOTTOM_CENTER);
    }
}
