package de.votian.web.view;

import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.checkbox.Checkbox;
import com.vaadin.flow.component.checkbox.CheckboxGroup;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.datepicker.DatePicker;
import com.vaadin.flow.component.formlayout.FormLayout;
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.PasswordField;
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.ParameterProvider;
import de.votian.web.service.RestClientService;
import org.springframework.core.ParameterizedTypeReference;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

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

    private final RestClientService restClient;
    private final SessionData sessionData;
    private final ParameterProvider parameterProvider;

    private Long employeeId;
    private boolean isNew = true;

    private final TextField accountField = new TextField("Benutzerkonto");
    private final ComboBox<UserTypeOption> userTypeField = new ComboBox<>("Benutzerart");
    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 DatePicker birthdateField = new DatePicker("Geburtsdatum");
    private final PasswordField passwordField = new PasswordField("Passwort");
    private final TextField headquartersField = new TextField("Niederlassungen");
    private final ComboBox<CustomerOption> customerField = new ComboBox<>("Kunde");
    private final ComboBox<CostCenterOption> primaryCostCenterField = new ComboBox<>("Haupt-Kostenstelle");
    private final CheckboxGroup<GroupOption> groupField = new CheckboxGroup<>();

    private final VerticalLayout globalRightsLayout = new VerticalLayout();
    private final VerticalLayout rightsLayout = new VerticalLayout();
    private final VerticalLayout costCenterAccessLayout = new VerticalLayout();
    private final VerticalLayout warehouseConfigLayout = new VerticalLayout();
    private final Button syncHeadquartersButton = new Button("Niederlassungen aus Rechten uebernehmen",
            e -> headquartersField.setValue(buildHeadquartersValueFromRights()));
    private final Button saveButton = new Button("Speichern", VaadinIcon.CHECK.create(), e -> save());
    private final CheckboxGroup<GlobalRightOption> globalRightsGroup = new CheckboxGroup<>();
    private final CheckboxGroup<CostCenterOption> costCenterAccessGroup = new CheckboxGroup<>();
    private final CheckboxGroup<WarehouseRootOption> warehouseRootAccessGroup = new CheckboxGroup<>();
    private final ComboBox<WarehouseRootOption> warehouseDefaultRootField = new ComboBox<>("Standard-Hauptlager");
    private final Checkbox warehouseReadOnlyField = new Checkbox("Nur Leseberechtigung im Lager");
    private final Checkbox warehouseArticleAccessField = new Checkbox("Berechtigung zur Artikelverwaltung");
    private final Checkbox warehouseRestrictVisibilityField = new Checkbox("Ansicht auf definierte Unterlager einschraenken");

    private final Map<Long, CheckboxGroup<RightOption>> rightsGroups = new LinkedHashMap<>();
    private final Map<Long, CheckboxGroup<WarehouseSubstockOption>> warehouseSubstockGroups = new LinkedHashMap<>();
    private final List<RightOption> availableRights = new ArrayList<>();
    private final List<HeadquartersOption> headquartersOptions = new ArrayList<>();
    private final Map<Long, Set<Long>> assignedRightIdsByHeadquarters = new LinkedHashMap<>();
    private final List<CustomerOption> customerOptions = new ArrayList<>();
    private final List<CostCenterOption> availableCostCenters = new ArrayList<>();
    private final List<GroupOption> groupOptions = new ArrayList<>();
    private final List<WarehouseRootOption> warehouseRootOptions = new ArrayList<>();
    private final Set<Long> assignedCostCenterIds = new LinkedHashSet<>();
    private final Map<Long, Set<Long>> assignedWarehouseSubstockIds = new LinkedHashMap<>();
    private String legacyRightsValue = defaultRightsFor(UserTypeOption.HEADQUARTERS);
    private boolean warehouseConfigLoading;

    public EmployeeDetailView(RestClientService restClient, SessionData sessionData, ParameterProvider parameterProvider) {
        this.restClient = restClient;
        this.sessionData = sessionData;
        this.parameterProvider = parameterProvider;

        setPadding(true);
        setSpacing(true);
        setWidthFull();

        globalRightsLayout.setPadding(false);
        globalRightsLayout.setSpacing(true);
        rightsLayout.setPadding(false);
        rightsLayout.setSpacing(true);
        costCenterAccessLayout.setPadding(false);
        costCenterAccessLayout.setSpacing(true);
        warehouseConfigLayout.setPadding(false);
        warehouseConfigLayout.setSpacing(true);

        userTypeField.setItems(sessionData.isCustomerUser()
                ? List.of(UserTypeOption.CUSTOMER)
                : java.util.Arrays.asList(UserTypeOption.values()));
        userTypeField.setItemLabelGenerator(UserTypeOption::label);
        userTypeField.setValue(sessionData.isCustomerUser() ? UserTypeOption.CUSTOMER : UserTypeOption.HEADQUARTERS);
        userTypeField.setReadOnly(sessionData.isCustomerUser());
        userTypeField.addValueChangeListener(event -> updateUserContext());

        customerField.setItemLabelGenerator(CustomerOption::label);
        customerField.addValueChangeListener(event -> {
            if (event.getValue() == null) {
                availableCostCenters.clear();
                primaryCostCenterField.clear();
                assignedCostCenterIds.clear();
                refreshCostCenterOptions();
                buildCostCenterAccessEditor();
                return;
            }
            loadCostCenters(event.getValue().id());
            assignedCostCenterIds.clear();
            buildCostCenterAccessEditor();
        });

        primaryCostCenterField.setItemLabelGenerator(CostCenterOption::label);
        primaryCostCenterField.addValueChangeListener(event -> buildCostCenterAccessEditor());
        groupField.setLabel("Gruppen");
        groupField.setWidthFull();
        groupField.setItemLabelGenerator(GroupOption::label);
        groupField.setVisible(sessionData.isHeadquartersUser());

        headquartersField.setHelperText("Kommagetrennte HQ-IDs. Wird bei leerem Feld aus den Rechtezuordnungen befuellt.");
        globalRightsGroup.setLabel("Globale Rechte");
        globalRightsGroup.setWidthFull();
        costCenterAccessGroup.setLabel("Zusaetzlich freigegebene Kostenstellen");
        costCenterAccessGroup.setWidthFull();

        warehouseRootAccessGroup.setLabel("Zugang Hauptlaeger");
        warehouseRootAccessGroup.setWidthFull();
        warehouseRootAccessGroup.setItemLabelGenerator(WarehouseRootOption::label);
        warehouseRootAccessGroup.addValueChangeListener(event -> {
            if (warehouseConfigLoading) {
                return;
            }
            syncWarehouseDefaultRootOptions();
            buildWarehouseConfigEditor();
        });

        warehouseDefaultRootField.setItemLabelGenerator(WarehouseRootOption::label);
        warehouseDefaultRootField.setWidth("360px");
        warehouseDefaultRootField.setClearButtonVisible(true);
    }

    @Override
    public void setParameter(BeforeEvent event, @OptionalParameter String parameter) {
        removeAll();
        resetState();
        if ("new".equals(parameter) && !sessionData.canManageEmployees()) {
            Notification.show("Keine Berechtigung zum Anlegen von Mitarbeitern.", 5000, Notification.Position.MIDDLE)
                    .addThemeVariants(NotificationVariant.LUMO_ERROR);
            getUI().ifPresent(ui -> ui.navigate(EmployeeListView.class));
            return;
        }
        loadReferenceData();
        if (parameter != null && !parameter.isEmpty() && !"new".equals(parameter)) {
            try {
                employeeId = Long.parseLong(parameter);
                isNew = false;
                loadEmployee();
                if (sessionData.canManageEmployeeMatrixRights()) {
                    loadRights();
                }
                if (sessionData.canManageEmployeeCostCenterMatrix()) {
                    loadCostCenterAccess();
                }
            } catch (NumberFormatException e) {
                isNew = true;
            }
        }
        loadWarehouseConfigState();
        buildForm();
    }

    private void buildForm() {
        H3 title = new H3(isNew ? "Neuer Mitarbeiter" : "Mitarbeiter bearbeiten");

        Tab masterDataTab = new Tab("Stammdaten");
        Tab globalRightsTab = new Tab("Globale Rechte");
        Tab rightsTab = new Tab("HQ-Rechte");
        Tab costCentersTab = new Tab("Kostenstellenrechte");
        Tab warehouseTab = new Tab("Lager");

        List<Tab> tabsToShow = new ArrayList<>();
        tabsToShow.add(masterDataTab);
        if (sessionData.canManageEmployees()) {
            tabsToShow.add(globalRightsTab);
        }
        if (sessionData.canManageEmployeeMatrixRights()) {
            tabsToShow.add(rightsTab);
        }
        if (sessionData.canManageEmployeeCostCenterMatrix()) {
            tabsToShow.add(costCentersTab);
        }
        if (sessionData.canManageEmployeeWarehouseConfig()) {
            tabsToShow.add(warehouseTab);
        }
        Tabs tabs = new Tabs(tabsToShow.toArray(Tab[]::new));

        FormLayout form = new FormLayout();
        form.setResponsiveSteps(
                new FormLayout.ResponsiveStep("0", 1),
                new FormLayout.ResponsiveStep("700px", 2)
        );
        form.add(userTypeField, accountField, firstnameField, nameField, emailField, phoneField, phone2Field,
                birthdateField, passwordField, headquartersField, customerField, primaryCostCenterField, groupField);
        form.setColspan(groupField, 2);

        VerticalLayout masterDataContent = new VerticalLayout(form, syncHeadquartersButton);
        masterDataContent.setPadding(false);
        masterDataContent.setSpacing(true);
        masterDataContent.setWidthFull();

        buildGlobalRightsEditor();
        buildRightsEditor();
        buildCostCenterAccessEditor();
        buildWarehouseConfigEditor();
        globalRightsLayout.setVisible(false);
        rightsLayout.setVisible(false);
        costCenterAccessLayout.setVisible(false);
        warehouseConfigLayout.setVisible(false);
        updateUserContext();

        tabs.addSelectedChangeListener(event -> {
            boolean showMasterData = event.getSelectedTab() == masterDataTab;
            masterDataContent.setVisible(showMasterData);
            globalRightsLayout.setVisible(event.getSelectedTab() == globalRightsTab);
            rightsLayout.setVisible(event.getSelectedTab() == rightsTab);
            costCenterAccessLayout.setVisible(event.getSelectedTab() == costCentersTab);
            warehouseConfigLayout.setVisible(event.getSelectedTab() == warehouseTab);
        });

        saveButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        Button cancelButton = new Button("Abbrechen", e ->
                getUI().ifPresent(ui -> ui.navigate(EmployeeListView.class)));

        applyFormPermissions();
        HorizontalLayout buttons = new HorizontalLayout(saveButton, cancelButton);
        add(title, tabs, masterDataContent, globalRightsLayout, rightsLayout, costCenterAccessLayout, warehouseConfigLayout, buttons);
    }

    private void loadEmployee() {
        try {
            @SuppressWarnings("unchecked")
            Map<String, Object> emp = restClient.get("/employees/" + employeeId, LinkedHashMap.class);
            if (emp == null) {
                return;
            }

            headquartersField.setValue(str(emp.get("headquarters")));
            accountField.setValue(str(emp.get("account")));
            nameField.setValue(str(emp.get("name")));
            firstnameField.setValue(str(emp.get("firstname")));
            emailField.setValue(str(emp.get("email")));
            phoneField.setValue(str(emp.get("phone")));
            phone2Field.setValue(str(emp.get("phone2")));
            if (emp.get("birthdate") != null) {
                birthdateField.setValue(LocalDate.parse(emp.get("birthdate").toString()));
            }

            UserTypeOption userType = UserTypeOption.fromCode(toInteger(emp.get("userType"), 1));
            userTypeField.setValue(userType);
            legacyRightsValue = normalizeRights(str(emp.get("rights")), userType);
            buildGlobalRightsEditor();

            Long customerId = toLong(emp.get("customerId"));
            if (customerId != null) {
                selectCustomer(customerId);
            }

            Long primaryCostCenterId = toLong(emp.get("costCenterId"));
            if (customerId != null) {
                loadCostCenters(customerId);
            }
            if (primaryCostCenterId != null) {
                selectPrimaryCostCenter(primaryCostCenterId);
            }
            if (sessionData.isHeadquartersUser()) {
                groupField.setValue(selectGroups(str(emp.get("group"))));
            }

            updateUserContext();
        } catch (Exception e) {
            Notification.show("Fehler: " + e.getMessage(), 5000, Notification.Position.MIDDLE)
                    .addThemeVariants(NotificationVariant.LUMO_ERROR);
        }
    }

    private void loadReferenceData() {
        loadCustomers();
        loadGroupOptions();
        if (sessionData.canManageEmployeeMatrixRights()) {
            loadHeadquarters();
            loadAvailableRights();
        }
    }

    private void loadGroupOptions() {
        if (!sessionData.isHeadquartersUser() || sessionData.getHeadquartersId() == null) {
            return;
        }
        try {
            groupOptions.clear();
            List<LinkedHashMap<String, Object>> result = restClient.getList(
                    "/group-filter-admin/groups?hqId=" + sessionData.getHeadquartersId(),
                    new ParameterizedTypeReference<>() {}
            );
            for (Map<String, Object> row : result) {
                Long id = toLong(row.get("id"));
                if (id != null) {
                    groupOptions.add(new GroupOption(id, str(row.get("name"))));
                }
            }
            groupField.setItems(groupOptions);
        } catch (Exception e) {
            Notification.show("Gruppenkatalog konnte nicht geladen werden: " + e.getMessage(),
                    5000, Notification.Position.MIDDLE).addThemeVariants(NotificationVariant.LUMO_ERROR);
        }
    }

    private void loadWarehouseConfigState() {
        if (!sessionData.canManageEmployeeWarehouseConfig()) {
            return;
        }
        try {
            @SuppressWarnings("unchecked")
            Map<String, Object> data = isNew
                    ? restClient.get("/employees/warehouse-config-template", LinkedHashMap.class)
                    : restClient.get("/employees/" + employeeId + "/warehouse-config", LinkedHashMap.class);
            applyWarehouseConfig(data);
        } catch (Exception e) {
            Notification.show("Lagerkonfiguration konnte nicht geladen werden: " + e.getMessage(),
                    5000, Notification.Position.MIDDLE).addThemeVariants(NotificationVariant.LUMO_ERROR);
        }
    }

    private void loadHeadquarters() {
        try {
            List<LinkedHashMap<String, Object>> result = restClient.getList(
                    "/headquarters",
                    new ParameterizedTypeReference<>() {}
            );
            for (Map<String, Object> row : result) {
                Long id = toLong(row.get("id"));
                if (id != null) {
                    headquartersOptions.add(new HeadquartersOption(id, str(row.get("name")), str(row.get("mnemonic"))));
                }
            }
        } catch (Exception e) {
            Notification.show("Niederlassungen konnten nicht geladen werden: " + e.getMessage(),
                    5000, Notification.Position.MIDDLE).addThemeVariants(NotificationVariant.LUMO_ERROR);
        }
    }

    private void loadAvailableRights() {
        try {
            List<LinkedHashMap<String, Object>> result = restClient.getList(
                    "/rights",
                    new ParameterizedTypeReference<>() {}
            );
            for (Map<String, Object> row : result) {
                Long id = toLong(row.get("id"));
                if (id != null) {
                    availableRights.add(new RightOption(id, str(row.get("name"))));
                }
            }
        } catch (Exception e) {
            Notification.show("Rechtekatalog konnte nicht geladen werden: " + e.getMessage(),
                    5000, Notification.Position.MIDDLE).addThemeVariants(NotificationVariant.LUMO_ERROR);
        }
    }

    private void loadCustomers() {
        if (sessionData.isCustomerUser()) {
            if (sessionData.getCustomerId() == null) {
                return;
            }
            try {
                @SuppressWarnings("unchecked")
                Map<String, Object> row = restClient.get("/customers/" + sessionData.getCustomerId(), LinkedHashMap.class);
                Long id = toLong(row.get("id"));
                if (id != null) {
                    CustomerOption option = new CustomerOption(id, str(row.get("companyName")), str(row.get("eid")));
                    customerOptions.add(option);
                    customerField.setItems(customerOptions);
                    customerField.setValue(option);
                    customerField.setReadOnly(true);
                }
            } catch (Exception e) {
                Notification.show("Kunde konnte nicht geladen werden: " + e.getMessage(),
                        5000, Notification.Position.MIDDLE).addThemeVariants(NotificationVariant.LUMO_ERROR);
            }
            return;
        }
        if (sessionData.getHeadquartersId() == null) {
            return;
        }
        try {
            List<LinkedHashMap<String, Object>> result = restClient.getList(
                    "/customers/hq/" + sessionData.getHeadquartersId(),
                    new ParameterizedTypeReference<>() {}
            );
            for (Map<String, Object> row : result) {
                Long id = toLong(row.get("id"));
                if (id != null) {
                    customerOptions.add(new CustomerOption(id, str(row.get("companyName")), str(row.get("eid"))));
                }
            }
            customerField.setItems(customerOptions);
        } catch (Exception e) {
            Notification.show("Kunden konnten nicht geladen werden: " + e.getMessage(),
                    5000, Notification.Position.MIDDLE).addThemeVariants(NotificationVariant.LUMO_ERROR);
        }
    }

    private void loadCostCenters(Long customerId) {
        availableCostCenters.clear();
        primaryCostCenterField.clear();
        try {
            List<LinkedHashMap<String, Object>> result = restClient.getList(
                    "/costcenters/customer/" + customerId,
                    new ParameterizedTypeReference<>() {}
            );
            for (Map<String, Object> row : result) {
                Long id = toLong(row.get("id"));
                if (id != null) {
                    availableCostCenters.add(new CostCenterOption(id, str(row.get("name")), str(row.get("path"))));
                }
            }
            refreshCostCenterOptions();
        } catch (Exception e) {
            Notification.show("Kostenstellen konnten nicht geladen werden: " + e.getMessage(),
                    5000, Notification.Position.MIDDLE).addThemeVariants(NotificationVariant.LUMO_ERROR);
        }
    }

    private void refreshCostCenterOptions() {
        primaryCostCenterField.setItems(availableCostCenters);
    }

    private void selectCustomer(Long customerId) {
        customerOptions.stream()
                .filter(option -> option.id().equals(customerId))
                .findFirst()
                .ifPresent(customerField::setValue);
    }

    private void selectPrimaryCostCenter(Long costCenterId) {
        availableCostCenters.stream()
                .filter(option -> option.id().equals(costCenterId))
                .findFirst()
                .ifPresent(primaryCostCenterField::setValue);
    }

    private void loadRights() {
        try {
            List<LinkedHashMap<String, Object>> result = restClient.getList(
                    "/employees/" + employeeId + "/rights",
                    new ParameterizedTypeReference<>() {}
            );
            for (Map<String, Object> row : result) {
                Long headquartersId = toLong(row.get("headquartersId"));
                Long rightId = toLong(row.get("rightId"));
                if (headquartersId != null && rightId != null) {
                    assignedRightIdsByHeadquarters
                            .computeIfAbsent(headquartersId, ignored -> new LinkedHashSet<>())
                            .add(rightId);
                }
            }
        } catch (Exception e) {
            Notification.show("Mitarbeiterrechte konnten nicht geladen werden: " + e.getMessage(),
                    5000, Notification.Position.MIDDLE).addThemeVariants(NotificationVariant.LUMO_ERROR);
        }
    }

    private void loadCostCenterAccess() {
        if (employeeId == null) {
            return;
        }
        try {
            List<LinkedHashMap<String, Object>> result = restClient.getList(
                    "/employees/" + employeeId + "/costcenters",
                    new ParameterizedTypeReference<>() {}
            );
            assignedCostCenterIds.clear();
            for (Map<String, Object> row : result) {
                Long costCenterId = toLong(row.get("costCenterId"));
                if (costCenterId != null) {
                    assignedCostCenterIds.add(costCenterId);
                }
            }
            buildCostCenterAccessEditor();
        } catch (Exception e) {
            Notification.show("Kostenstellenrechte konnten nicht geladen werden: " + e.getMessage(),
                    5000, Notification.Position.MIDDLE).addThemeVariants(NotificationVariant.LUMO_ERROR);
        }
    }

    private void buildGlobalRightsEditor() {
        globalRightsLayout.removeAll();
        List<GlobalRightOption> options = globalRightOptions(userTypeField.getValue());
        if (options.isEmpty()) {
            globalRightsLayout.add(new Paragraph("Fuer diesen Benutzertyp sind aktuell keine globalen Rechte hinterlegt."));
            return;
        }

        globalRightsGroup.setItems(options);
        globalRightsGroup.setItemLabelGenerator(GlobalRightOption::label);
        globalRightsGroup.setValue(options.stream()
                .filter(option -> legacyRightsValue.length() > option.index() && legacyRightsValue.charAt(option.index()) == '1')
                .collect(Collectors.toCollection(LinkedHashSet::new)));

        globalRightsLayout.add(
                new Paragraph("Die globalen Rechte entsprechen der Legacy-Logik aus `employee.emp_rights` und bleiben auch fuer noch nicht migrierte Module editierbar."),
                globalRightsGroup
        );
    }

    private void buildRightsEditor() {
        rightsLayout.removeAll();
        rightsGroups.clear();

        if (!sessionData.canManageEmployeeMatrixRights()) {
            rightsLayout.add(new Paragraph("Die HQ-Rechtematrix ist nur fuer HQ-Benutzer mit Rechteverwaltung verfuegbar."));
            return;
        }
        if (userTypeField.getValue() == UserTypeOption.CUSTOMER) {
            rightsLayout.add(new Paragraph("Die HQ-Rechtematrix wird nur fuer HQ- und Lagerbenutzer gepflegt."));
            return;
        }

        if (headquartersOptions.isEmpty()) {
            rightsLayout.add(new Paragraph("Keine Niederlassungen geladen."));
            return;
        }
        if (availableRights.isEmpty()) {
            rightsLayout.add(new Paragraph("Kein Rechtekatalog geladen."));
            return;
        }

        rightsLayout.add(new Paragraph("Fachrechte werden pro Niederlassung vergeben."));
        for (HeadquartersOption headquarters : headquartersOptions) {
            H4 heading = new H4(headquarters.label());
            CheckboxGroup<RightOption> group = new CheckboxGroup<>();
            group.setLabel("Rechte");
            group.setItems(availableRights);
            group.setItemLabelGenerator(RightOption::label);
            group.setWidthFull();

            Set<RightOption> selectedRights = availableRights.stream()
                    .filter(right -> assignedRightIdsByHeadquarters
                            .getOrDefault(headquarters.id(), Set.of())
                            .contains(right.id()))
                    .collect(Collectors.toCollection(LinkedHashSet::new));
            group.setValue(selectedRights);
            rightsGroups.put(headquarters.id(), group);
            rightsLayout.add(heading, group);
        }
    }

    private void buildCostCenterAccessEditor() {
        costCenterAccessLayout.removeAll();

        if (!sessionData.canManageEmployeeCostCenterMatrix()) {
            costCenterAccessLayout.add(new Paragraph("Zusaetzliche Kostenstellenrechte sind fuer den angemeldeten Benutzer nicht freigegeben."));
            return;
        }
        if (!isCustomerUserType()) {
            costCenterAccessLayout.add(new Paragraph("Kostenstellenrechte werden nur fuer Kundenbenutzer gepflegt."));
            return;
        }
        if (customerField.getValue() == null) {
            costCenterAccessLayout.add(new Paragraph("Bitte zuerst einen Kunden waehlen."));
            return;
        }
        if (availableCostCenters.isEmpty()) {
            costCenterAccessLayout.add(new Paragraph("Keine Kostenstellen zum Kunden geladen."));
            return;
        }

        List<CostCenterOption> assignableCostCenters = availableCostCenters.stream()
                .filter(option -> primaryCostCenterField.getValue() == null
                        || !option.id().equals(primaryCostCenterField.getValue().id()))
                .toList();

        costCenterAccessGroup.setItems(assignableCostCenters);
        costCenterAccessGroup.setItemLabelGenerator(CostCenterOption::label);
        Set<CostCenterOption> selected = assignableCostCenters.stream()
                .filter(option -> assignedCostCenterIds.contains(option.id()))
                .collect(Collectors.toCollection(LinkedHashSet::new));
        costCenterAccessGroup.setValue(selected);

        costCenterAccessLayout.add(
                new Paragraph("Die Haupt-Kostenstelle ist automatisch freigegeben. Hier werden zusaetzliche Kostenstellen vergeben."),
                costCenterAccessGroup
        );
    }

    private void buildWarehouseConfigEditor() {
        warehouseConfigLayout.removeAll();
        warehouseSubstockGroups.clear();

        if (!sessionData.canManageEmployeeWarehouseConfig()) {
            warehouseConfigLayout.add(new Paragraph("Die Lagerkonfiguration ist fuer den angemeldeten Benutzer nicht freigegeben."));
            return;
        }
        if (!isWarehouseUserType()) {
            warehouseConfigLayout.add(new Paragraph("Lagerrechte werden nur fuer Lagernutzer gepflegt."));
            return;
        }
        if (warehouseRootOptions.isEmpty()) {
            warehouseConfigLayout.add(new Paragraph("Keine Hauptlaeger fuer die aktuelle Niederlassung gefunden."));
            return;
        }

        syncWarehouseDefaultRootOptions();

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

        List<WarehouseRootOption> selectedRoots = selectedWarehouseRoots();
        if (selectedRoots.isEmpty()) {
            sections.add(new Paragraph("Bitte zuerst mindestens ein Hauptlager freigeben."));
        } else {
            for (WarehouseRootOption root : selectedRoots) {
                VerticalLayout section = new VerticalLayout();
                section.setPadding(false);
                section.setSpacing(true);

                H4 heading = new H4(root.name());
                if (root.substocks().isEmpty()) {
                    section.add(
                            heading,
                            new Paragraph("Keine Unterlager gefunden. Ohne Unterlager-Matrix gilt der Zugriff fuer das gesamte Hauptlager.")
                    );
                } else {
                    CheckboxGroup<WarehouseSubstockOption> group = new CheckboxGroup<>();
                    group.setLabel("Schreibberechtigte Unterlager");
                    group.setWidthFull();
                    group.setItems(root.substocks());
                    group.setItemLabelGenerator(WarehouseSubstockOption::label);

                    Set<Long> selectedIds = assignedWarehouseSubstockIds.getOrDefault(root.id(), Set.of());
                    Set<WarehouseSubstockOption> selectedOptions = root.substocks().stream()
                            .filter(option -> selectedIds.contains(option.id()))
                            .collect(Collectors.toCollection(LinkedHashSet::new));
                    group.setValue(selectedOptions);
                    group.addValueChangeListener(event -> assignedWarehouseSubstockIds.put(
                            root.id(),
                            event.getValue().stream()
                                    .map(WarehouseSubstockOption::id)
                                    .collect(Collectors.toCollection(LinkedHashSet::new))
                    ));
                    warehouseSubstockGroups.put(root.id(), group);

                    section.add(
                            heading,
                            new Paragraph("Wenn hier nichts markiert ist, gilt Schreibzugriff fuer das gesamte Hauptlager. Die Sicht-Einschraenkung greift nur mit der Option darunter."),
                            group
                    );
                }
                sections.add(section);
            }
        }

        warehouseConfigLayout.add(
                new Paragraph("Die Konfiguration bestimmt zugaengliche Hauptlaeger, das Standardlager und optional eine feinere Unterlager-Matrix fuer Schreib- und Sichtrechte."),
                warehouseRootAccessGroup,
                warehouseDefaultRootField,
                warehouseReadOnlyField,
                warehouseArticleAccessField,
                warehouseRestrictVisibilityField,
                sections
        );
    }

    private void updateUserContext() {
        boolean customerUser = isCustomerUserType();
        customerField.setVisible(customerUser);
        primaryCostCenterField.setVisible(customerUser);
        if (customerUser && sessionData.isCustomerUser() && !customerOptions.isEmpty()) {
            customerField.setValue(customerOptions.get(0));
        }
        legacyRightsValue = normalizeRights(legacyRightsValue, userTypeField.getValue());
        buildGlobalRightsEditor();
        buildRightsEditor();
        buildCostCenterAccessEditor();
        buildWarehouseConfigEditor();
        applyFormPermissions();
    }

    private void save() {
        if (!canEditCurrentEmployee()) {
            Notification.show("Keine Berechtigung zum Bearbeiten von Mitarbeitern.", 5000, Notification.Position.MIDDLE)
                    .addThemeVariants(NotificationVariant.LUMO_ERROR);
            return;
        }
        try {
            if (isCustomerUserType() && (customerField.getValue() == null || primaryCostCenterField.getValue() == null)) {
                Notification.show("Kundenbenutzer benoetigen Kunde und Haupt-Kostenstelle.", 5000, Notification.Position.MIDDLE)
                        .addThemeVariants(NotificationVariant.LUMO_ERROR);
                return;
            }

            Map<String, Object> request = new HashMap<>();
            Map<String, Object> employee = new HashMap<>();
            String headquartersValue = headquartersField.getValue().trim();
            if (headquartersValue.isBlank()) {
                headquartersValue = buildHeadquartersValueFromRights();
            }
            if (headquartersValue.isBlank() && sessionData.getHeadquartersId() != null) {
                headquartersValue = String.valueOf(sessionData.getHeadquartersId());
            }
            headquartersField.setValue(headquartersValue);
            employee.put("headquarters", headquartersValue);
            employee.put("headquartersId", sessionData.getHeadquartersId());
            employee.put("costCenterId", isCustomerUserType() && primaryCostCenterField.getValue() != null
                    ? primaryCostCenterField.getValue().id()
                    : null);
            employee.put("rights", buildLegacyRightsValue());
            if (sessionData.isHeadquartersUser()) {
                employee.put("group", toGroupString(groupField.getValue()));
            }
            request.put("employee", employee);

            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("birthdate", birthdateField.getValue());
            user.put("type", userTypeField.getValue() != null ? userTypeField.getValue().code() : 1);
            user.put("headquartersId", sessionData.getHeadquartersId());
            request.put("user", user);

            if (isNew) {
                request.put("password", passwordField.getValue());
                @SuppressWarnings("unchecked")
                Map<String, Object> created = restClient.post("/employees", request, LinkedHashMap.class);
                employeeId = created != null ? toLong(created.get("id")) : null;
                isNew = false;
            } else {
                restClient.put("/employees/" + employeeId, request);
            }

            saveRights();
            saveCostCenterAccess();
            saveWarehouseConfig();
            Notification.show("Mitarbeiter gespeichert!", 3000, Notification.Position.MIDDLE)
                    .addThemeVariants(NotificationVariant.LUMO_SUCCESS);
            getUI().ifPresent(ui -> ui.navigate(EmployeeListView.class));
        } catch (Exception e) {
            Notification.show("Fehler: " + e.getMessage(), 5000, Notification.Position.MIDDLE)
                    .addThemeVariants(NotificationVariant.LUMO_ERROR);
        }
    }

    private void saveRights() {
        if (employeeId == null || !sessionData.canManageEmployeeMatrixRights()) {
            return;
        }

        List<Map<String, Object>> rights = new ArrayList<>();
        for (Map.Entry<Long, CheckboxGroup<RightOption>> entry : rightsGroups.entrySet()) {
            Long headquartersId = entry.getKey();
            for (RightOption option : entry.getValue().getValue()) {
                Map<String, Object> right = new HashMap<>();
                right.put("employeeId", employeeId);
                right.put("headquartersId", headquartersId);
                right.put("rightId", option.id());
                rights.add(right);
            }
        }

        restClient.put("/employees/" + employeeId + "/rights", rights);
    }

    private void saveCostCenterAccess() {
        if (employeeId == null || !sessionData.canManageEmployeeCostCenterMatrix()) {
            return;
        }

        List<Map<String, Object>> costCenters = new ArrayList<>();
        if (isCustomerUserType()) {
            for (CostCenterOption option : costCenterAccessGroup.getValue()) {
                Map<String, Object> row = new HashMap<>();
                row.put("employeeId", employeeId);
                row.put("costCenterId", option.id());
                costCenters.add(row);
            }
        }
        restClient.put("/employees/" + employeeId + "/costcenters", costCenters);
    }

    private void saveWarehouseConfig() {
        if (employeeId == null
                || !sessionData.canManageEmployeeWarehouseConfig()
                || !isWarehouseUserType()
                || employeeId.equals(sessionData.getEmployeeId())) {
            return;
        }

        Map<String, Object> request = new LinkedHashMap<>();
        request.put("selectedRootIds", selectedWarehouseRoots().stream()
                .map(WarehouseRootOption::id)
                .toList());
        request.put("defaultRootStockId", warehouseDefaultRootField.getValue() != null
                ? warehouseDefaultRootField.getValue().id()
                : null);
        request.put("readOnly", warehouseReadOnlyField.getValue());
        request.put("articleAccess", warehouseArticleAccessField.getValue());
        request.put("restrictVisibilityToAssignedSubstocks", warehouseRestrictVisibilityField.getValue());

        List<Map<String, Object>> assignments = new ArrayList<>();
        for (WarehouseRootOption root : selectedWarehouseRoots()) {
            Set<Long> selectedIds = assignedWarehouseSubstockIds.getOrDefault(root.id(), Set.of());
            if (selectedIds.isEmpty()) {
                continue;
            }
            Map<String, Object> assignment = new LinkedHashMap<>();
            assignment.put("rootStockId", root.id());
            assignment.put("stockIds", new ArrayList<>(selectedIds));
            assignments.add(assignment);
        }
        request.put("substockAssignments", assignments);
        restClient.put("/employees/" + employeeId + "/warehouse-config", request);
    }

    private boolean isCustomerUserType() {
        return userTypeField.getValue() == UserTypeOption.CUSTOMER;
    }

    private boolean isWarehouseUserType() {
        return userTypeField.getValue() == UserTypeOption.WAREHOUSE;
    }

    private boolean canEditCurrentEmployee() {
        if (isNew) {
            return sessionData.canManageEmployees();
        }
        return sessionData.canEditEmployee(employeeId) || canEditOwnEmployeeWithGlobalOverride();
    }

    private boolean canEditOwnEmployeeWithGlobalOverride() {
        return sessionData.isCustomerUser()
                && employeeId != null
                && employeeId.equals(sessionData.getEmployeeId())
                && "1".equals(parameterProvider.get("GLOBAL_CUSTOMER_READONLY_DISABLED"));
    }

    private void applyFormPermissions() {
        boolean employeeAdmin = sessionData.canManageEmployees();
        boolean editable = canEditCurrentEmployee();

        accountField.setReadOnly(!editable);
        nameField.setReadOnly(!editable);
        firstnameField.setReadOnly(!editable);
        emailField.setReadOnly(!editable);
        phoneField.setReadOnly(!editable);
        phone2Field.setReadOnly(!editable);
        birthdateField.setReadOnly(!editable);
        passwordField.setVisible(isNew && employeeAdmin);
        userTypeField.setReadOnly(sessionData.isCustomerUser() || !employeeAdmin);
        headquartersField.setReadOnly(!employeeAdmin);
        customerField.setReadOnly(sessionData.isCustomerUser() || !employeeAdmin);
        primaryCostCenterField.setReadOnly(!employeeAdmin);
        groupField.setReadOnly(!employeeAdmin);
        globalRightsGroup.setReadOnly(!employeeAdmin);
        costCenterAccessGroup.setReadOnly(!sessionData.canManageEmployeeCostCenterMatrix() || !employeeAdmin);
        boolean warehouseEditable = sessionData.canManageEmployeeWarehouseConfig()
                && employeeAdmin
                && (isNew || employeeId == null || !employeeId.equals(sessionData.getEmployeeId()));
        warehouseRootAccessGroup.setReadOnly(!warehouseEditable);
        warehouseDefaultRootField.setReadOnly(!warehouseEditable);
        warehouseReadOnlyField.setEnabled(warehouseEditable);
        warehouseArticleAccessField.setEnabled(warehouseEditable);
        warehouseRestrictVisibilityField.setEnabled(warehouseEditable);
        for (CheckboxGroup<WarehouseSubstockOption> group : warehouseSubstockGroups.values()) {
            group.setReadOnly(!warehouseEditable);
        }
        syncHeadquartersButton.setVisible(employeeAdmin);
        syncHeadquartersButton.setEnabled(employeeAdmin && !headquartersOptions.isEmpty());
        saveButton.setEnabled(editable);
    }

    private String buildHeadquartersValueFromRights() {
        return rightsGroups.entrySet().stream()
                .filter(entry -> !entry.getValue().getValue().isEmpty())
                .map(Map.Entry::getKey)
                .map(String::valueOf)
                .collect(Collectors.joining(","));
    }

    private String buildLegacyRightsValue() {
        String normalized = normalizeRights(legacyRightsValue, userTypeField.getValue());
        char[] flags = normalized.toCharArray();
        for (GlobalRightOption option : globalRightOptions(userTypeField.getValue())) {
            if (option.index() >= 0 && option.index() < flags.length) {
                flags[option.index()] = '0';
            }
        }
        for (GlobalRightOption option : globalRightsGroup.getValue()) {
            if (option.index() >= 0 && option.index() < flags.length) {
                flags[option.index()] = '1';
            }
        }
        legacyRightsValue = new String(flags);
        return legacyRightsValue;
    }

    private String normalizeRights(String value, UserTypeOption userType) {
        String normalized = value == null || value.isBlank() ? defaultRightsFor(userType) : value.trim();
        int minimumLength = requiredRightsLength(userType);
        if (normalized.length() >= minimumLength) {
            return normalized;
        }
        return normalized + "0".repeat(minimumLength - normalized.length());
    }

    private String defaultRightsFor(UserTypeOption userType) {
        if (userType == UserTypeOption.CUSTOMER) {
            return "11111100001000000000000000000000000000000000000000";
        }
        if (userType == UserTypeOption.WAREHOUSE) {
            return "00000000000000000000000000000000000000000000000000";
        }
        return "11111111111111101111111111111111111111111111111111";
    }

    private List<GlobalRightOption> globalRightOptions(UserTypeOption userType) {
        if (userType == UserTypeOption.CUSTOMER) {
            return List.of(
                    new GlobalRightOption(0, "Kostenstellennavigation"),
                    new GlobalRightOption(1, "Kostenstellenverwaltung"),
                    new GlobalRightOption(2, "Mitarbeiter"),
                    new GlobalRightOption(3, "Adressen"),
                    new GlobalRightOption(4, "Auftraege"),
                    new GlobalRightOption(5, "Rechnungen"),
                    new GlobalRightOption(6, "Intervall"),
                    new GlobalRightOption(7, "Export"),
                    new GlobalRightOption(8, "Statistik"),
                    new GlobalRightOption(9, "Kartenuebersicht"),
                    new GlobalRightOption(10, "Benutzerkonten aendern"),
                    new GlobalRightOption(11, "Dokumente"),
                    new GlobalRightOption(12, "Benutzer-Kostenstellen-Matrix"),
                    new GlobalRightOption(13, "Termine"),
                    new GlobalRightOption(14, "Scan-Objektmanagement"),
                    new GlobalRightOption(15, "Import")
            );
        }
        if (userType == UserTypeOption.WAREHOUSE) {
            return List.of();
        }
        return availableRights.stream()
                .map(option -> new GlobalRightOption(Math.toIntExact(option.id()), option.label()))
                .toList();
    }

    private int requiredRightsLength(UserTypeOption userType) {
        int minimumLength = Math.max(50, defaultRightsFor(userType).length());
        for (GlobalRightOption option : globalRightOptions(userType)) {
            minimumLength = Math.max(minimumLength, option.index() + 1);
        }
        return minimumLength;
    }

    private void resetState() {
        employeeId = null;
        isNew = true;
        availableRights.clear();
        headquartersOptions.clear();
        assignedRightIdsByHeadquarters.clear();
        rightsGroups.clear();
        customerOptions.clear();
        availableCostCenters.clear();
        groupOptions.clear();
        assignedCostCenterIds.clear();
        warehouseRootOptions.clear();
        warehouseSubstockGroups.clear();
        assignedWarehouseSubstockIds.clear();
        legacyRightsValue = defaultRightsFor(userTypeField.getValue());

        accountField.clear();
        userTypeField.setValue(sessionData.isCustomerUser() ? UserTypeOption.CUSTOMER : UserTypeOption.HEADQUARTERS);
        nameField.clear();
        firstnameField.clear();
        emailField.clear();
        phoneField.clear();
        phone2Field.clear();
        birthdateField.clear();
        passwordField.clear();
        headquartersField.clear();
        customerField.clear();
        customerField.setItems(List.of());
        customerField.setReadOnly(sessionData.isCustomerUser());
        primaryCostCenterField.clear();
        primaryCostCenterField.setItems(List.of());
        groupField.clear();
        groupField.setItems(groupOptions);
        costCenterAccessGroup.clear();
        warehouseRootAccessGroup.clear();
        warehouseRootAccessGroup.setItems(List.of());
        warehouseDefaultRootField.clear();
        warehouseDefaultRootField.setItems(List.of());
        warehouseReadOnlyField.setValue(false);
        warehouseArticleAccessField.setValue(false);
        warehouseRestrictVisibilityField.setValue(true);
    }

    private void applyWarehouseConfig(Map<String, Object> data) {
        warehouseConfigLoading = true;
        try {
            warehouseRootOptions.clear();
            assignedWarehouseSubstockIds.clear();

            for (Map<String, Object> root : asMapList(data.get("roots"))) {
                Long rootId = toLong(root.get("id"));
                if (rootId == null) {
                    continue;
                }
                List<WarehouseSubstockOption> substocks = new ArrayList<>();
                for (Map<String, Object> stock : asMapList(root.get("substocks"))) {
                    Long stockId = toLong(stock.get("id"));
                    if (stockId == null) {
                        continue;
                    }
                    substocks.add(new WarehouseSubstockOption(
                            stockId,
                            toLong(stock.get("parentId")),
                            toInteger(stock.get("level"), 0),
                            str(stock.get("name"))
                    ));
                }
                warehouseRootOptions.add(new WarehouseRootOption(rootId, str(root.get("name")), substocks));
            }

            for (Map<String, Object> assignment : asMapList(data.get("substockAssignments"))) {
                Long rootId = toLong(assignment.get("rootStockId"));
                if (rootId == null) {
                    continue;
                }
                assignedWarehouseSubstockIds.put(
                        rootId,
                        toLongList(assignment.get("stockIds")).stream()
                                .collect(Collectors.toCollection(LinkedHashSet::new))
                );
            }

            warehouseRootAccessGroup.setItems(warehouseRootOptions);
            Set<Long> selectedRootIds = toLongList(data.get("selectedRootIds")).stream()
                    .collect(Collectors.toCollection(LinkedHashSet::new));
            warehouseRootAccessGroup.setValue(warehouseRootOptions.stream()
                    .filter(option -> selectedRootIds.contains(option.id()))
                    .collect(Collectors.toCollection(LinkedHashSet::new)));

            warehouseReadOnlyField.setValue(toBoolean(data.get("readOnly")));
            warehouseArticleAccessField.setValue(toBoolean(data.get("articleAccess")));
            warehouseRestrictVisibilityField.setValue(!data.containsKey("restrictVisibilityToAssignedSubstocks")
                    || toBoolean(data.get("restrictVisibilityToAssignedSubstocks")));

            syncWarehouseDefaultRootOptions();
            Long defaultRootId = toLong(data.get("defaultRootStockId"));
            if (defaultRootId != null) {
                selectedWarehouseRoots().stream()
                        .filter(option -> option.id().equals(defaultRootId))
                        .findFirst()
                        .ifPresent(warehouseDefaultRootField::setValue);
            } else {
                warehouseDefaultRootField.clear();
            }
        } finally {
            warehouseConfigLoading = false;
        }
    }

    private void syncWarehouseDefaultRootOptions() {
        List<WarehouseRootOption> selectedRoots = selectedWarehouseRoots();
        warehouseDefaultRootField.setItems(selectedRoots);
        WarehouseRootOption current = warehouseDefaultRootField.getValue();
        if (current != null && selectedRoots.stream().noneMatch(option -> option.id().equals(current.id()))) {
            warehouseDefaultRootField.clear();
        }
    }

    private List<WarehouseRootOption> selectedWarehouseRoots() {
        Set<Long> selectedIds = warehouseRootAccessGroup.getValue().stream()
                .map(WarehouseRootOption::id)
                .collect(Collectors.toCollection(LinkedHashSet::new));
        return warehouseRootOptions.stream()
                .filter(option -> selectedIds.contains(option.id()))
                .toList();
    }

    @SuppressWarnings("unchecked")
    private List<Map<String, Object>> asMapList(Object value) {
        if (value instanceof List<?> list) {
            return list.stream()
                    .filter(Map.class::isInstance)
                    .map(entry -> (Map<String, Object>) entry)
                    .toList();
        }
        return List.of();
    }

    private List<Long> toLongList(Object value) {
        List<Long> result = new ArrayList<>();
        if (value instanceof List<?> list) {
            for (Object item : list) {
                Long parsed = toLong(item);
                if (parsed != null) {
                    result.add(parsed);
                }
            }
        }
        return result;
    }

    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 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 Set<Long> parseLongTokens(String value) {
        Set<Long> result = new LinkedHashSet<>();
        if (value == null || value.isBlank()) {
            return result;
        }
        for (String token : value.split(",")) {
            if (token == null || token.isBlank()) {
                continue;
            }
            try {
                result.add(Long.parseLong(token.trim()));
            } catch (NumberFormatException ignored) {
            }
        }
        return result;
    }

    private boolean toBoolean(Object value) {
        if (value instanceof Boolean bool) {
            return bool;
        }
        if (value instanceof Number number) {
            return number.intValue() != 0;
        }
        return "true".equalsIgnoreCase(str(value)) || "1".equals(str(value));
    }

    private Long toLong(Object obj) {
        if (obj == null) {
            return null;
        }
        if (obj instanceof Number number) {
            return number.longValue();
        }
        try {
            return Long.parseLong(obj.toString());
        } catch (NumberFormatException e) {
            return null;
        }
    }

    private Integer toInteger(Object obj, Integer fallback) {
        if (obj == null) {
            return fallback;
        }
        if (obj instanceof Number number) {
            return number.intValue();
        }
        try {
            return Integer.parseInt(obj.toString());
        } catch (NumberFormatException e) {
            return fallback;
        }
    }

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

    private record GlobalRightOption(int index, String label) { }

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

    private record HeadquartersOption(Long id, String name, String mnemonic) {
        private String label() {
            if (mnemonic != null && !mnemonic.isBlank()) {
                return mnemonic + " - " + name + " (" + id + ")";
            }
            return name + " (" + id + ")";
        }
    }

    private record CustomerOption(Long id, String companyName, String eid) {
        private String label() {
            return companyName + (eid != null && !eid.isBlank() ? " [" + eid + "]" : "");
        }
    }

    private record CostCenterOption(Long id, String name, String path) {
        private String label() {
            if (path != null && !path.isBlank()) {
                return name + " (" + path + ")";
            }
            return name + " (" + id + ")";
        }
    }

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

    private record WarehouseRootOption(Long id, String name, List<WarehouseSubstockOption> substocks) {
        private String label() {
            return name + " (" + id + ")";
        }
    }

    private record WarehouseSubstockOption(Long id, Long parentId, int level, String name) {
        private String label() {
            String prefix = level > 1 ? "--".repeat(level - 1) + " " : "";
            return prefix + name + " (" + id + ")";
        }
    }

    private enum UserTypeOption {
        HEADQUARTERS(1, "HQ-Mitarbeiter"),
        CUSTOMER(2, "Kundenbenutzer"),
        WAREHOUSE(4, "Lager");

        private final int code;
        private final String label;

        UserTypeOption(int code, String label) {
            this.code = code;
            this.label = label;
        }

        private int code() {
            return code;
        }

        private String label() {
            return label;
        }

        private static UserTypeOption fromCode(Integer code) {
            if (Integer.valueOf(2).equals(code)) {
                return CUSTOMER;
            }
            if (Integer.valueOf(4).equals(code)) {
                return WAREHOUSE;
            }
            return HEADQUARTERS;
        }
    }
}
