package de.votian.web.view;

import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.checkbox.CheckboxGroup;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.dialog.Dialog;
import com.vaadin.flow.component.formlayout.FormLayout;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.html.H3;
import com.vaadin.flow.component.html.H4;
import com.vaadin.flow.component.html.Paragraph;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.notification.NotificationVariant;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.tabs.Tab;
import com.vaadin.flow.component.tabs.Tabs;
import com.vaadin.flow.component.textfield.IntegerField;
import com.vaadin.flow.component.textfield.NumberField;
import com.vaadin.flow.component.textfield.PasswordField;
import com.vaadin.flow.component.textfield.TextArea;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.router.BeforeEvent;
import com.vaadin.flow.router.HasUrlParameter;
import com.vaadin.flow.router.OptionalParameter;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import de.votian.web.model.SessionData;
import de.votian.web.service.RestClientService;
import org.springframework.core.ParameterizedTypeReference;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

@Route(value = "customers/:id?", layout = MainLayout.class)
@PageTitle("Votian - Kundendetail")
public class CustomerDetailView extends VerticalLayout implements HasUrlParameter<String> {

    private final RestClientService restClient;
    private final SessionData sessionData;

    private Long customerId;
    private boolean isNew = true;

    private H3 title;

    private final TextField compField = new TextField("Firma");
    private final TextField comp2Field = new TextField("Firma 2");
    private final TextField comp3Field = new TextField("Firma 3");
    private final TextField comp4Field = new TextField("Firma 4");
    private final TextField hsnoField = new TextField("Hausnummer");
    private final TextField streetField = new TextField("Strasse");
    private final TextField zipcodeField = new TextField("PLZ");
    private final TextField cityField = new TextField("Ort");
    private final TextField ilnField = new TextField("ILN");
    private final TextField taxIdField = new TextField("Steuer-Nr.");
    private final TextField staxIdField = new TextField("USt-IdNr.");

    private final TextField eidField = new TextField("Kunden-Nr. (EID)");
    private final NumberField discountField = new NumberField("Rabatt (%)");
    private final CheckboxGroup<GroupOption> groupField = new CheckboxGroup<>();
    private final CheckboxGroup<FilterOption> filterField = new CheckboxGroup<>();
    private final TextField commissionField = new TextField("Kommissions-Nr.");
    private final TextField trackingField = new TextField("Tracking");

    private final TextField accountField = new TextField("Benutzerkonto");
    private final TextField nameField = new TextField("Name");
    private final TextField firstnameField = new TextField("Vorname");
    private final TextField emailField = new TextField("E-Mail");
    private final TextField phoneField = new TextField("Telefon");
    private final TextField phone2Field = new TextField("Telefon 2");
    private final TextField faxField = new TextField("Fax");
    private final PasswordField passwordField = new PasswordField("Passwort");

    private final CheckboxGroup<ServiceOption> servicesField = new CheckboxGroup<>();
    private final CheckboxGroup<ServiceOption> serviceTypesField = new CheckboxGroup<>();
    private final List<GroupOption> groupOptions = new ArrayList<>();
    private final List<FilterOption> filterOptions = new ArrayList<>();
    private final List<ServiceOption> serviceOptions = new ArrayList<>();
    private final List<ServiceOption> serviceTypeOptions = new ArrayList<>();

    private final Grid<ZoneRow> zoneGrid = new Grid<>(ZoneRow.class, false);
    private final Grid<ZoneMappingRow> mappingGrid = new Grid<>(ZoneMappingRow.class, false);
    private final Grid<RadiusRow> radiusGrid = new Grid<>(RadiusRow.class, false);
    private final List<ZoneRow> zones = new ArrayList<>();
    private final List<ZoneMappingRow> mappings = new ArrayList<>();
    private final List<RadiusRow> radii = new ArrayList<>();
    private final ComboBox<DeliveryReasonListConfig> deliveryReasonListField = new ComboBox<>("Liste");
    private final Grid<DeliveryReasonRow> deliveryReasonGrid = new Grid<>(DeliveryReasonRow.class, false);
    private final TextArea deliveryReasonTextField = new TextArea("Hinweistext");
    private final List<DeliveryReasonListConfig> deliveryReasonLists = new ArrayList<>();
    private final ComboBox<AcceptanceProtocolConfig> acceptanceProtocolField = new ComboBox<>("Service");
    private final Grid<AcceptanceQuestionRow> acceptanceQuestionGrid = new Grid<>(AcceptanceQuestionRow.class, false);
    private final TextArea acceptanceProtocolTextField = new TextArea("Protokolltext");
    private final List<AcceptanceProtocolConfig> acceptanceProtocols = new ArrayList<>();

    private final Button addZoneButton = new Button("Zone anlegen", VaadinIcon.PLUS.create());
    private final Button editZoneButton = new Button("Zone bearbeiten", VaadinIcon.EDIT.create());
    private final Button deleteZoneButton = new Button("Zone loeschen", VaadinIcon.TRASH.create());
    private final Button addMappingButton = new Button("PLZ-Zuordnung anlegen", VaadinIcon.PLUS.create());
    private final Button editMappingButton = new Button("PLZ-Zuordnung bearbeiten", VaadinIcon.EDIT.create());
    private final Button deleteMappingButton = new Button("PLZ-Zuordnung loeschen", VaadinIcon.TRASH.create());
    private final Button addRadiusButton = new Button("Radius anlegen", VaadinIcon.PLUS.create());
    private final Button editRadiusButton = new Button("Radius bearbeiten", VaadinIcon.EDIT.create());
    private final Button deleteRadiusButton = new Button("Radius loeschen", VaadinIcon.TRASH.create());
    private final Button addDeliveryReasonButton = new Button("Grund anlegen", VaadinIcon.PLUS.create());
    private final Button editDeliveryReasonButton = new Button("Grund bearbeiten", VaadinIcon.EDIT.create());
    private final Button deleteDeliveryReasonButton = new Button("Grund loeschen", VaadinIcon.TRASH.create());
    private final Button saveDeliveryReasonButton = new Button("Abliefergruende speichern", VaadinIcon.CHECK.create());
    private final Button addAcceptanceQuestionButton = new Button("Frage anlegen", VaadinIcon.PLUS.create());
    private final Button editAcceptanceQuestionButton = new Button("Frage bearbeiten", VaadinIcon.EDIT.create());
    private final Button deleteAcceptanceQuestionButton = new Button("Frage loeschen", VaadinIcon.TRASH.create());
    private final Button saveAcceptanceProtocolButton = new Button("Abnahmeprotokoll speichern", VaadinIcon.CHECK.create());

    public CustomerDetailView(RestClientService restClient, SessionData sessionData) {
        this.restClient = restClient;
        this.sessionData = sessionData;
        setPadding(true);
        setSpacing(true);
        setSizeFull();

        configureServiceGroups();
        configureGroupFilterFields();
        configureZoneGrid();
        configureMappingGrid();
        configureRadiusGrid();
        configureActionButtons();
        configureProtocolFields();
        configureDeliveryReasonGrid();
        configureAcceptanceQuestionGrid();
        configureProtocolButtons();
    }

    @Override
    public void setParameter(BeforeEvent event, @OptionalParameter String parameter) {
        removeAll();
        resetState();
        loadGroupFilterOptions();

        if (parameter != null && !parameter.isEmpty() && !"new".equals(parameter)) {
            try {
                customerId = Long.parseLong(parameter);
                isNew = false;
            } catch (NumberFormatException e) {
                showError("Ungueltige Kunden-ID.");
            }
        }

        buildForm();
        updateTitle();

        if (!isNew && customerId != null) {
            loadCustomer();
            loadServiceConfiguration();
            loadZoneData();
            loadProtocolData();
        }
    }

    private void configureServiceGroups() {
        servicesField.setWidthFull();
        servicesField.setItemLabelGenerator(this::serviceLabel);
        serviceTypesField.setWidthFull();
        serviceTypesField.setItemLabelGenerator(this::serviceLabel);
    }

    private void configureGroupFilterFields() {
        groupField.setLabel("Gruppen");
        groupField.setWidthFull();
        groupField.setItemLabelGenerator(GroupOption::label);

        filterField.setLabel("Filter");
        filterField.setWidthFull();
        filterField.setItemLabelGenerator(FilterOption::label);
    }

    private void configureZoneGrid() {
        zoneGrid.addColumn(ZoneRow::zoneNo).setHeader("Zone").setAutoWidth(true);
        zoneGrid.addColumn(ZoneRow::name).setHeader("Bezeichnung").setFlexGrow(1);
        zoneGrid.addColumn(row -> decimalLabel(row.price())).setHeader("Preis").setAutoWidth(true);
        zoneGrid.addColumn(row -> decimalLabel(row.price1())).setHeader("Preis 1").setAutoWidth(true);
        zoneGrid.addColumn(row -> decimalLabel(row.price2())).setHeader("Preis 2").setAutoWidth(true);
        zoneGrid.addColumn(row -> decimalLabel(row.price3())).setHeader("Preis 3").setAutoWidth(true);
        zoneGrid.setWidthFull();
        zoneGrid.setMinHeight("220px");
        zoneGrid.asSingleSelect().addValueChangeListener(event -> refreshZoneActions());
    }

