package de.assecutor.votianlt.pages.view;

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.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.HasDynamicTitle;
import com.vaadin.flow.router.Route;
import de.assecutor.votianlt.model.PriceTable;
import de.assecutor.votianlt.model.invoices.SystemInvoiceData;
import de.assecutor.votianlt.pages.base.ui.component.DialogStylingHelper;
import de.assecutor.votianlt.pages.base.ui.view.AdminLayout;
import de.assecutor.votianlt.repository.PriceTableRepository;
import de.assecutor.votianlt.service.CustomerInvoiceService;
import de.assecutor.votianlt.service.InvoiceTemplateService;
import de.assecutor.votianlt.service.SystemCompanyProfileService;
import jakarta.annotation.security.RolesAllowed;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Admin canvas designer for the SYSTEM invoice template: the template used for
 * invoices from Assecutor Data Service GmbH to the users of this system.
 * Mirrors the per-user invoice generator on the profile page (customer
 * invoices), but the issuer master data is fixed to Assecutor and the
 * recipient variables are filled with the invoiced user's data.
 */
@Route(value = "invoice-generator", layout = AdminLayout.class)
@RolesAllowed("ADMIN")
@JsModule("./invoice-generator/profile-invoice-generator.js")
@Slf4j
public class InvoiceGeneratorView extends VerticalLayout implements HasDynamicTitle {

    /**
     * Reserved userId under which the system invoice template is stored in the
     * invoice_templates collection.
     */
    public static final String SYSTEM_TEMPLATE_USER_ID = "SYSTEM";

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

    private final CustomerInvoiceService customerInvoiceService;
    private final InvoiceTemplateService invoiceTemplateService;
    private final PriceTableRepository priceTableRepository;
    private final SystemCompanyProfileService systemCompanyProfileService;

    private VerticalLayout propertiesPanel;