    private void configureMappingGrid() {
        mappingGrid.addColumn(ZoneMappingRow::zipcode).setHeader("PLZ").setAutoWidth(true);
        mappingGrid.addColumn(ZoneMappingRow::zoneNo).setHeader("Zone").setAutoWidth(true);
        mappingGrid.addColumn(ZoneMappingRow::zoneName).setHeader("Bezeichnung").setFlexGrow(1);
        mappingGrid.addColumn(row -> decimalLabel(row.mappedValue1())).setHeader("MT 1").setAutoWidth(true);
        mappingGrid.addColumn(row -> decimalLabel(row.mappedValue2())).setHeader("MT 2").setAutoWidth(true);
        mappingGrid.addColumn(row -> decimalLabel(row.mappedValue3())).setHeader("MT 3").setAutoWidth(true);
        mappingGrid.setWidthFull();
        mappingGrid.setMinHeight("220px");
        mappingGrid.asSingleSelect().addValueChangeListener(event -> refreshMappingActions());
    }

    private void configureRadiusGrid() {
        radiusGrid.addColumn(RadiusRow::radiusAreaNo).setHeader("Radius").setAutoWidth(true);
        radiusGrid.addColumn(RadiusRow::zipcode).setHeader("PLZ").setAutoWidth(true);
        radiusGrid.addColumn(RadiusRow::cityDistrict).setHeader("Bezirk").setFlexGrow(1);
        radiusGrid.setWidthFull();
        radiusGrid.setMinHeight("220px");
        radiusGrid.asSingleSelect().addValueChangeListener(event -> refreshRadiusActions());
    }

    private void configureProtocolFields() {
        deliveryReasonListField.setWidth("260px");
        deliveryReasonListField.setItemLabelGenerator(DeliveryReasonListConfig::getLabel);
        deliveryReasonListField.addValueChangeListener(event -> refreshDeliveryReasonView());

        deliveryReasonTextField.setWidthFull();
        deliveryReasonTextField.setMinHeight("140px");
        deliveryReasonTextField.setMaxHeight("240px");
        deliveryReasonTextField.addValueChangeListener(event -> {
            DeliveryReasonListConfig selected = deliveryReasonListField.getValue();
            if (selected != null) {
                selected.setText(event.getValue());
            }
        });

        acceptanceProtocolField.setWidth("320px");
        acceptanceProtocolField.setItemLabelGenerator(AcceptanceProtocolConfig::getLabel);
        acceptanceProtocolField.addValueChangeListener(event -> refreshAcceptanceProtocolView());

        acceptanceProtocolTextField.setWidthFull();
        acceptanceProtocolTextField.setMinHeight("160px");
        acceptanceProtocolTextField.setMaxHeight("260px");
        acceptanceProtocolTextField.addValueChangeListener(event -> {
            AcceptanceProtocolConfig selected = acceptanceProtocolField.getValue();
            if (selected != null) {
                selected.setText(event.getValue());
            }
        });
    }

    private void configureDeliveryReasonGrid() {
        deliveryReasonGrid.addColumn(DeliveryReasonRow::getSort).setHeader("Lfd.").setAutoWidth(true);
        deliveryReasonGrid.addColumn(DeliveryReasonRow::getCode).setHeader("Code").setAutoWidth(true);
        deliveryReasonGrid.addColumn(DeliveryReasonRow::getReason).setHeader("Text").setFlexGrow(1);
        deliveryReasonGrid.setWidthFull();
        deliveryReasonGrid.setMinHeight("220px");
        deliveryReasonGrid.asSingleSelect().addValueChangeListener(event -> refreshDeliveryReasonActions());
    }

    private void configureAcceptanceQuestionGrid() {
        acceptanceQuestionGrid.addColumn(AcceptanceQuestionRow::getSort).setHeader("Lfd.").setAutoWidth(true);
        acceptanceQuestionGrid.addColumn(AcceptanceQuestionRow::getQuestion).setHeader("Frage").setFlexGrow(1);
        acceptanceQuestionGrid.addColumn(row -> mailStateLabel(row.getMailState())).setHeader("Mail-Status").setAutoWidth(true);
        acceptanceQuestionGrid.setWidthFull();
        acceptanceQuestionGrid.setMinHeight("220px");
        acceptanceQuestionGrid.asSingleSelect().addValueChangeListener(event -> refreshAcceptanceQuestionActions());
    }

    private void configureActionButtons() {
        addZoneButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        addMappingButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        addRadiusButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);

        deleteZoneButton.addThemeVariants(ButtonVariant.LUMO_ERROR);
        deleteMappingButton.addThemeVariants(ButtonVariant.LUMO_ERROR);
        deleteRadiusButton.addThemeVariants(ButtonVariant.LUMO_ERROR);

        addZoneButton.addClickListener(event -> openZoneDialog(null));
        editZoneButton.addClickListener(event -> {
            ZoneRow selected = zoneGrid.asSingleSelect().getValue();
            if (selected != null) {
                openZoneDialog(selected);
            }
        });
        deleteZoneButton.addClickListener(event -> deleteSelectedZone());

        addMappingButton.addClickListener(event -> openMappingDialog(null));
        editMappingButton.addClickListener(event -> {
            ZoneMappingRow selected = mappingGrid.asSingleSelect().getValue();
            if (selected != null) {
                openMappingDialog(selected);
            }
        });
        deleteMappingButton.addClickListener(event -> deleteSelectedMapping());

        addRadiusButton.addClickListener(event -> openRadiusDialog(null));
        editRadiusButton.addClickListener(event -> {
            RadiusRow selected = radiusGrid.asSingleSelect().getValue();
            if (selected != null) {
                openRadiusDialog(selected);
            }
        });
        deleteRadiusButton.addClickListener(event -> deleteSelectedRadius());