    public InvoiceGeneratorView(CustomerInvoiceService customerInvoiceService,
            InvoiceTemplateService invoiceTemplateService, PriceTableRepository priceTableRepository,
            SystemCompanyProfileService systemCompanyProfileService) {
        this.customerInvoiceService = customerInvoiceService;
        this.invoiceTemplateService = invoiceTemplateService;
        this.priceTableRepository = priceTableRepository;
        this.systemCompanyProfileService = systemCompanyProfileService;

        setId("invoice-generator-view");
        addClassNames("invoice-generator-view", "admin-form-view");

        setSpacing(false);
        setPadding(false);
        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);
        String masterdataJson = buildMasterdataJson();
        getElement().executeJs("window.invoiceGeneratorViewProfile = $0;"
                + "window.profileInvoiceVatRate = " + SYSTEM_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(), masterdataJson, buildCanvasPositionsJson());
    }

    /**
     * Called by JavaScript when the canvas is ready.
     */
    @ClientCallable
    public void onCanvasReady() {
        loadSystemTemplate();
    }

    private void loadSystemTemplate() {
        try {
            Optional<de.assecutor.votianlt.model.InvoiceTemplate> optionalTemplate = invoiceTemplateService
                    .getTemplateByUserId(SYSTEM_TEMPLATE_USER_ID);
            if (optionalTemplate.isEmpty()) {
                return;
            }
            String templateData = optionalTemplate.get().getTemplateData();
            if (templateData == null || templateData.isEmpty()) {
                return;
            }
            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);
        } catch (Exception ex) {
            log.error("Error loading system invoice template: {}", ex.getMessage(), ex);
        }
    }

    /**
     * Build the variable substitution values for the system invoice template.
     * Issuer data comes from SystemInvoiceData (Assecutor Data Service GmbH),
     * recipient values are sample data replaced with the actual user when a real
     * invoice is generated.
     */
    private Map<String, String> buildSystemVariables() {
        SystemInvoiceData data = systemCompanyProfileService.createSystemInvoiceData();
        Map<String, String> variables = new LinkedHashMap<>();
        String companyFull = (safe(data.getCompanyName()) + " " + safe(data.getCompanySubtitle())).trim();
        variables.put("masterdata.company_name", companyFull);
        variables.put("masterdata.street", safe(data.getCompanyStreet()));
        variables.put("masterdata.city", safe(data.getCompanyCity()));
        variables.put("masterdata.phone", safe(data.getCompanyPhone()));
        variables.put("masterdata.email", safe(data.getCompanyEmail()));
        variables.put("masterdata.website", safe(data.getCompanyWebsite()));
        variables.put("masterdata.sender_line", safe(data.getSenderLine()));
        variables.put("masterdata.payment_terms", safe(data.getPaymentTerms()));
        variables.put("masterdata.footer", safe(data.getFooterText()).replace("<br>", "\n"));
        variables.put("masterdata.invoice_number", "SYS-2026-0001");
        variables.put("masterdata.invoice_date",
                LocalDate.now().format(DateTimeFormatter.ofPattern("dd.MM.yyyy")));

        // Empfänger (Nutzer des Systems) - Beispieldaten für Entwurf und Vorschau
        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", "nutzer@beispiel.de");
        variables.put("customer.phone", "0123 456789");

        // Positionen: die Rechnung an die Nutzer enthält ausschließlich die
        // Parameter der Preistabelle (/admin-price-table), keine Leistungen
        addPriceTablePositions(variables);
        return variables;
    }

    /** Example app user count for the template preview (Musterfirma). */
    private static final int SAMPLE_APP_USER_COUNT = 3;

    /**
     * The positions of the price table (/admin-price-table): label plus
     * configured value. These are the only positions on system invoices. The
     * per-app-user fee is shown multiplied by an example user count, since the
     * preview works with sample data anyway.
     */
    private String[][] priceTableEntries() {
        PriceTable priceTable = priceTableRepository.findAll().stream().findFirst().orElse(null);

        String feeLabel = getTranslation("adminpricetable.field.appuserfee");
        String feeValue = priceTable != null ? priceTable.getAppUserFee() : null;
        BigDecimal unitAmount = parseAmount(feeValue != null ? feeValue.trim() : "");
        if (unitAmount != null) {
            feeLabel += " " + getTranslation("admincreateinvoices.appusers.suffix", SAMPLE_APP_USER_COUNT);
            feeValue = formatAmount(unitAmount.multiply(new BigDecimal(SAMPLE_APP_USER_COUNT))) + " €";
        }

        return new String[][] {
                { getTranslation("adminpricetable.field.monthly"),
                        priceTable != null ? priceTable.getMonthlyBasePackage() : null },
                { feeLabel, feeValue } };
    }

    /**
     * JSON for the canvas rendering of the "services.list" block: the
     * price table positions plus totals (window.profileInvoiceServiceData).
     */
    private String buildCanvasPositionsJson() {
        String vatLabel = SYSTEM_VAT_RATE.multiply(new BigDecimal("100")).setScale(0, RoundingMode.HALF_UP) + "%";
        List<Map<String, String>> rows = new ArrayList<>();
        BigDecimal netTotal = BigDecimal.ZERO;

        for (String[] entry : priceTableEntries()) {
            String rawValue = entry[1] != null ? entry[1].trim() : "";
            BigDecimal amount = parseAmount(rawValue);
            String netDisplay;
            if (amount != null) {
                netDisplay = formatAmount(amount) + " €";
                netTotal = netTotal.add(amount);
            } else {
                netDisplay = rawValue.isEmpty() ? "-" : rawValue;
            }
            Map<String, String> row = new HashMap<>();
            row.put("name", entry[0]);
            row.put("vat", vatLabel);
            row.put("net", netDisplay);
            rows.add(row);
        }

        BigDecimal vatTotal = netTotal.multiply(SYSTEM_VAT_RATE);
        Map<String, Object> data = new LinkedHashMap<>();
        data.put("rows", rows);
        data.put("netTotal", formatAmount(netTotal) + " €");
        data.put("vatTotal", formatAmount(vatTotal) + " €");
        data.put("grossTotal", formatAmount(netTotal.add(vatTotal)) + " €");

        try {
            return new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsString(data);
        } catch (Exception ex) {
            log.error("Error serializing canvas positions: {}", ex.getMessage(), ex);
            return "null";
        }
    }

    private void addPriceTablePositions(Map<String, String> variables) {
        List<Map<String, String>> positions = new ArrayList<>();
        BigDecimal netTotal = BigDecimal.ZERO;

        for (String[] entry : priceTableEntries()) {
            String rawValue = entry[1] != null ? entry[1].trim() : "";
            Map<String, String> position = new HashMap<>();
            position.put("name", entry[0]);
            BigDecimal amount = parseAmount(rawValue);
            if (amount != null) {
                position.put("netAmount", formatAmount(amount));
                netTotal = netTotal.add(amount);
            } else {
                // Nicht-numerische Werte unverändert anzeigen
                position.put("netAmount", rawValue.isEmpty() ? "-" : rawValue);
            }
            positions.add(position);
        }

        BigDecimal vatTotal = netTotal.multiply(SYSTEM_VAT_RATE);
        BigDecimal grossTotal = netTotal.add(vatTotal);

        try {
            variables.put("services.json",
                    new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsString(positions));
        } catch (Exception ex) {
            log.error("Error serializing price table positions: {}", ex.getMessage(), ex);
            variables.put("services.json", "[]");
        }
        variables.put("services.count", String.valueOf(positions.size()));
        variables.put("invoice.net_total", formatAmount(netTotal) + " €");
        variables.put("invoice.vat_total", formatAmount(vatTotal) + " €");
        variables.put("invoice.gross_total", formatAmount(grossTotal) + " €");
        variables.put("invoice.vat_rate",
                SYSTEM_VAT_RATE.multiply(new BigDecimal("100")).setScale(0, RoundingMode.HALF_UP) + "%");
        variables.put("services.net_total", formatAmount(netTotal) + " €");
        variables.put("services.gross_total", formatAmount(grossTotal) + " €");
    }

    /**
     * Parse a price table value like "99,00 €", "1.234,56" or "120 EUR" into a
     * numeric amount. Returns null for non-monetary values such as "15 %".
     */
    private BigDecimal parseAmount(String value) {
        if (value == null || value.isBlank() || value.contains("%")) {
            return null;
        }
        Matcher matcher = Pattern.compile("([0-9]{1,3}(?:\\.[0-9]{3})*(?:,[0-9]+)?|[0-9]+(?:[.,][0-9]+)?)")
                .matcher(value);
        if (!matcher.find()) {
            return null;
        }
        String number = matcher.group(1);
        if (number.contains(",")) {
            number = number.replace(".", "").replace(',', '.');
        }
        try {
            return new BigDecimal(number);
        } catch (NumberFormatException ex) {
            return null;
        }
    }

    private String formatAmount(BigDecimal amount) {
        return amount.setScale(2, RoundingMode.HALF_UP).toString().replace(".", ",");
    }

    private String buildMasterdataJson() {
        try {
            return new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsString(buildSystemVariables());
        } catch (Exception ex) {
            log.error("Error serializing masterdata values: {}", ex.getMessage(), ex);
            return "{}";
        }
    }

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

    // ==========================================
    // 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 = buildSystemVariables();

        // Bereich 1: Rechnungssteller (Assecutor Data Service GmbH)
        Span issuerHeader = createSectionHeader(getTranslation("invoicegenerator.issuer.header"));

        Div issuerCompany = createVariableTemplate(getTranslation("profile.company"), VaadinIcon.OFFICE,
                "masterdata.company_name", values.get("masterdata.company_name"));
        Div issuerStreet = createVariableTemplate(getTranslation("profile.street"), VaadinIcon.MAP_MARKER,
                "masterdata.street", values.get("masterdata.street"));
        Div issuerCity = createVariableTemplate(getTranslation("profile.invoice.city"), VaadinIcon.BUILDING,
                "masterdata.city", values.get("masterdata.city"));
        Div issuerPhone = createVariableTemplate(getTranslation("profile.invoice.phone"), VaadinIcon.PHONE,
                "masterdata.phone", values.get("masterdata.phone"));
        Div issuerEmail = createVariableTemplate(getTranslation("profile.invoice.email"), VaadinIcon.ENVELOPE,
                "masterdata.email", values.get("masterdata.email"));
        Div issuerWebsite = createVariableTemplate(getTranslation("invoicegenerator.issuer.website"),
                VaadinIcon.GLOBE, "masterdata.website", values.get("masterdata.website"));
        Div issuerSenderLine = createVariableTemplate(getTranslation("invoicegenerator.issuer.senderline"),
                VaadinIcon.ENVELOPE_OPEN_O, "masterdata.sender_line", values.get("masterdata.sender_line"));
        Div issuerPaymentTerms = createVariableTemplate(getTranslation("invoicegenerator.issuer.paymentterms"),
                VaadinIcon.CREDIT_CARD, "masterdata.payment_terms", values.get("masterdata.payment_terms"));
        Div issuerFooter = createVariableTemplate(getTranslation("invoicegenerator.issuer.footer"),
                VaadinIcon.FILE_TEXT, "masterdata.footer", values.get("masterdata.footer"));
        Div invoiceNumber = createVariableTemplate(getTranslation("profile.invoice.element.invoicenumber"),
                VaadinIcon.HASH, "masterdata.invoice_number", values.get("masterdata.invoice_number"));
        Div invoiceDate = createVariableTemplate(getTranslation("invoicegenerator.issuer.invoicedate"),
                VaadinIcon.CALENDAR_CLOCK, "masterdata.invoice_date", values.get("masterdata.invoice_date"));

        // Bereich 2: Positionen (Parameter der Preistabelle, keine Leistungen)
        Span servicesHeader = createSectionHeader(getTranslation("invoicegenerator.positions.header"));

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

        // Bereich 3: Empfänger (Nutzer des Systems)
        Span recipientHeader = createSectionHeader(getTranslation("invoicegenerator.recipient.header"));

        Div recipientCompany = createCustomerVariableTemplate(getTranslation("invoicegenerator.recipient.company"),
                VaadinIcon.OFFICE, "customer.company_name", values.get("customer.company_name"));
        Div recipientName = createCustomerVariableTemplate(getTranslation("invoicegenerator.recipient.name"),
                VaadinIcon.USER, "customer.contact_name", values.get("customer.contact_name"));
        Div recipientStreet = createCustomerVariableTemplate(getTranslation("invoicegenerator.recipient.street"),
                VaadinIcon.MAP_MARKER, "customer.street", values.get("customer.street"));
        Div recipientCity = createCustomerVariableTemplate(getTranslation("invoicegenerator.recipient.city"),
                VaadinIcon.BUILDING, "customer.city", values.get("customer.city"));
        Div recipientEmail = createCustomerVariableTemplate(getTranslation("invoicegenerator.recipient.email"),
                VaadinIcon.ENVELOPE, "customer.email", values.get("customer.email"));

        // Bereich 4: Freie Elemente
        Span freeHeader = createSectionHeader(getTranslation("profile.invoice.free.elements"));

        Div textBlock = createDraggableTemplate(getTranslation("profile.invoice.element.text"), VaadinIcon.TEXT_LABEL,
                "text");
        Div headerBlock = createDraggableTemplate(getTranslation("profile.invoice.element.header"), VaadinIcon.HEADER,
                "header");
        Div dateBlock = createDraggableTemplate(getTranslation("profile.invoice.element.date"), VaadinIcon.CALENDAR,
                "date");
        Div lineBlock = createDraggableTemplate(getTranslation("profile.invoice.element.line"), VaadinIcon.LINE_V,
                "line");
        Div imageBlock = createDraggableTemplate(getTranslation("profile.invoice.element.image"), 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.* = Nutzer des Systems).
     */
    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: Leistungen/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");

        Span header = new Span(getTranslation("invoicegenerator.properties.title"));
        header.addClassName("invoice-generator-panel-title");

        Div info = new Div();
        info.setText(getTranslation("invoicegenerator.properties.select.info"));
        info.addClassName("invoice-generator-info");

        panel.add(header, info);

        return panel;
    }

    @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();

            Span header = new Span(getTranslation("invoicegenerator.properties.title"));
            header.addClassName("invoice-generator-panel-title");

            String typeDisplay = getTranslation("invoicegenerator.properties.type") + ": " + elementType;
            if (variable != null && !variable.isEmpty()) {
                typeDisplay += " (" + getTranslation("profile.invoice.variable") + ")";
            }
            Span typeLabel = new Span(typeDisplay);
            typeLabel.addClassName("invoice-generator-info");

            propertiesPanel.add(header, typeLabel);

            // Variable anzeigen wenn vorhanden
            if (variable != null && !variable.isEmpty()) {
                TextField variableField = new TextField(getTranslation("profile.invoice.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(getTranslation("invoicegenerator.upload.drop")));
                upload.setWidthFull();

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

                        getElement()
                                .executeJs("if (window.updateProfileElementImage) { window.updateProfileElementImage('"
                                        + elementId + "', $0); }", dataUrl);
                        showNotification(getTranslation("invoicegenerator.upload.success"));
                    } catch (Exception ex) {
                        showNotification(getTranslation("invoicegenerator.upload.error", ex.getMessage()));
                    }
                });

                upload.addFileRejectedListener(event -> {
                    showNotification(getTranslation("invoicegenerator.file.rejected", event.getErrorMessage()));
                });

                propertiesPanel.add(upload);
            }

            // Text Feld (nur für Text-Elemente)
            if (!"line".equals(elementType) && !"image".equals(elementType)) {
                TextField textField = new TextField(getTranslation("profile.invoice.element.text"));
                textField.setValue(text != null ? text : "");
                textField.setWidthFull();
                if (Boolean.TRUE.equals(isStatic)) {
                    textField.setReadOnly(true);
                    textField.setHelperText(getTranslation("profile.invoice.text.from.masterdata"));
                } 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(getTranslation("invoicegenerator.fontsize.label"));
                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(getTranslation("invoicegenerator.color.label"));
                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(getTranslation("invoicegenerator.button.delete"),
                    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();

            Span header = new Span(getTranslation("invoicegenerator.properties.title"));
            header.addClassName("invoice-generator-panel-title");

            Div info = new Div();
            info.setText(getTranslation("invoicegenerator.properties.select.info"));
            info.addClassName("invoice-generator-info");

            propertiesPanel.add(header, info);
        }));
    }

    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 Geschäftsführung, Registerdaten und Bankverbindung";
        case "masterdata.invoice_number" -> "Rechnungsnummer";
        case "masterdata.invoice_date" -> "Rechnungsdatum";
        case "customer.company_name" -> "Firmenname des Nutzers (Rechnungsempfänger)";
        case "customer.contact_name" -> "Name des Nutzers (Rechnungsempfänger)";
        case "customer.street" -> "Straße des Nutzers";
        case "customer.city" -> "PLZ und Ort des Nutzers";
        case "customer.email" -> "E-Mail des Nutzers";
        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");

        Button undoButton = new Button(getTranslation("button.undo"), new Icon(VaadinIcon.ARROW_BACKWARD));
        undoButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
        undoButton.addClickListener(
                e -> getElement().executeJs("if (window.undoProfileCanvas) { window.undoProfileCanvas(); }"));

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

        Button saveTemplateButton = new Button(getTranslation("button.savetemplate"), new Icon(VaadinIcon.CHECK));
        saveTemplateButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        saveTemplateButton.addClickListener(e -> saveSystemTemplate());

        layout.add(undoButton, previewButton, saveTemplateButton);

        return layout;
    }

    private void saveSystemTemplate() {
        getElement().executeJs(
                "if (window.getProfileCanvasData) { return JSON.stringify(window.getProfileCanvasData()); } else { return null; }")
                .then(result -> {
                    if (result == null) {
                        showNotification(getTranslation("invoicegenerator.notification.canvas.error"));
                        return;
                    }
                    try {
                        String templateData;
                        if (result instanceof elemental.json.JsonValue jsonValue) {
                            templateData = jsonValue.toJson();
                        } else {
                            templateData = result.toString();
                        }
                        invoiceTemplateService.saveTemplate(SYSTEM_TEMPLATE_USER_ID, templateData);
                        showNotification(getTranslation("invoicegenerator.template.saved"));
                    } catch (Exception ex) {
                        showNotification(getTranslation("invoicegenerator.template.save.error", ex.getMessage()));
                    }
                });
    }

    private void generatePreviewPdf() {
        try {
            getElement().executeJs(
                    "if (window.getProfileCanvasData) { return window.getProfileCanvasData(); } else { return null; }")
                    .then(result -> {
                        if (result == null) {
                            showNotification(getTranslation("invoicegenerator.notification.canvas.error"));
                            return;
                        }
                        try {
                            String templateData;
                            if (result instanceof elemental.json.JsonValue jsonValue) {
                                templateData = jsonValue.toJson();
                            } else {
                                templateData = result.toString();
                            }
                            byte[] pdfBytes = customerInvoiceService.generatePdfFromCanvasTemplate(templateData,
                                    buildSystemVariables(), SYSTEM_VAT_RATE);
                            showPdfInDialog(pdfBytes);
                        } catch (Exception ex) {
                            showNotification(
                                    getTranslation("invoicegenerator.notification.preview.error", ex.getMessage()));
                        }
                    });
        } catch (Exception ex) {
            showNotification(getTranslation("invoicegenerator.notification.preview.error", ex.getMessage()));
        }
    }

    private void showPdfInDialog(byte[] pdfBytes) {
        Dialog pdfDialog = DialogStylingHelper.createStyledDialog(getTranslation("invoicegenerator.pdf.preview.title"),
                "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(getTranslation("button.close"), e -> pdfDialog.close());
        closeButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);

        Button downloadButton = new Button(getTranslation("button.download"), e -> {
            getElement()
                    .executeJs("const link = document.createElement('a');" + "link.href = 'data:application/pdf;base64,"
                            + base64Pdf + "';" + "link.download = 'vorschau.pdf';" + "link.click();");
        });
        downloadButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);

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

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

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