        refreshZoneActions();
        refreshMappingActions();
        refreshRadiusActions();
    }

    private void configureProtocolButtons() {
        saveDeliveryReasonButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        saveAcceptanceProtocolButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        deleteDeliveryReasonButton.addThemeVariants(ButtonVariant.LUMO_ERROR);
        deleteAcceptanceQuestionButton.addThemeVariants(ButtonVariant.LUMO_ERROR);

        addDeliveryReasonButton.addClickListener(event -> openDeliveryReasonDialog(null));
        editDeliveryReasonButton.addClickListener(event -> {
            DeliveryReasonRow selected = deliveryReasonGrid.asSingleSelect().getValue();
            if (selected != null) {
                openDeliveryReasonDialog(selected);
            }
        });
        deleteDeliveryReasonButton.addClickListener(event -> deleteSelectedDeliveryReason());
        saveDeliveryReasonButton.addClickListener(event -> saveDeliveryReasonConfiguration());

        addAcceptanceQuestionButton.addClickListener(event -> openAcceptanceQuestionDialog(null));
        editAcceptanceQuestionButton.addClickListener(event -> {
            AcceptanceQuestionRow selected = acceptanceQuestionGrid.asSingleSelect().getValue();
            if (selected != null) {
                openAcceptanceQuestionDialog(selected);
            }
        });
        deleteAcceptanceQuestionButton.addClickListener(event -> deleteSelectedAcceptanceQuestion());
        saveAcceptanceProtocolButton.addClickListener(event -> saveAcceptanceProtocolConfiguration());

        refreshDeliveryReasonActions();
        refreshAcceptanceQuestionActions();
    }

    private void buildForm() {
        title = new H3();

        Tab companyTab = new Tab("Firma");
        Tab contactTab = new Tab("Kontakt");
        Tab settingsTab = new Tab("Einstellungen");
        Tab servicesTab = new Tab("Leistungen");
        Tab areasTab = new Tab("Zonen/Radien");
        Tab protocolsTab = new Tab("Protokolle");
        Tabs tabs = new Tabs(companyTab, contactTab, settingsTab, servicesTab, areasTab, protocolsTab);

        VerticalLayout companyLayout = buildCompanyLayout();
        VerticalLayout contactLayout = buildContactLayout();
        VerticalLayout settingsLayout = buildSettingsLayout();
        VerticalLayout servicesLayout = buildServicesLayout();
        VerticalLayout areasLayout = buildAreasLayout();
        VerticalLayout protocolsLayout = buildProtocolsLayout();

        List<Component> sections = List.of(companyLayout, contactLayout, settingsLayout, servicesLayout, areasLayout, protocolsLayout);
        sections.forEach(section -> section.setVisible(false));
        companyLayout.setVisible(true);

        tabs.addSelectedChangeListener(event -> {
            companyLayout.setVisible(event.getSelectedTab() == companyTab);
            contactLayout.setVisible(event.getSelectedTab() == contactTab);
            settingsLayout.setVisible(event.getSelectedTab() == settingsTab);
            servicesLayout.setVisible(event.getSelectedTab() == servicesTab);
            areasLayout.setVisible(event.getSelectedTab() == areasTab);
            protocolsLayout.setVisible(event.getSelectedTab() == protocolsTab);
        });

        Button saveButton = new Button("Speichern", VaadinIcon.CHECK.create(), event -> saveCustomer());
        saveButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        saveButton.setEnabled(canEditCustomer());
        Button cancelButton = new Button("Abbrechen", event ->
                getUI().ifPresent(ui -> ui.navigate(CustomerListView.class)));

        add(title, tabs, companyLayout, contactLayout, settingsLayout, servicesLayout, areasLayout, protocolsLayout,
                new HorizontalLayout(saveButton, cancelButton));
    }

    private VerticalLayout buildCompanyLayout() {
        FormLayout form = new FormLayout();
        form.setResponsiveSteps(
                new FormLayout.ResponsiveStep("0", 1),
                new FormLayout.ResponsiveStep("700px", 2)
        );
        compField.setWidthFull();
        comp2Field.setWidthFull();
        streetField.setWidthFull();
        form.add(compField, comp2Field, comp3Field, comp4Field,
                streetField, hsnoField, zipcodeField, cityField, ilnField, taxIdField, staxIdField);

        VerticalLayout layout = new VerticalLayout(form);
        layout.setPadding(false);
        layout.setSpacing(true);
        return layout;
    }

    private VerticalLayout buildContactLayout() {
        FormLayout form = new FormLayout();
        form.setResponsiveSteps(
                new FormLayout.ResponsiveStep("0", 1),
                new FormLayout.ResponsiveStep("700px", 2)
        );
        form.add(nameField, firstnameField, emailField, phoneField,
                phone2Field, faxField, accountField, passwordField);

        VerticalLayout layout = new VerticalLayout(form);
        layout.setPadding(false);
        layout.setSpacing(true);
        return layout;
    }

    private VerticalLayout buildSettingsLayout() {
        FormLayout form = new FormLayout();
        form.setResponsiveSteps(
                new FormLayout.ResponsiveStep("0", 1),
                new FormLayout.ResponsiveStep("700px", 2)
        );
        trackingField.setWidthFull();
        form.add(eidField, discountField, trackingField, commissionField, groupField, filterField);
        form.setColspan(groupField, 2);
        form.setColspan(filterField, 2);

        VerticalLayout layout = new VerticalLayout(form);
        layout.setPadding(false);
        layout.setSpacing(true);
        return layout;
    }

    private VerticalLayout buildServicesLayout() {
        if (customerId == null) {
            VerticalLayout layout = new VerticalLayout(new Paragraph(
                    "Leistungen und Leistungstypen koennen nach dem ersten Speichern des Kunden gepflegt werden."));
            layout.setPadding(false);
            layout.setSpacing(true);
            return layout;
        }

        Button saveServicesButton = new Button("Leistungszuordnung speichern", event -> saveServiceConfiguration());
        saveServicesButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);

        VerticalLayout servicesSection = new VerticalLayout(
                new H4("Kundenleistungen"),
                new Paragraph("Zulassige Leistungen aus dem zentralen Leistungskatalog."),
                servicesField
        );
        servicesSection.setPadding(false);
        servicesSection.setSpacing(true);

        VerticalLayout serviceTypesSection = new VerticalLayout(
                new H4("Leistungstypen"),
                new Paragraph("Zulassige Leistungstypen fuer den Kunden."),
                serviceTypesField
        );
        serviceTypesSection.setPadding(false);
        serviceTypesSection.setSpacing(true);

        VerticalLayout layout = new VerticalLayout(servicesSection, serviceTypesSection, saveServicesButton);
        layout.setPadding(false);
        layout.setSpacing(true);
        return layout;
    }

    private VerticalLayout buildAreasLayout() {
        if (customerId == null) {
            VerticalLayout layout = new VerticalLayout(new Paragraph(
                    "Zonen und Radiusgebiete koennen nach dem ersten Speichern des Kunden gepflegt werden."));
            layout.setPadding(false);
            layout.setSpacing(true);
            return layout;
        }

        VerticalLayout zoneSection = new VerticalLayout(
                new H4("Zonen"),
                new Paragraph("Definition der servicebezogenen Kundenzonen inklusive zonenspezifischer Preise."),
                new HorizontalLayout(addZoneButton, editZoneButton, deleteZoneButton),
                zoneGrid
        );
        zoneSection.setPadding(false);
        zoneSection.setSpacing(true);

        VerticalLayout mappingSection = new VerticalLayout(
                new H4("PLZ-Zuordnungen"),
                new Paragraph("Zuordnung einzelner Postleitzahlen zu den Kundenzonen mit optionalen Mapping-Werten."),
                new HorizontalLayout(addMappingButton, editMappingButton, deleteMappingButton),
                mappingGrid
        );
        mappingSection.setPadding(false);
        mappingSection.setSpacing(true);

        VerticalLayout radiusSection = new VerticalLayout(
                new H4("Radiusbereiche"),
                new Paragraph("Pflege von Radiusgebieten analog zum Legacy-Modul."),
                new HorizontalLayout(addRadiusButton, editRadiusButton, deleteRadiusButton),
                radiusGrid
        );
        radiusSection.setPadding(false);
        radiusSection.setSpacing(true);

        VerticalLayout layout = new VerticalLayout(zoneSection, mappingSection, radiusSection);
        layout.setPadding(false);
        layout.setSpacing(true);
        return layout;
    }

    private VerticalLayout buildProtocolsLayout() {
        if (customerId == null) {
            VerticalLayout layout = new VerticalLayout(new Paragraph(
                    "Abliefergruende und Abnahmeprotokolle koennen nach dem ersten Speichern des Kunden gepflegt werden."));
            layout.setPadding(false);
            layout.setSpacing(true);
            return layout;
        }

        VerticalLayout deliverySection = new VerticalLayout(
                new H4("Abliefergruende"),
                new Paragraph("Pflege der mobilen Ablieferlisten analog zum PHP-Modul mit vier getrennten Listen."),
                new HorizontalLayout(deliveryReasonListField, saveDeliveryReasonButton),
                new HorizontalLayout(addDeliveryReasonButton, editDeliveryReasonButton, deleteDeliveryReasonButton),
                deliveryReasonGrid,
                deliveryReasonTextField
        );
        deliverySection.setPadding(false);
        deliverySection.setSpacing(true);

        VerticalLayout acceptanceSection = new VerticalLayout(
                new H4("Abnahmeprotokolle"),
                new Paragraph("Fragenkataloge und Protokolltexte pro Service fuer das spaetere Auftragsprotokoll."),
                new HorizontalLayout(acceptanceProtocolField, saveAcceptanceProtocolButton),
                new HorizontalLayout(addAcceptanceQuestionButton, editAcceptanceQuestionButton, deleteAcceptanceQuestionButton),
                acceptanceQuestionGrid,
                acceptanceProtocolTextField
        );
        acceptanceSection.setPadding(false);
        acceptanceSection.setSpacing(true);

        VerticalLayout layout = new VerticalLayout(deliverySection, acceptanceSection);
        layout.setPadding(false);
        layout.setSpacing(true);
        return layout;
    }

    private void updateTitle() {
        if (title != null) {
            title.setText(isNew ? "Neuer Kunde" : "Kunde bearbeiten: " + customerId);
        }
    }

    private void loadCustomer() {
        try {
            @SuppressWarnings("unchecked")
            Map<String, Object> customer = restClient.get("/customers/" + customerId, LinkedHashMap.class);
            if (customer == null || customer.isEmpty()) {
                showError("Kunde wurde nicht gefunden.");
                return;
            }

            eidField.setValue(str(customer.get("eid")));
            discountField.setValue(customer.get("discount") != null ? ((Number) customer.get("discount")).doubleValue() : null);
            groupField.setValue(selectGroups(str(customer.get("group"))));
            filterField.setValue(selectFilters(str(customer.get("filter"))));
            commissionField.setValue(str(customer.get("commissionNo")));
            trackingField.setValue(str(customer.get("tracking")));
            compField.setValue(str(customer.get("companyName")));
            comp2Field.setValue(str(customer.get("companyName2")));
            comp3Field.setValue(str(customer.get("companyName3")));
            comp4Field.setValue(str(customer.get("companyName4")));
            hsnoField.setValue(str(customer.get("hsno")));
            streetField.setValue(str(customer.get("street")));
            zipcodeField.setValue(str(customer.get("zipcode")));
            cityField.setValue(str(customer.get("city")));
            ilnField.setValue(str(customer.get("iln")));
            taxIdField.setValue(str(customer.get("taxIdNo")));
            staxIdField.setValue(str(customer.get("staxIdNo")));
            accountField.setValue(str(customer.get("account")));
            nameField.setValue(str(customer.get("name")));
            firstnameField.setValue(str(customer.get("firstname")));
            emailField.setValue(str(customer.get("email")));
            phoneField.setValue(str(customer.get("phone")));
            phone2Field.setValue(str(customer.get("phone2")));
            faxField.setValue(str(customer.get("fax")));
        } catch (Exception e) {
            showError("Fehler beim Laden: " + e.getMessage());
        }
    }

    private void loadServiceConfiguration() {
        if (customerId == null) {
            clearServiceConfiguration();
            return;
        }

        try {
            @SuppressWarnings("unchecked")
            Map<String, Object> config = restClient.get("/customers/" + customerId + "/service-config", LinkedHashMap.class);
            serviceOptions.clear();
            serviceOptions.addAll(readOptions(config.get("availableServices")));
            serviceOptions.sort(Comparator.comparing(CustomerDetailView::serviceOrderLabel, String::compareToIgnoreCase));
            servicesField.setItems(serviceOptions);
            servicesField.setValue(selectOptions(serviceOptions, toLongSet(config.get("selectedServiceIds"))));

            serviceTypeOptions.clear();
            serviceTypeOptions.addAll(readOptions(config.get("availableServiceTypes")));
            serviceTypeOptions.sort(Comparator.comparing(CustomerDetailView::serviceOrderLabel, String::compareToIgnoreCase));
            serviceTypesField.setItems(serviceTypeOptions);
            serviceTypesField.setValue(selectOptions(serviceTypeOptions, toLongSet(config.get("selectedServiceTypeIds"))));
        } catch (Exception e) {
            showError("Leistungskonfiguration konnte nicht geladen werden: " + e.getMessage());
        }
    }

    private void saveServiceConfiguration() {
        if (customerId == null) {
            return;
        }

        try {
            Map<String, Object> request = new HashMap<>();
            request.put("serviceIds", orderedSelection(serviceOptions, servicesField.getValue()));
            request.put("serviceTypeIds", orderedSelection(serviceTypeOptions, serviceTypesField.getValue()));
            restClient.put("/customers/" + customerId + "/service-config", request);
            Notification.show("Leistungszuordnung gespeichert.", 3000, Notification.Position.MIDDLE)
                    .addThemeVariants(NotificationVariant.LUMO_SUCCESS);
            loadServiceConfiguration();
        } catch (Exception e) {
            showError("Leistungszuordnung konnte nicht gespeichert werden: " + e.getMessage());
        }
    }

    private void loadZoneData() {
        if (customerId == null) {
            zones.clear();
            mappings.clear();
            radii.clear();
            zoneGrid.setItems(zones);
            mappingGrid.setItems(mappings);
            radiusGrid.setItems(radii);
            return;
        }

        try {
            zones.clear();
            Object[] zoneResult = restClient.get("/customers/" + customerId + "/service-zones", Object[].class);
            for (Map<String, Object> row : toMapList(zoneResult)) {
                zones.add(new ZoneRow(
                        toLong(row.get("id")),
                        toInteger(row.get("zoneNo")),
                        str(row.get("name")),
                        toBigDecimal(row.get("price")),
                        toBigDecimal(row.get("price1")),
                        toBigDecimal(row.get("price2")),
                        toBigDecimal(row.get("price3"))
                ));
            }
            zones.sort(Comparator.comparing(ZoneRow::zoneNo, Comparator.nullsLast(Integer::compareTo)));
            zoneGrid.setItems(zones);
            zoneGrid.asSingleSelect().clear();

            mappings.clear();
            Object[] mappingResult = restClient.get("/customers/" + customerId + "/service-zone-mappings", Object[].class);
            for (Map<String, Object> row : toMapList(mappingResult)) {
                mappings.add(new ZoneMappingRow(
                        toLong(row.get("zoneId")),
                        toInteger(row.get("zoneNo")),
                        str(row.get("zoneName")),
                        toLong(row.get("zipCodeId")),
                        str(row.get("zipcode")),
                        toBigDecimal(row.get("mappedValue1")),
                        toBigDecimal(row.get("mappedValue2")),
                        toBigDecimal(row.get("mappedValue3"))
                ));
            }
            mappingGrid.setItems(mappings);
            mappingGrid.asSingleSelect().clear();

            radii.clear();
            Object[] radiusResult = restClient.get("/customers/" + customerId + "/service-radii", Object[].class);
            for (Map<String, Object> row : toMapList(radiusResult)) {
                radii.add(new RadiusRow(
                        toLong(row.get("id")),
                        str(row.get("zipcode")),
                        str(row.get("cityDistrict")),
                        toInteger(row.get("radiusAreaNo"))
                ));
            }
            radiusGrid.setItems(radii);
            radiusGrid.asSingleSelect().clear();

            refreshZoneActions();
            refreshMappingActions();
            refreshRadiusActions();
        } catch (Exception e) {
            showError("Zonen- und Radiusdaten konnten nicht geladen werden: " + e.getMessage());
        }
    }

    private void loadProtocolData() {
        Integer selectedListNo = deliveryReasonListField.getValue() != null ? deliveryReasonListField.getValue().getListNo() : null;
        Integer selectedServiceKey = acceptanceProtocolField.getValue() != null ? acceptanceProtocolField.getValue().getServiceKey() : null;
        loadProtocolData(selectedListNo, selectedServiceKey);
    }

    private void loadProtocolData(Integer selectedListNo, Integer selectedServiceKey) {
        if (customerId == null) {
            deliveryReasonLists.clear();
            deliveryReasonListField.clear();
            deliveryReasonListField.setItems(deliveryReasonLists);
            deliveryReasonGrid.setItems(List.of());
            deliveryReasonTextField.clear();

            acceptanceProtocols.clear();
            acceptanceProtocolField.clear();
            acceptanceProtocolField.setItems(acceptanceProtocols);
            acceptanceQuestionGrid.setItems(List.of());
            acceptanceProtocolTextField.clear();
            return;
        }

        try {
            @SuppressWarnings("unchecked")
            Map<String, Object> deliveryConfig = restClient.get("/customers/" + customerId + "/delivery-reasons", LinkedHashMap.class);
            deliveryReasonLists.clear();
            deliveryReasonLists.addAll(readDeliveryReasonLists(deliveryConfig.get("lists")));
            deliveryReasonListField.setItems(deliveryReasonLists);
            deliveryReasonListField.setValue(findDeliveryReasonList(selectedListNo));
            if (deliveryReasonListField.getValue() == null && !deliveryReasonLists.isEmpty()) {
                deliveryReasonListField.setValue(deliveryReasonLists.get(0));
            }
            refreshDeliveryReasonView();

            @SuppressWarnings("unchecked")
            Map<String, Object> acceptanceConfig = restClient.get("/customers/" + customerId + "/acceptance-protocols", LinkedHashMap.class);
            acceptanceProtocols.clear();
            acceptanceProtocols.addAll(readAcceptanceProtocols(acceptanceConfig.get("protocols")));
            acceptanceProtocolField.setItems(acceptanceProtocols);
            acceptanceProtocolField.setValue(findAcceptanceProtocol(selectedServiceKey));
            if (acceptanceProtocolField.getValue() == null && !acceptanceProtocols.isEmpty()) {
                acceptanceProtocolField.setValue(acceptanceProtocols.get(0));
            }
            refreshAcceptanceProtocolView();
        } catch (Exception e) {
            showError("Protokollkonfiguration konnte nicht geladen werden: " + e.getMessage());
        }
    }

    private void saveCustomer() {
        if (!canEditCustomer()) {
            showError("Keine Berechtigung zum Bearbeiten von Kunden.");
            return;
        }
        try {
            if (isNew) {
                Map<String, Object> request = new HashMap<>();
                Map<String, Object> customer = new HashMap<>();
                customer.put("headquartersId", sessionData.getHeadquartersId());
                customer.put("eid", eidField.getValue());
                customer.put("discount", discountField.getValue());
                customer.put("group", toGroupString(groupField.getValue()));
                customer.put("filter", toFilterString(filterField.getValue()));
                customer.put("commissionNo", commissionField.getValue());
                customer.put("tracking", trackingField.getValue());
                request.put("customer", customer);

                Map<String, Object> company = buildCompanyPayload();
                request.put("company", company);

                Map<String, Object> user = buildUserPayload();
                request.put("user", user);
                request.put("password", passwordField.getValue());

                @SuppressWarnings("unchecked")
                Map<String, Object> created = restClient.post("/customers", request, LinkedHashMap.class);
                Long createdId = toLong(created.get("id"));
                if (createdId == null) {
                    throw new RuntimeException("Neue Kunden-ID fehlt in der Antwort.");
                }
                Notification.show("Kunde gespeichert!", 3000, Notification.Position.MIDDLE)
                        .addThemeVariants(NotificationVariant.LUMO_SUCCESS);
                getUI().ifPresent(ui -> ui.navigate("customers/" + createdId));
            } else {
                Map<String, Object> request = new HashMap<>();
                Map<String, Object> customer = new HashMap<>();
                customer.put("discount", discountField.getValue());
                customer.put("group", toGroupString(groupField.getValue()));
                customer.put("filter", toFilterString(filterField.getValue()));
                customer.put("commissionNo", commissionField.getValue());
                customer.put("tracking", trackingField.getValue());
                request.put("customer", customer);
                request.put("company", buildCompanyPayload());
                request.put("user", buildUserPayload());

                restClient.put("/customers/" + customerId, request);
                Notification.show("Kunde gespeichert!", 3000, Notification.Position.MIDDLE)
                        .addThemeVariants(NotificationVariant.LUMO_SUCCESS);
                loadCustomer();
            }
        } catch (Exception e) {
            showError("Fehler beim Speichern: " + e.getMessage());
        }
    }

    private Map<String, Object> buildCompanyPayload() {
        Map<String, Object> company = new HashMap<>();
        company.put("comp", compField.getValue());
        company.put("comp2", comp2Field.getValue());
        company.put("comp3", comp3Field.getValue());
        company.put("comp4", comp4Field.getValue());
        company.put("hsno", hsnoField.getValue());
        company.put("iln", ilnField.getValue());
        company.put("taxIdNo", taxIdField.getValue());
        company.put("staxIdNo", staxIdField.getValue());
        return company;
    }

    private Map<String, Object> buildUserPayload() {
        Map<String, Object> user = new HashMap<>();
        user.put("account", accountField.getValue());
        user.put("name", nameField.getValue());
        user.put("firstname", firstnameField.getValue());
        user.put("email", emailField.getValue());
        user.put("phone", phoneField.getValue());
        user.put("phone2", phone2Field.getValue());
        user.put("fax", faxField.getValue());
        return user;
    }

    private void openZoneDialog(ZoneRow existing) {
        if (!canEditCustomer()) {
            showError("Keine Berechtigung zur Pflege von Kundenkonfigurationen.");
            return;
        }
        if (customerId == null) {
            return;
        }

        Dialog dialog = new Dialog();
        dialog.setHeaderTitle(existing == null ? "Zone anlegen" : "Zone bearbeiten");

        TextField nameField = new TextField("Bezeichnung");
        nameField.setWidthFull();
        NumberField priceField = new NumberField("Preis");
        priceField.setWidthFull();
        NumberField price1Field = new NumberField("Preis 1");
        price1Field.setWidthFull();
        NumberField price2Field = new NumberField("Preis 2");
        price2Field.setWidthFull();
        NumberField price3Field = new NumberField("Preis 3");
        price3Field.setWidthFull();

        if (existing != null) {
            nameField.setValue(existing.name());
            priceField.setValue(asDouble(existing.price()));
            price1Field.setValue(asDouble(existing.price1()));
            price2Field.setValue(asDouble(existing.price2()));
            price3Field.setValue(asDouble(existing.price3()));
        }

        Button saveButton = new Button("Speichern", event -> {
            if (nameField.getValue().isBlank()) {
                showError("Bitte eine Bezeichnung eingeben.");
                return;
            }

            try {
                Map<String, Object> request = new HashMap<>();
                request.put("name", nameField.getValue());
                request.put("price", decimal(priceField.getValue()));
                request.put("price1", decimal(price1Field.getValue()));
                request.put("price2", decimal(price2Field.getValue()));
                request.put("price3", decimal(price3Field.getValue()));

                if (existing == null) {
                    restClient.post("/customers/" + customerId + "/service-zones", request, LinkedHashMap.class);
                } else {
                    restClient.put("/customers/" + customerId + "/service-zones/" + existing.id(), request);
                }
                loadZoneData();
                dialog.close();
            } catch (Exception e) {
                showError("Zone konnte nicht gespeichert werden: " + e.getMessage());
            }
        });
        saveButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);

        Button cancelButton = new Button("Abbrechen", event -> dialog.close());

        FormLayout form = new FormLayout();
        form.setResponsiveSteps(
                new FormLayout.ResponsiveStep("0", 1),
                new FormLayout.ResponsiveStep("650px", 2)
        );
        form.add(nameField, priceField, price1Field, price2Field, price3Field);

        dialog.add(new VerticalLayout(form, new HorizontalLayout(saveButton, cancelButton)));
        dialog.open();
    }

    private void openMappingDialog(ZoneMappingRow existing) {
        if (!canEditCustomer()) {
            showError("Keine Berechtigung zur Pflege von Kundenkonfigurationen.");
            return;
        }
        if (customerId == null) {
            return;
        }
        if (zones.isEmpty()) {
            showError("Zuerst mindestens eine Zone anlegen.");
            return;
        }

        Dialog dialog = new Dialog();
        dialog.setHeaderTitle(existing == null ? "PLZ-Zuordnung anlegen" : "PLZ-Zuordnung bearbeiten");

        ComboBox<ZoneRow> zoneField = new ComboBox<>("Zone");
        zoneField.setItems(zones);
        zoneField.setItemLabelGenerator(zone -> zone.zoneNo() + " - " + zone.name());
        zoneField.setWidthFull();

        TextField zipcodeField = new TextField("PLZ");
        zipcodeField.setWidthFull();
        NumberField mt1Field = new NumberField("MT 1");
        mt1Field.setWidthFull();
        NumberField mt2Field = new NumberField("MT 2");
        mt2Field.setWidthFull();
        NumberField mt3Field = new NumberField("MT 3");
        mt3Field.setWidthFull();

        if (existing != null) {
            zoneField.setValue(findZone(existing.zoneId()));
            zipcodeField.setValue(existing.zipcode());
            mt1Field.setValue(asDouble(existing.mappedValue1()));
            mt2Field.setValue(asDouble(existing.mappedValue2()));
            mt3Field.setValue(asDouble(existing.mappedValue3()));
        } else {
            zoneField.setValue(zones.get(0));
        }

        Button saveButton = new Button("Speichern", event -> {
            if (zoneField.getValue() == null || zipcodeField.getValue().isBlank()) {
                showError("Bitte Zone und PLZ angeben.");
                return;
            }

            try {
                Map<String, Object> request = new HashMap<>();
                request.put("zoneId", zoneField.getValue().id());
                request.put("zipcode", zipcodeField.getValue());
                request.put("mappedValue1", decimal(mt1Field.getValue()));
                request.put("mappedValue2", decimal(mt2Field.getValue()));
                request.put("mappedValue3", decimal(mt3Field.getValue()));

                if (existing == null) {
                    restClient.post("/customers/" + customerId + "/service-zone-mappings", request, LinkedHashMap.class);
                } else {
                    restClient.put("/customers/" + customerId + "/service-zone-mappings/" + existing.zoneId() + "/" + existing.zipCodeId(), request);
                }
                loadZoneData();
                dialog.close();
            } catch (Exception e) {
                showError("PLZ-Zuordnung konnte nicht gespeichert werden: " + e.getMessage());
            }
        });
        saveButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);

        Button cancelButton = new Button("Abbrechen", event -> dialog.close());

        FormLayout form = new FormLayout();
        form.setResponsiveSteps(
                new FormLayout.ResponsiveStep("0", 1),
                new FormLayout.ResponsiveStep("650px", 2)
        );
        form.add(zoneField, zipcodeField, mt1Field, mt2Field, mt3Field);

        dialog.add(new VerticalLayout(form, new HorizontalLayout(saveButton, cancelButton)));
        dialog.open();
    }

    private void openRadiusDialog(RadiusRow existing) {
        if (!canEditCustomer()) {
            showError("Keine Berechtigung zur Pflege von Kundenkonfigurationen.");
            return;
        }
        if (customerId == null) {
            return;
        }

        Dialog dialog = new Dialog();
        dialog.setHeaderTitle(existing == null ? "Radius anlegen" : "Radius bearbeiten");

        IntegerField radiusAreaField = new IntegerField("Radiusbereich");
        radiusAreaField.setWidthFull();
        TextField zipcodeField = new TextField("PLZ");
        zipcodeField.setWidthFull();
        TextField districtField = new TextField("Bezirk");
        districtField.setWidthFull();

        if (existing != null) {
            radiusAreaField.setValue(existing.radiusAreaNo());
            zipcodeField.setValue(existing.zipcode());
            districtField.setValue(existing.cityDistrict());
        }

        Button saveButton = new Button("Speichern", event -> {
            if (radiusAreaField.getValue() == null || zipcodeField.getValue().isBlank() || districtField.getValue().isBlank()) {
                showError("Bitte Radiusbereich, PLZ und Bezirk angeben.");
                return;
            }

            try {
                Map<String, Object> request = new HashMap<>();
                request.put("radiusAreaNo", radiusAreaField.getValue());
                request.put("zipcode", zipcodeField.getValue());
                request.put("cityDistrict", districtField.getValue());

                if (existing == null) {
                    restClient.post("/customers/" + customerId + "/service-radii", request, LinkedHashMap.class);
                } else {
                    restClient.put("/customers/" + customerId + "/service-radii/" + existing.id(), request);
                }
                loadZoneData();
                dialog.close();
            } catch (Exception e) {
                showError("Radius konnte nicht gespeichert werden: " + e.getMessage());
            }
        });
        saveButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);

        Button cancelButton = new Button("Abbrechen", event -> dialog.close());
        dialog.add(new VerticalLayout(radiusAreaField, zipcodeField, districtField, new HorizontalLayout(saveButton, cancelButton)));
        dialog.open();
    }

    private void deleteSelectedZone() {
        if (!canEditCustomer()) {
            return;
        }
        ZoneRow selected = zoneGrid.asSingleSelect().getValue();
        if (selected == null || customerId == null) {
            return;
        }
        try {
            restClient.delete("/customers/" + customerId + "/service-zones/" + selected.id());
            loadZoneData();
        } catch (Exception e) {
            showError("Zone konnte nicht geloescht werden: " + e.getMessage());
        }
    }

    private void deleteSelectedMapping() {
        if (!canEditCustomer()) {
            return;
        }
        ZoneMappingRow selected = mappingGrid.asSingleSelect().getValue();
        if (selected == null || customerId == null) {
            return;
        }
        try {
            restClient.delete("/customers/" + customerId + "/service-zone-mappings/" + selected.zoneId() + "/" + selected.zipCodeId());
            loadZoneData();
        } catch (Exception e) {
            showError("PLZ-Zuordnung konnte nicht geloescht werden: " + e.getMessage());
        }
    }

    private void deleteSelectedRadius() {
        if (!canEditCustomer()) {
            return;
        }
        RadiusRow selected = radiusGrid.asSingleSelect().getValue();
        if (selected == null || customerId == null) {
            return;
        }
        try {
            restClient.delete("/customers/" + customerId + "/service-radii/" + selected.id());
            loadZoneData();
        } catch (Exception e) {
            showError("Radius konnte nicht geloescht werden: " + e.getMessage());
        }
    }

    private void refreshDeliveryReasonView() {
        DeliveryReasonListConfig selected = deliveryReasonListField.getValue();
        deliveryReasonGrid.setItems(selected != null ? selected.getItems() : List.of());
        deliveryReasonGrid.asSingleSelect().clear();
        deliveryReasonTextField.setValue(selected != null ? safe(selected.getText()) : "");
        saveDeliveryReasonButton.setEnabled(canEditCustomer() && selected != null);
        refreshDeliveryReasonActions();
    }

    private void refreshAcceptanceProtocolView() {
        AcceptanceProtocolConfig selected = acceptanceProtocolField.getValue();
        acceptanceQuestionGrid.setItems(selected != null ? selected.getQuestions() : List.of());
        acceptanceQuestionGrid.asSingleSelect().clear();
        acceptanceProtocolTextField.setValue(selected != null ? safe(selected.getText()) : "");
        saveAcceptanceProtocolButton.setEnabled(canEditCustomer() && selected != null);
        refreshAcceptanceQuestionActions();
    }

    private void openDeliveryReasonDialog(DeliveryReasonRow existing) {
        if (!canEditCustomer()) {
            showError("Keine Berechtigung zur Pflege von Kundenprotokollen.");
            return;
        }
        DeliveryReasonListConfig selected = deliveryReasonListField.getValue();
        if (selected == null) {
            showError("Bitte zuerst eine Ablieferliste waehlen.");
            return;
        }

        Dialog dialog = new Dialog();
        dialog.setHeaderTitle(existing == null ? "Abliefergrund anlegen" : "Abliefergrund bearbeiten");

        TextField codeField = new TextField("Code");
        codeField.setWidthFull();
        TextField reasonField = new TextField("Text");
        reasonField.setWidthFull();

        if (existing != null) {
            codeField.setValue(safe(existing.getCode()));
            reasonField.setValue(safe(existing.getReason()));
        }

        Button saveButton = new Button("Speichern", event -> {
            if (reasonField.getValue().isBlank()) {
                showError("Bitte einen Text fuer den Abliefergrund angeben.");
                return;
            }

            if (existing == null) {
                selected.getItems().add(new DeliveryReasonRow(null, codeField.getValue(), reasonField.getValue()));
            } else {
                existing.setCode(codeField.getValue());
                existing.setReason(reasonField.getValue());
            }
            resequenceDeliveryReasons(selected.getItems());
            refreshDeliveryReasonView();
            dialog.close();
        });
        saveButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);

        Button cancelButton = new Button("Abbrechen", event -> dialog.close());
        dialog.add(new VerticalLayout(codeField, reasonField, new HorizontalLayout(saveButton, cancelButton)));
        dialog.open();
    }

    private void deleteSelectedDeliveryReason() {
        if (!canEditCustomer()) {
            return;
        }
        DeliveryReasonListConfig selectedList = deliveryReasonListField.getValue();
        DeliveryReasonRow selectedRow = deliveryReasonGrid.asSingleSelect().getValue();
        if (selectedList == null || selectedRow == null) {
            return;
        }
        selectedList.getItems().remove(selectedRow);
        resequenceDeliveryReasons(selectedList.getItems());
        refreshDeliveryReasonView();
    }

    private void saveDeliveryReasonConfiguration() {
        if (!canEditCustomer()) {
            return;
        }
        if (customerId == null || deliveryReasonListField.getValue() == null) {
            return;
        }

        DeliveryReasonListConfig selected = deliveryReasonListField.getValue();
        selected.setText(deliveryReasonTextField.getValue());

        try {
            Map<String, Object> request = new HashMap<>();
            request.put("lists", List.of(toDeliveryReasonRequest(selected)));
            restClient.put("/customers/" + customerId + "/delivery-reasons", request);
            Notification.show("Abliefergruende gespeichert.", 3000, Notification.Position.MIDDLE)
                    .addThemeVariants(NotificationVariant.LUMO_SUCCESS);
            loadProtocolData(selected.getListNo(),
                    acceptanceProtocolField.getValue() != null ? acceptanceProtocolField.getValue().getServiceKey() : null);
        } catch (Exception e) {
            showError("Abliefergruende konnten nicht gespeichert werden: " + e.getMessage());
        }
    }

    private void openAcceptanceQuestionDialog(AcceptanceQuestionRow existing) {
        if (!canEditCustomer()) {
            showError("Keine Berechtigung zur Pflege von Kundenprotokollen.");
            return;
        }
        AcceptanceProtocolConfig selected = acceptanceProtocolField.getValue();
        if (selected == null) {
            showError("Bitte zuerst einen Service waehlen.");
            return;
        }

        Dialog dialog = new Dialog();
        dialog.setHeaderTitle(existing == null ? "Frage anlegen" : "Frage bearbeiten");

        TextField questionField = new TextField("Frage");
        questionField.setWidthFull();
        ComboBox<MailStateOption> mailStateField = new ComboBox<>("Mail-Status");
        mailStateField.setItems(MailStateOption.values());
        mailStateField.setItemLabelGenerator(MailStateOption::label);
        mailStateField.setWidthFull();

        if (existing != null) {
            questionField.setValue(safe(existing.getQuestion()));
            mailStateField.setValue(MailStateOption.fromValue(existing.getMailState()));
        } else {
            mailStateField.setValue(MailStateOption.NONE);
        }

        Button saveButton = new Button("Speichern", event -> {
            if (questionField.getValue().isBlank()) {
                showError("Bitte eine Frage eingeben.");
                return;
            }

            if (existing == null) {
                selected.getQuestions().add(new AcceptanceQuestionRow(null, questionField.getValue(),
                        mailStateField.getValue() != null ? mailStateField.getValue().value() : ""));
            } else {
                existing.setQuestion(questionField.getValue());
                existing.setMailState(mailStateField.getValue() != null ? mailStateField.getValue().value() : "");
            }
            resequenceAcceptanceQuestions(selected.getQuestions());
            refreshAcceptanceProtocolView();
            dialog.close();
        });
        saveButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);

        Button cancelButton = new Button("Abbrechen", event -> dialog.close());
        dialog.add(new VerticalLayout(questionField, mailStateField, new HorizontalLayout(saveButton, cancelButton)));
        dialog.open();
    }

    private void deleteSelectedAcceptanceQuestion() {
        if (!canEditCustomer()) {
            return;
        }
        AcceptanceProtocolConfig selectedProtocol = acceptanceProtocolField.getValue();
        AcceptanceQuestionRow selectedRow = acceptanceQuestionGrid.asSingleSelect().getValue();
        if (selectedProtocol == null || selectedRow == null) {
            return;
        }
        selectedProtocol.getQuestions().remove(selectedRow);
        resequenceAcceptanceQuestions(selectedProtocol.getQuestions());
        refreshAcceptanceProtocolView();
    }

    private void saveAcceptanceProtocolConfiguration() {
        if (!canEditCustomer()) {
            return;
        }
        if (customerId == null || acceptanceProtocolField.getValue() == null) {
            return;
        }

        AcceptanceProtocolConfig selected = acceptanceProtocolField.getValue();
        selected.setText(acceptanceProtocolTextField.getValue());

        try {
            Map<String, Object> request = new HashMap<>();
            request.put("protocols", List.of(toAcceptanceProtocolRequest(selected)));
            restClient.put("/customers/" + customerId + "/acceptance-protocols", request);
            Notification.show("Abnahmeprotokoll gespeichert.", 3000, Notification.Position.MIDDLE)
                    .addThemeVariants(NotificationVariant.LUMO_SUCCESS);
            loadProtocolData(
                    deliveryReasonListField.getValue() != null ? deliveryReasonListField.getValue().getListNo() : null,
                    selected.getServiceKey()
            );
        } catch (Exception e) {
            showError("Abnahmeprotokoll konnte nicht gespeichert werden: " + e.getMessage());
        }
    }

    private void refreshZoneActions() {
        boolean selected = zoneGrid.asSingleSelect().getValue() != null;
        boolean editable = canEditCustomer();
        addZoneButton.setEnabled(editable);
        editZoneButton.setEnabled(editable && selected);
        deleteZoneButton.setEnabled(editable && selected);
    }

    private void refreshMappingActions() {
        boolean selected = mappingGrid.asSingleSelect().getValue() != null;
        boolean editable = canEditCustomer();
        addMappingButton.setEnabled(editable);
        editMappingButton.setEnabled(editable && selected);
        deleteMappingButton.setEnabled(editable && selected);
    }

    private void refreshRadiusActions() {
        boolean selected = radiusGrid.asSingleSelect().getValue() != null;
        boolean editable = canEditCustomer();
        addRadiusButton.setEnabled(editable);
        editRadiusButton.setEnabled(editable && selected);
        deleteRadiusButton.setEnabled(editable && selected);
    }

    private void refreshDeliveryReasonActions() {
        boolean listSelected = deliveryReasonListField.getValue() != null;
        boolean rowSelected = deliveryReasonGrid.asSingleSelect().getValue() != null;
        boolean editable = canEditCustomer();
        addDeliveryReasonButton.setEnabled(editable && listSelected);
        editDeliveryReasonButton.setEnabled(editable && rowSelected);
        deleteDeliveryReasonButton.setEnabled(editable && rowSelected);
    }

    private void refreshAcceptanceQuestionActions() {
        boolean protocolSelected = acceptanceProtocolField.getValue() != null;
        boolean rowSelected = acceptanceQuestionGrid.asSingleSelect().getValue() != null;
        boolean editable = canEditCustomer();
        addAcceptanceQuestionButton.setEnabled(editable && protocolSelected);
        editAcceptanceQuestionButton.setEnabled(editable && rowSelected);
        deleteAcceptanceQuestionButton.setEnabled(editable && rowSelected);
    }

    private boolean canEditCustomer() {
        return sessionData.canManageCustomers();
    }

    private void loadGroupFilterOptions() {
        if (sessionData.getHeadquartersId() == null) {
            return;
        }
        try {
            groupOptions.clear();
            List<LinkedHashMap<String, Object>> groups = restClient.getList(
                    "/group-filter-admin/groups?hqId=" + sessionData.getHeadquartersId(),
                    new ParameterizedTypeReference<>() {}
            );
            for (Map<String, Object> row : groups) {
                Long id = toLong(row.get("id"));
                if (id != null) {
                    groupOptions.add(new GroupOption(id, str(row.get("name"))));
                }
            }
            groupField.setItems(groupOptions);

            filterOptions.clear();
            List<LinkedHashMap<String, Object>> filters = restClient.getList(
                    "/group-filter-admin/filters?hqId=" + sessionData.getHeadquartersId(),
                    new ParameterizedTypeReference<>() {}
            );
            for (Map<String, Object> row : filters) {
                String shortCode = str(row.get("shortCode"));
                if (!shortCode.isBlank()) {
                    filterOptions.add(new FilterOption(shortCode, str(row.get("text"))));
                }
            }
            filterField.setItems(filterOptions);
        } catch (Exception e) {
            showError("Gruppen- und Filterkatalog konnte nicht geladen werden: " + e.getMessage());
        }
    }

    private void resetState() {
        customerId = null;
        isNew = true;
        groupOptions.clear();
        filterOptions.clear();

        compField.clear();
        comp2Field.clear();
        comp3Field.clear();
        comp4Field.clear();
        hsnoField.clear();
        streetField.clear();
        zipcodeField.clear();
        cityField.clear();
        ilnField.clear();
        taxIdField.clear();
        staxIdField.clear();
        eidField.clear();
        discountField.clear();
        groupField.clear();
        groupField.setItems(groupOptions);
        filterField.clear();
        filterField.setItems(filterOptions);
        commissionField.clear();
        trackingField.clear();
        accountField.clear();
        nameField.clear();
        firstnameField.clear();
        emailField.clear();
        phoneField.clear();
        phone2Field.clear();
        faxField.clear();
        passwordField.clear();

        clearServiceConfiguration();

        zones.clear();
        mappings.clear();
        radii.clear();
        zoneGrid.setItems(zones);
        mappingGrid.setItems(mappings);
        radiusGrid.setItems(radii);

        deliveryReasonLists.clear();
        deliveryReasonListField.clear();
        deliveryReasonListField.setItems(deliveryReasonLists);
        deliveryReasonGrid.setItems(List.of());
        deliveryReasonTextField.clear();

        acceptanceProtocols.clear();
        acceptanceProtocolField.clear();
        acceptanceProtocolField.setItems(acceptanceProtocols);
        acceptanceQuestionGrid.setItems(List.of());
        acceptanceProtocolTextField.clear();
    }

    private void clearServiceConfiguration() {
        serviceOptions.clear();
        serviceTypeOptions.clear();
        servicesField.setItems(serviceOptions);
        serviceTypesField.setItems(serviceTypeOptions);
        servicesField.clear();
        serviceTypesField.clear();
    }

    private ZoneRow findZone(Long zoneId) {
        for (ZoneRow zone : zones) {
            if (Objects.equals(zone.id(), zoneId)) {
                return zone;
            }
        }
        return null;
    }

    private DeliveryReasonListConfig findDeliveryReasonList(Integer listNo) {
        if (listNo == null) {
            return null;
        }
        for (DeliveryReasonListConfig config : deliveryReasonLists) {
            if (Objects.equals(config.getListNo(), listNo)) {
                return config;
            }
        }
        return null;
    }

    private AcceptanceProtocolConfig findAcceptanceProtocol(Integer serviceKey) {
        if (serviceKey == null) {
            return null;
        }
        for (AcceptanceProtocolConfig config : acceptanceProtocols) {
            if (Objects.equals(config.getServiceKey(), serviceKey)) {
                return config;
            }
        }
        return null;
    }

    private LinkedHashSet<GroupOption> selectGroups(String value) {
        Set<Long> selectedIds = parseLongTokens(value);
        LinkedHashSet<GroupOption> selected = new LinkedHashSet<>();
        for (GroupOption option : groupOptions) {
            if (selectedIds.contains(option.id())) {
                selected.add(option);
            }
        }
        return selected;
    }

    private LinkedHashSet<FilterOption> selectFilters(String value) {
        Set<String> selectedCodes = parseStringTokens(value);
        LinkedHashSet<FilterOption> selected = new LinkedHashSet<>();
        for (FilterOption option : filterOptions) {
            if (selectedCodes.contains(option.shortCode())) {
                selected.add(option);
            }
        }
        return selected;
    }

    private String toGroupString(Set<GroupOption> selected) {
        if (selected == null || selected.isEmpty()) {
            return "";
        }
        return "," + selected.stream()
                .map(GroupOption::id)
                .sorted()
                .map(String::valueOf)
                .reduce((left, right) -> left + "," + right)
                .orElse("") + ",";
    }

    private String toFilterString(Set<FilterOption> selected) {
        if (selected == null || selected.isEmpty()) {
            return "";
        }
        return "," + selected.stream()
                .map(FilterOption::shortCode)
                .sorted(String::compareToIgnoreCase)
                .reduce((left, right) -> left + "," + right)
                .orElse("") + ",";
    }

    private Set<Long> parseLongTokens(String value) {
        Set<Long> parsed = new LinkedHashSet<>();
        if (value == null || value.isBlank()) {
            return parsed;
        }
        for (String token : value.split(",")) {
            if (token == null || token.isBlank()) {
                continue;
            }
            Long parsedId = toLong(token.trim());
            if (parsedId != null) {
                parsed.add(parsedId);
            }
        }
        return parsed;
    }

    private Set<String> parseStringTokens(String value) {
        Set<String> parsed = new LinkedHashSet<>();
        if (value == null || value.isBlank()) {
            return parsed;
        }
        for (String token : value.split(",")) {
            if (token != null && !token.isBlank()) {
                parsed.add(token.trim());
            }
        }
        return parsed;
    }

    private List<DeliveryReasonListConfig> readDeliveryReasonLists(Object value) {
        List<DeliveryReasonListConfig> items = new ArrayList<>();
        if (value instanceof List<?> rows) {
            for (Object row : rows) {
                if (row instanceof Map<?, ?> map) {
                    DeliveryReasonListConfig config = new DeliveryReasonListConfig(
                            toInteger(map.get("listNo")),
                            str(map.get("label")),
                            str(map.get("text"))
                    );
                    config.getItems().addAll(readDeliveryReasonItems(map.get("items")));
                    resequenceDeliveryReasons(config.getItems());
                    items.add(config);
                }
            }
        }
        items.sort(Comparator.comparing(DeliveryReasonListConfig::getListNo, Comparator.nullsLast(Integer::compareTo)));
        return items;
    }

    private List<DeliveryReasonRow> readDeliveryReasonItems(Object value) {
        List<DeliveryReasonRow> items = new ArrayList<>();
        if (value instanceof List<?> rows) {
            for (Object row : rows) {
                if (row instanceof Map<?, ?> map) {
                    items.add(new DeliveryReasonRow(
                            toInteger(map.get("sort")),
                            str(map.get("code")),
                            str(map.get("reason"))
                    ));
                }
            }
        }
        return items;
    }

    private List<AcceptanceProtocolConfig> readAcceptanceProtocols(Object value) {
        List<AcceptanceProtocolConfig> items = new ArrayList<>();
        if (value instanceof List<?> rows) {
            for (Object row : rows) {
                if (row instanceof Map<?, ?> map) {
                    AcceptanceProtocolConfig config = new AcceptanceProtocolConfig(
                            toInteger(map.get("serviceKey")),
                            str(map.get("serviceLabel")),
                            str(map.get("text"))
                    );
                    config.getQuestions().addAll(readAcceptanceQuestions(map.get("questions")));
                    resequenceAcceptanceQuestions(config.getQuestions());
                    items.add(config);
                }
            }
        }
        items.sort(Comparator.comparing(AcceptanceProtocolConfig::getServiceKey, Comparator.nullsLast(Integer::compareTo)));
        return items;
    }

    private List<AcceptanceQuestionRow> readAcceptanceQuestions(Object value) {
        List<AcceptanceQuestionRow> items = new ArrayList<>();
        if (value instanceof List<?> rows) {
            for (Object row : rows) {
                if (row instanceof Map<?, ?> map) {
                    items.add(new AcceptanceQuestionRow(
                            toInteger(map.get("sort")),
                            str(map.get("question")),
                            str(map.get("mailState"))
                    ));
                }
            }
        }
        return items;
    }

    private Map<String, Object> toDeliveryReasonRequest(DeliveryReasonListConfig config) {
        Map<String, Object> row = new LinkedHashMap<>();
        row.put("listNo", config.getListNo());
        row.put("text", config.getText());

        List<Map<String, Object>> items = new ArrayList<>();
        for (DeliveryReasonRow item : config.getItems()) {
            Map<String, Object> value = new LinkedHashMap<>();
            value.put("sort", item.getSort());
            value.put("code", item.getCode());
            value.put("reason", item.getReason());
            items.add(value);
        }
        row.put("items", items);
        return row;
    }

    private Map<String, Object> toAcceptanceProtocolRequest(AcceptanceProtocolConfig config) {
        Map<String, Object> row = new LinkedHashMap<>();
        row.put("serviceKey", config.getServiceKey());
        row.put("text", config.getText());

        List<Map<String, Object>> items = new ArrayList<>();
        for (AcceptanceQuestionRow question : config.getQuestions()) {
            Map<String, Object> value = new LinkedHashMap<>();
            value.put("sort", question.getSort());
            value.put("question", question.getQuestion());
            value.put("mailState", question.getMailState());
            items.add(value);
        }
        row.put("questions", items);
        return row;
    }

    private void resequenceDeliveryReasons(List<DeliveryReasonRow> items) {
        for (int i = 0; i < items.size(); i++) {
            items.get(i).setSort(i + 1);
        }
    }

    private void resequenceAcceptanceQuestions(List<AcceptanceQuestionRow> items) {
        for (int i = 0; i < items.size(); i++) {
            items.get(i).setSort(i + 1);
        }
    }

    private List<ServiceOption> readOptions(Object value) {
        List<ServiceOption> options = new ArrayList<>();
        if (value instanceof List<?> rows) {
            for (Object row : rows) {
                if (row instanceof Map<?, ?> map) {
                    options.add(new ServiceOption(
                            toLong(map.get("id")),
                            str(map.get("name")),
                            toInteger(map.get("mode"))
                    ));
                }
            }
        }
        return options;
    }

    private Set<Long> toLongSet(Object value) {
        Set<Long> ids = new LinkedHashSet<>();
        if (value instanceof List<?> rows) {
            for (Object row : rows) {
                Long id = toLong(row);
                if (id != null) {
                    ids.add(id);
                }
            }
        }
        return ids;
    }

    private Set<ServiceOption> selectOptions(List<ServiceOption> options, Set<Long> selectedIds) {
        Set<ServiceOption> selected = new LinkedHashSet<>();
        for (ServiceOption option : options) {
            if (selectedIds.contains(option.id())) {
                selected.add(option);
            }
        }
        return selected;
    }

    private List<Long> orderedSelection(List<ServiceOption> options, Set<ServiceOption> selectedValues) {
        List<Long> ids = new ArrayList<>();
        for (ServiceOption option : options) {
            if (selectedValues.contains(option)) {
                ids.add(option.id());
            }
        }
        return ids;
    }

    private List<Map<String, Object>> toMapList(Object[] rows) {
        List<Map<String, Object>> items = new ArrayList<>();
        if (rows == null) {
            return items;
        }
        for (Object row : rows) {
            if (row instanceof Map<?, ?> rawMap) {
                Map<String, Object> casted = new LinkedHashMap<>();
                for (Map.Entry<?, ?> entry : rawMap.entrySet()) {
                    casted.put(String.valueOf(entry.getKey()), entry.getValue());
                }
                items.add(casted);
            }
        }
        return items;
    }

    private static String serviceOrderLabel(ServiceOption option) {
        return option.name() + "|" + (option.mode() != null ? option.mode() : "");
    }

    private String serviceLabel(ServiceOption option) {
        if (option.mode() == null) {
            return option.name();
        }
        return option.name() + " (Modus " + option.mode() + ")";
    }

    private String mailStateLabel(String value) {
        return switch (safe(value)) {
            case "1" -> "Mail bei JA";
            case "2" -> "Mail bei NEIN";
            default -> "Keine Mail";
        };
    }

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

    private String str(Object obj) {
        return obj != null ? obj.toString() : "";
    }

    private Long toLong(Object value) {
        if (value == null) {
            return null;
        }
        if (value instanceof Number number) {
            return number.longValue();
        }
        String raw = value.toString().trim();
        return raw.isBlank() ? null : Long.parseLong(raw);
    }

    private Integer toInteger(Object value) {
        if (value == null) {
            return null;
        }
        if (value instanceof Number number) {
            return number.intValue();
        }
        String raw = value.toString().trim();
        return raw.isBlank() ? null : Integer.parseInt(raw);
    }

    private BigDecimal toBigDecimal(Object value) {
        if (value == null) {
            return null;
        }
        if (value instanceof BigDecimal bigDecimal) {
            return bigDecimal;
        }
        String raw = value.toString().trim();
        return raw.isBlank() ? null : new BigDecimal(raw.replace(',', '.'));
    }

    private Double asDouble(BigDecimal value) {
        return value != null ? value.doubleValue() : null;
    }

    private BigDecimal decimal(Double value) {
        return value != null ? BigDecimal.valueOf(value) : null;
    }

    private String decimalLabel(BigDecimal value) {
        return value != null ? value.stripTrailingZeros().toPlainString() : "";
    }

    private void showError(String message) {
        Notification notification = Notification.show(message, 5000, Notification.Position.MIDDLE);
        notification.addThemeVariants(NotificationVariant.LUMO_ERROR);
    }

    private record ServiceOption(Long id, String name, Integer mode) {
    }

    private record ZoneRow(Long id,
                           Integer zoneNo,
                           String name,
                           BigDecimal price,
                           BigDecimal price1,
                           BigDecimal price2,
                           BigDecimal price3) {
    }

    private record ZoneMappingRow(Long zoneId,
                                  Integer zoneNo,
                                  String zoneName,
                                  Long zipCodeId,
                                  String zipcode,
                                  BigDecimal mappedValue1,
                                  BigDecimal mappedValue2,
                                  BigDecimal mappedValue3) {
    }

    private record RadiusRow(Long id, String zipcode, String cityDistrict, Integer radiusAreaNo) {
    }

    private static class DeliveryReasonListConfig {
        private final Integer listNo;
        private final String label;
        private final List<DeliveryReasonRow> items = new ArrayList<>();
        private String text;

        private DeliveryReasonListConfig(Integer listNo, String label, String text) {
            this.listNo = listNo;
            this.label = label;
            this.text = text;
        }

        public Integer getListNo() {
            return listNo;
        }

        public String getLabel() {
            return label;
        }

        public List<DeliveryReasonRow> getItems() {
            return items;
        }

        public String getText() {
            return text;
        }

        public void setText(String text) {
            this.text = text;
        }
    }

    private static class DeliveryReasonRow {
        private Integer sort;
        private String code;
        private String reason;

        private DeliveryReasonRow(Integer sort, String code, String reason) {
            this.sort = sort;
            this.code = code;
            this.reason = reason;
        }

        public Integer getSort() {
            return sort;
        }

        public void setSort(Integer sort) {
            this.sort = sort;
        }

        public String getCode() {
            return code;
        }

        public void setCode(String code) {
            this.code = code;
        }

        public String getReason() {
            return reason;
        }

        public void setReason(String reason) {
            this.reason = reason;
        }
    }

    private static class AcceptanceProtocolConfig {
        private final Integer serviceKey;
        private final String label;
        private final List<AcceptanceQuestionRow> questions = new ArrayList<>();
        private String text;

        private AcceptanceProtocolConfig(Integer serviceKey, String label, String text) {
            this.serviceKey = serviceKey;
            this.label = label;
            this.text = text;
        }

        public Integer getServiceKey() {
            return serviceKey;
        }

        public String getLabel() {
            return label;
        }

        public List<AcceptanceQuestionRow> getQuestions() {
            return questions;
        }

        public String getText() {
            return text;
        }

        public void setText(String text) {
            this.text = text;
        }
    }

    private static class AcceptanceQuestionRow {
        private Integer sort;
        private String question;
        private String mailState;

        private AcceptanceQuestionRow(Integer sort, String question, String mailState) {
            this.sort = sort;
            this.question = question;
            this.mailState = mailState;
        }

        public Integer getSort() {
            return sort;
        }

        public void setSort(Integer sort) {
            this.sort = sort;
        }

        public String getQuestion() {
            return question;
        }

        public void setQuestion(String question) {
            this.question = question;
        }

        public String getMailState() {
            return mailState;
        }

        public void setMailState(String mailState) {
            this.mailState = mailState;
        }
    }

    private enum MailStateOption {
        NONE("", "Keine Mail"),
        YES("1", "Mail bei JA"),
        NO("2", "Mail bei NEIN");

        private final String value;
        private final String label;

        MailStateOption(String value, String label) {
            this.value = value;
            this.label = label;
        }

        public String value() {
            return value;
        }

        public String label() {
            return label;
        }

        private static MailStateOption fromValue(String value) {
            for (MailStateOption option : values()) {
                if (Objects.equals(option.value, value)) {
                    return option;
                }
            }
            return NONE;
        }
    }

    private record GroupOption(Long id, String label) {
    }

    private record FilterOption(String shortCode, String label) {
    }
}
