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.datepicker.DatePicker;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.html.Anchor;
import com.vaadin.flow.component.html.H3;
import com.vaadin.flow.component.html.H4;
import com.vaadin.flow.component.html.Span;
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.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.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Route(value = "disposition", layout = MainLayout.class)
@PageTitle("Votian - Disposition")
public class DispositionView extends VerticalLayout {

    private final RestClientService restClient;
    private final SessionData sessionData;

    private final DatePicker dayField = new DatePicker("Tag");

    private final Span totalJobs = new Span("-");
    private final Span assignedJobs = new Span("-");
    private final Span unassignedJobs = new Span("-");
    private final Span availableVehicles = new Span("-");

    private final Grid<JobRow> jobGrid = new Grid<>(JobRow.class, false);
    private final Grid<VehicleRow> vehicleGrid = new Grid<>(VehicleRow.class, false);
    private final Grid<StationRow> stationGrid = new Grid<>(StationRow.class, false);

    private final Button assignButton = new Button("Zuordnen");
    private final Button clearAssignmentButton = new Button("Zuordnung loesen");
    private final Button openJobButton = new Button("Auftrag oeffnen");

    private final Span detailJob = new Span("-");
    private final Span detailCustomer = new Span("-");
    private final Span detailCostCenter = new Span("-");
    private final Span detailStatus = new Span("-");
    private final Span detailCourier = new Span("-");
    private final Span detailVehicle = new Span("-");
    private final Span detailPickup = new Span("-");
    private final Span detailDelivery = new Span("-");
    private final Anchor routeLink = createLink("Route in Google Maps");
    private final Anchor pickupMapLink = createLink("Abholung auf Karte");
    private final Anchor deliveryMapLink = createLink("Zustellung auf Karte");

    private final Span stationJob = new Span("-");
    private final Span stationCustomer = new Span("-");
    private final Span stationName = new Span("-");
    private final Span stationStatus = new Span("-");
    private final Span stationAddress = new Span("-");
    private final Span stationCourier = new Span("-");
    private final Span stationRemark = new Span("-");
    private final Span stationPhone = new Span("-");
    private final Anchor stationMapLink = createLink("Station auf Karte");
    private final Anchor stationRouteLink = createLink("Auftragsroute");

    private final List<JobRow> jobs = new ArrayList<>();
    private final List<VehicleRow> vehicles = new ArrayList<>();
    private final List<StationRow> stations = new ArrayList<>();
    private final Map<Long, JobRow> jobsById = new HashMap<>();

    private JobRow selectedJob;
    private VehicleRow selectedVehicle;
    private StationRow selectedStation;

    public DispositionView(RestClientService restClient, SessionData sessionData) {
        this.restClient = restClient;
        this.sessionData = sessionData;

        setSizeFull();
        setPadding(true);
        setSpacing(true);

        dayField.setValue(LocalDate.now());

        configureJobGrid();
        configureVehicleGrid();
        configureStationGrid();
        configureActions();
        clearJobDetail();
        clearStationDetail();

        Button reloadButton = new Button("Neu laden", event -> loadWorkspace(selectedJob != null ? selectedJob.jobId() : null));
        reloadButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        HorizontalLayout filterBar = new HorizontalLayout(dayField, reloadButton);
        filterBar.setAlignItems(Alignment.BASELINE);

        HorizontalLayout cards = new HorizontalLayout(
                createCard("Auftraege", totalJobs),
                createCard("Zugeordnet", assignedJobs),
                createCard("Offen", unassignedJobs),
                createCard("Fahrzeuge", availableVehicles)
        );
        cards.setWidthFull();

        VerticalLayout dispositionContent = buildDispositionContent();
        VerticalLayout locatingContent = buildLocatingContent();
        locatingContent.setVisible(false);

        Tab dispositionTab = new Tab("Disposition");
        Tab locatingTab = new Tab("Locating");
        Tabs tabs = new Tabs(dispositionTab, locatingTab);
        Map<Tab, Component> tabContent = Map.of(
                dispositionTab, dispositionContent,
                locatingTab, locatingContent
        );
        tabs.addSelectedChangeListener(event -> {
            for (Component content : tabContent.values()) {
                content.setVisible(false);
            }
            tabContent.get(event.getSelectedTab()).setVisible(true);
        });

        add(new H3("Disposition"), filterBar, cards, tabs, dispositionContent, locatingContent);
        expand(dispositionContent, locatingContent);

        loadWorkspace(null);
    }

    private void configureJobGrid() {
        jobGrid.addColumn(JobRow::jobId).setHeader("Auftrag").setAutoWidth(true).setFrozen(true);
        jobGrid.addColumn(row -> formatDateTime(row.orderTime())).setHeader("Zeit").setAutoWidth(true);
        jobGrid.addColumn(JobRow::customerName).setHeader("Kunde").setFlexGrow(1);
        jobGrid.addColumn(JobRow::pickupName).setHeader("Abholung").setFlexGrow(1);
        jobGrid.addColumn(JobRow::deliveryName).setHeader("Zustellung").setFlexGrow(1);
        jobGrid.addColumn(row -> statusLabel(row.status(), false)).setHeader("Status").setAutoWidth(true);
        jobGrid.addColumn(JobRow::courierSid).setHeader("Kurier").setAutoWidth(true);
        jobGrid.addColumn(JobRow::vehicleDisplay).setHeader("Fahrzeug").setAutoWidth(true);
        jobGrid.addColumn(row -> formatAmount(row.totalPrice())).setHeader("Umsatz").setAutoWidth(true);
        jobGrid.setSizeFull();
        jobGrid.asSingleSelect().addValueChangeListener(event -> {
            selectedJob = event.getValue();
            refreshActionState();
            updateJobDetail();
        });
    }

    private void configureVehicleGrid() {
        vehicleGrid.addColumn(VehicleRow::courierSid).setHeader("Kurier").setAutoWidth(true);
        vehicleGrid.addColumn(VehicleRow::courierEid).setHeader("EID").setAutoWidth(true);
        vehicleGrid.addColumn(VehicleRow::vehicleDisplay).setHeader("Fahrzeug").setAutoWidth(true);
        vehicleGrid.addColumn(row -> vehicleTypeLabel(row.vehicleTypeId())).setHeader("Typ").setAutoWidth(true);
        vehicleGrid.addColumn(row -> availabilityLabel(row.available())).setHeader("Verfuegbar").setAutoWidth(true);
        vehicleGrid.addColumn(VehicleRow::assignedJobs).setHeader("Auftraege").setAutoWidth(true);
        vehicleGrid.setWidthFull();
        vehicleGrid.setMinHeight("260px");
        vehicleGrid.asSingleSelect().addValueChangeListener(event -> {
            selectedVehicle = event.getValue();
            refreshActionState();
        });
    }

    private void configureStationGrid() {
        stationGrid.addColumn(StationRow::jobId).setHeader("Auftrag").setAutoWidth(true).setFrozen(true);
        stationGrid.addColumn(StationRow::stopSort).setHeader("Stop").setAutoWidth(true);
        stationGrid.addColumn(row -> formatDateTime(row.orderTime())).setHeader("Zeit").setAutoWidth(true);
        stationGrid.addColumn(StationRow::customerName).setHeader("Kunde").setFlexGrow(1);
        stationGrid.addColumn(StationRow::stopName).setHeader("Station").setFlexGrow(1);
        stationGrid.addColumn(StationRow::addressLabel).setHeader("Adresse").setFlexGrow(1);
        stationGrid.addColumn(row -> statusLabel(row.stopStatus(), true)).setHeader("Status").setAutoWidth(true);
        stationGrid.addColumn(StationRow::courierSid).setHeader("Kurier").setAutoWidth(true);
        stationGrid.addComponentColumn(row -> mapAnchor(row.mapUrl(), "Karte")).setHeader("Karte").setAutoWidth(true);
        stationGrid.setSizeFull();
        stationGrid.asSingleSelect().addValueChangeListener(event -> {
            selectedStation = event.getValue();
            updateStationDetail();
        });
    }

    private void configureActions() {
        assignButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        assignButton.addClickListener(event -> assignSelectedVehicle());

        clearAssignmentButton.addThemeVariants(ButtonVariant.LUMO_ERROR);
        clearAssignmentButton.addClickListener(event -> clearSelectedAssignment());

        openJobButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
        openJobButton.addClickListener(event -> {
            if (selectedJob != null && sessionData.canViewJobs()) {
                getUI().ifPresent(ui -> ui.navigate("jobs/" + selectedJob.jobId()));
            }
        });

        refreshActionState();
    }

    private VerticalLayout buildDispositionContent() {
        VerticalLayout jobPanel = new VerticalLayout(new H4("Tagesauftraege"), jobGrid);
        jobPanel.setPadding(false);
        jobPanel.setSpacing(true);
        jobPanel.setSizeFull();
        jobPanel.expand(jobGrid);

        HorizontalLayout actions = new HorizontalLayout(assignButton, clearAssignmentButton, openJobButton);
        actions.setAlignItems(Alignment.BASELINE);

        VerticalLayout vehiclePanel = new VerticalLayout(new H4("Fahrzeuge"), vehicleGrid);
        vehiclePanel.setPadding(false);
        vehiclePanel.setSpacing(true);
        vehiclePanel.setWidthFull();

        VerticalLayout detailPanel = buildJobDetailPanel();

        VerticalLayout sideColumn = new VerticalLayout(actions, vehiclePanel, detailPanel);
        sideColumn.setPadding(false);
        sideColumn.setSpacing(true);
        sideColumn.setWidth("520px");
        sideColumn.setHeightFull();
        sideColumn.expand(vehiclePanel);

        HorizontalLayout workspace = new HorizontalLayout(jobPanel, sideColumn);
        workspace.setSizeFull();
        workspace.expand(jobPanel);

        VerticalLayout content = new VerticalLayout(workspace);
        content.setPadding(false);
        content.setSpacing(false);
        content.setSizeFull();
        content.expand(workspace);
        return content;
    }

    private VerticalLayout buildLocatingContent() {
        VerticalLayout stationPanel = new VerticalLayout(new H4("Stationen"), stationGrid);
        stationPanel.setPadding(false);
        stationPanel.setSpacing(true);
        stationPanel.setSizeFull();
        stationPanel.expand(stationGrid);

        VerticalLayout detailPanel = buildStationDetailPanel();
        detailPanel.setWidth("520px");

        HorizontalLayout workspace = new HorizontalLayout(stationPanel, detailPanel);
        workspace.setSizeFull();
        workspace.expand(stationPanel);

        VerticalLayout content = new VerticalLayout(workspace);
        content.setPadding(false);
        content.setSpacing(false);
        content.setSizeFull();
        content.expand(workspace);
        return content;
    }

    private VerticalLayout buildJobDetailPanel() {
        VerticalLayout panel = new VerticalLayout();
        panel.setPadding(true);
        panel.setSpacing(true);
        panel.getStyle()
                .set("border", "1px solid var(--lumo-contrast-20pct)")
                .set("border-radius", "var(--lumo-border-radius-l)");

        panel.add(
                new H4("Auftragsdetail"),
                detailLine("Auftrag", detailJob),
                detailLine("Kunde", detailCustomer),
                detailLine("Kostenstelle", detailCostCenter),
                detailLine("Status", detailStatus),
                detailLine("Kurier", detailCourier),
                detailLine("Fahrzeug", detailVehicle),
                detailLine("Abholung", detailPickup),
                detailLine("Zustellung", detailDelivery),
                routeLink,
                pickupMapLink,
                deliveryMapLink
        );
        return panel;
    }

    private VerticalLayout buildStationDetailPanel() {
        VerticalLayout panel = new VerticalLayout();
        panel.setPadding(true);
        panel.setSpacing(true);
        panel.getStyle()
                .set("border", "1px solid var(--lumo-contrast-20pct)")
                .set("border-radius", "var(--lumo-border-radius-l)");

        panel.add(
                new H4("Stationsdetail"),
                detailLine("Auftrag", stationJob),
                detailLine("Kunde", stationCustomer),
                detailLine("Station", stationName),
                detailLine("Status", stationStatus),
                detailLine("Adresse", stationAddress),
                detailLine("Kurier", stationCourier),
                detailLine("Telefon", stationPhone),
                detailLine("Bemerkung", stationRemark),
                stationMapLink,
                stationRouteLink
        );
        return panel;
    }

    private HorizontalLayout detailLine(String label, Span value) {
        Span caption = new Span(label + ":");
        caption.getStyle().set("font-weight", "600");
        HorizontalLayout line = new HorizontalLayout(caption, value);
        line.setPadding(false);
        line.setSpacing(true);
        return line;
    }

    private VerticalLayout createCard(String title, Span value) {
        VerticalLayout card = new VerticalLayout();
        card.setPadding(true);
        card.setSpacing(false);
        card.getStyle()
                .set("border", "1px solid var(--lumo-contrast-20pct)")
                .set("border-radius", "var(--lumo-border-radius-l)")
                .set("min-width", "180px");

        H4 headline = new H4(title);
        headline.getStyle().set("margin", "0");
        value.getStyle().set("font-size", "var(--lumo-font-size-xl)").set("font-weight", "700");
        card.add(headline, value);
        return card;
    }

    private void loadWorkspace(Long preferredJobId) {
        if (sessionData.getHeadquartersId() == null) {
            return;
        }
        loadJobs(preferredJobId);
        loadVehicles();
        loadStations();
    }

    private void loadJobs(Long preferredJobId) {
        try {
            jobs.clear();
            jobsById.clear();
            List<LinkedHashMap<String, Object>> data = restClient.getList(
                    "/disposition/jobs?hqId=" + sessionData.getHeadquartersId() + "&day=" + dayField.getValue(),
                    new ParameterizedTypeReference<>() {}
            );
            long assignedCount = 0L;
            for (Map<String, Object> row : data) {
                JobRow item = new JobRow(
                        toLong(row.get("jobId")),
                        toInteger(row.get("status")),
                        str(row.get("orderTime")),
                        str(row.get("customerName")),
                        str(row.get("costCenterName")),
                        str(row.get("pickupName")),
                        str(row.get("pickupStreet")),
                        str(row.get("pickupZipcode")),
                        str(row.get("pickupCity")),
                        str(row.get("deliveryName")),
                        str(row.get("deliveryStreet")),
                        str(row.get("deliveryZipcode")),
                        str(row.get("deliveryCity")),
                        toLong(row.get("courierId")),
                        str(row.get("courierSid")),
                        toInteger(row.get("vehicleTypeId")),
                        toLong(row.get("courierVehicleId")),
                        str(row.get("courierVehicleSid")),
                        toBigDecimal(row.get("totalPrice")),
                        Boolean.TRUE.equals(row.get("exported")) || "true".equalsIgnoreCase(str(row.get("exported")))
                );
                jobs.add(item);
                jobsById.put(item.jobId(), item);
                if (item.courierId() != null || item.courierVehicleId() != null) {
                    assignedCount++;
                }
            }
            jobGrid.setItems(jobs);
            totalJobs.setText(String.valueOf(jobs.size()));
            assignedJobs.setText(String.valueOf(assignedCount));
            unassignedJobs.setText(String.valueOf(Math.max(0, jobs.size() - assignedCount)));
            selectedJob = null;
            jobGrid.asSingleSelect().clear();
            if (preferredJobId != null) {
                jobs.stream()
                        .filter(job -> preferredJobId.equals(job.jobId()))
                        .findFirst()
                        .ifPresent(jobGrid::select);
            }
            if (jobGrid.asSingleSelect().getValue() == null && !jobs.isEmpty()) {
                jobGrid.select(jobs.get(0));
            }
            if (jobs.isEmpty()) {
                clearJobDetail();
            }
        } catch (Exception e) {
            Notification.show("Disposition konnte nicht geladen werden.", 4000, Notification.Position.MIDDLE)
                    .addThemeVariants(NotificationVariant.LUMO_ERROR);
        }
    }

    private void loadVehicles() {
        try {
            vehicles.clear();
            List<LinkedHashMap<String, Object>> data = restClient.getList(
                    "/disposition/vehicles?hqId=" + sessionData.getHeadquartersId() + "&day=" + dayField.getValue(),
                    new ParameterizedTypeReference<>() {}
            );
            long availableCount = 0L;
            for (Map<String, Object> row : data) {
                VehicleRow item = new VehicleRow(
                        toLong(row.get("courierId")),
                        str(row.get("courierSid")),
                        str(row.get("courierEid")),
                        str(row.get("available")),
                        toLong(row.get("courierVehicleId")),
                        str(row.get("courierVehicleSid")),
                        toInteger(row.get("vehicleTypeId")),
                        toLong(row.get("assignedJobs")) != null ? toLong(row.get("assignedJobs")) : 0L
                );
                vehicles.add(item);
                if ("1".equals(item.available())) {
                    availableCount++;
                }
            }
            vehicleGrid.setItems(vehicles);
            availableVehicles.setText(String.valueOf(availableCount));
            selectedVehicle = null;
            vehicleGrid.asSingleSelect().clear();
            refreshActionState();
        } catch (Exception e) {
            Notification.show("Fahrzeuge konnten nicht geladen werden.", 4000, Notification.Position.MIDDLE)
                    .addThemeVariants(NotificationVariant.LUMO_ERROR);
        }
    }

    private void loadStations() {
        try {
            stations.clear();
            List<LinkedHashMap<String, Object>> data = restClient.getList(
                    "/disposition/stations?hqId=" + sessionData.getHeadquartersId() + "&day=" + dayField.getValue(),
                    new ParameterizedTypeReference<>() {}
            );
            for (Map<String, Object> row : data) {
                stations.add(new StationRow(
                        toLong(row.get("jobId")),
                        str(row.get("orderTime")),
                        toInteger(row.get("stopSort")),
                        toInteger(row.get("stopStatus")),
                        str(row.get("customerName")),
                        str(row.get("commissionNo")),
                        str(row.get("stopName")),
                        str(row.get("street")),
                        str(row.get("zipcode")),
                        str(row.get("city")),
                        str(row.get("country")),
                        str(row.get("remark")),
                        str(row.get("phone")),
                        str(row.get("courierSid"))
                ));
            }
            stationGrid.setItems(stations);
            selectedStation = null;
            stationGrid.asSingleSelect().clear();
            if (!stations.isEmpty()) {
                stationGrid.select(stations.get(0));
            } else {
                clearStationDetail();
            }
        } catch (Exception e) {
            Notification.show("Stationsdaten konnten nicht geladen werden.", 4000, Notification.Position.MIDDLE)
                    .addThemeVariants(NotificationVariant.LUMO_ERROR);
        }
    }

    private void assignSelectedVehicle() {
        if (!sessionData.canManageDisposition()) {
            return;
        }
        if (selectedJob == null || selectedVehicle == null) {
            return;
        }

        try {
            Map<String, Object> request = new LinkedHashMap<>();
            request.put("courierId", selectedVehicle.courierId());
            request.put("courierVehicleId", selectedVehicle.courierVehicleId());
            restClient.put(
                    "/disposition/jobs/" + selectedJob.jobId() + "/assignment?hqId=" + sessionData.getHeadquartersId(),
                    request
            );
            loadWorkspace(selectedJob.jobId());
        } catch (Exception e) {
            Notification.show("Zuordnung konnte nicht gespeichert werden: " + e.getMessage(),
                            5000, Notification.Position.MIDDLE)
                    .addThemeVariants(NotificationVariant.LUMO_ERROR);
        }
    }

    private void clearSelectedAssignment() {
        if (!sessionData.canManageDisposition()) {
            return;
        }
        if (selectedJob == null) {
            return;
        }

        try {
            restClient.delete(
                    "/disposition/jobs/" + selectedJob.jobId() + "/assignment?hqId=" + sessionData.getHeadquartersId()
            );
            loadWorkspace(selectedJob.jobId());
        } catch (Exception e) {
            Notification.show("Zuordnung konnte nicht geloest werden: " + e.getMessage(),
                            5000, Notification.Position.MIDDLE)
                    .addThemeVariants(NotificationVariant.LUMO_ERROR);
        }
    }

    private void refreshActionState() {
        assignButton.setEnabled(sessionData.canManageDisposition() && selectedJob != null && selectedVehicle != null);
        clearAssignmentButton.setEnabled(sessionData.canManageDisposition() && selectedJob != null
                && (selectedJob.courierId() != null || selectedJob.courierVehicleId() != null));
        openJobButton.setEnabled(sessionData.canViewJobs() && selectedJob != null);
    }

    private void updateJobDetail() {
        if (selectedJob == null) {
            clearJobDetail();
            return;
        }

        detailJob.setText(String.valueOf(selectedJob.jobId()));
        detailCustomer.setText(selectedJob.customerName());
        detailCostCenter.setText(selectedJob.costCenterName());
        detailStatus.setText(statusLabel(selectedJob.status(), false));
        detailCourier.setText(selectedJob.courierSid().isBlank() ? "-" : selectedJob.courierSid());
        detailVehicle.setText(selectedJob.vehicleDisplay());
        detailPickup.setText(selectedJob.pickupAddressLabel());
        detailDelivery.setText(selectedJob.deliveryAddressLabel());
        updateLink(routeLink, directionsUrl(selectedJob.pickupAddressLabel(), selectedJob.deliveryAddressLabel()), true);
        updateLink(pickupMapLink, mapUrl(selectedJob.pickupAddressLabel()), true);
        updateLink(deliveryMapLink, mapUrl(selectedJob.deliveryAddressLabel()), true);
    }

    private void clearJobDetail() {
        detailJob.setText("-");
        detailCustomer.setText("-");
        detailCostCenter.setText("-");
        detailStatus.setText("-");
        detailCourier.setText("-");
        detailVehicle.setText("-");
        detailPickup.setText("-");
        detailDelivery.setText("-");
        updateLink(routeLink, "", false);
        updateLink(pickupMapLink, "", false);
        updateLink(deliveryMapLink, "", false);
    }

    private void updateStationDetail() {
        if (selectedStation == null) {
            clearStationDetail();
            return;
        }

        stationJob.setText(String.valueOf(selectedStation.jobId()));
        stationCustomer.setText(selectedStation.customerName());
        stationName.setText(selectedStation.stopName());
        stationStatus.setText(statusLabel(selectedStation.stopStatus(), true));
        stationAddress.setText(selectedStation.addressLabel());
        stationCourier.setText(selectedStation.courierSid().isBlank() ? "-" : selectedStation.courierSid());
        stationRemark.setText(selectedStation.remark().isBlank() ? "-" : selectedStation.remark());
        stationPhone.setText(selectedStation.phone().isBlank() ? "-" : selectedStation.phone());
        updateLink(stationMapLink, selectedStation.mapUrl(), true);
        JobRow job = jobsById.get(selectedStation.jobId());
        updateLink(stationRouteLink,
                job != null ? directionsUrl(job.pickupAddressLabel(), job.deliveryAddressLabel()) : "",
                job != null);
    }

    private void clearStationDetail() {
        stationJob.setText("-");
        stationCustomer.setText("-");
        stationName.setText("-");
        stationStatus.setText("-");
        stationAddress.setText("-");
        stationCourier.setText("-");
        stationRemark.setText("-");
        stationPhone.setText("-");
        updateLink(stationMapLink, "", false);
        updateLink(stationRouteLink, "", false);
    }

    private Anchor mapAnchor(String href, String label) {
        Anchor anchor = createLink(label);
        updateLink(anchor, href, href != null && !href.isBlank());
        return anchor;
    }

    private Anchor createLink(String text) {
        Anchor anchor = new Anchor();
        anchor.setText(text);
        anchor.setTarget("_blank");
        anchor.setVisible(false);
        return anchor;
    }

    private void updateLink(Anchor anchor, String href, boolean visible) {
        if (visible && href != null && !href.isBlank()) {
            anchor.setHref(href);
            anchor.setVisible(true);
        } else {
            anchor.setHref("");
            anchor.setVisible(false);
        }
    }

    private String mapUrl(String address) {
        if (address == null || address.isBlank() || "-".equals(address)) {
            return "";
        }
        return "https://www.google.com/maps/search/?api=1&query="
                + URLEncoder.encode(address, StandardCharsets.UTF_8);
    }

    private String directionsUrl(String origin, String destination) {
        if (origin == null || origin.isBlank() || destination == null || destination.isBlank()
                || "-".equals(origin) || "-".equals(destination)) {
            return "";
        }
        return "https://www.google.com/maps/dir/?api=1&origin="
                + URLEncoder.encode(origin, StandardCharsets.UTF_8)
                + "&destination="
                + URLEncoder.encode(destination, StandardCharsets.UTF_8);
    }

    private String formatDateTime(String value) {
        return value == null || value.isBlank() ? "-" : value.replace("T", " ");
    }

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

    private String statusLabel(Integer status, boolean station) {
        if (station) {
            return Integer.valueOf(1).equals(status) ? "Erledigt" : "Offen";
        }
        return switch (status != null ? status : -1) {
            case 0 -> "Blockiert";
            case 1 -> "Zugewiesen";
            case 2 -> "In Zustellung";
            case 8 -> "Zugestellt";
            case 9 -> "Abgeschlossen";
            case 10 -> "Storniert";
            default -> status != null ? String.valueOf(status) : "-";
        };
    }

    private String vehicleTypeLabel(Integer vehicleTypeId) {
        return vehicleTypeId != null ? "Typ " + vehicleTypeId : "-";
    }

    private String availabilityLabel(String value) {
        return "1".equals(value) ? "Ja" : "Nein";
    }

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

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

    private BigDecimal toBigDecimal(Object value) {
        if (value == null) {
            return null;
        }
        if (value instanceof BigDecimal decimal) {
            return decimal;
        }
        try {
            return new BigDecimal(value.toString());
        } catch (NumberFormatException e) {
            return null;
        }
    }

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

    private record JobRow(Long jobId, Integer status, String orderTime, String customerName, String costCenterName,
                          String pickupName, String pickupStreet, String pickupZipcode, String pickupCity,
                          String deliveryName, String deliveryStreet, String deliveryZipcode, String deliveryCity,
                          Long courierId, String courierSid, Integer vehicleTypeId, Long courierVehicleId,
                          String courierVehicleSid, BigDecimal totalPrice, boolean exported) {

        private String vehicleDisplay() {
            if (courierVehicleSid != null && !courierVehicleSid.isBlank()) {
                return courierVehicleSid;
            }
            if (vehicleTypeId != null) {
                return "Typ " + vehicleTypeId;
            }
            return "-";
        }

        private String pickupAddressLabel() {
            return joinAddress(pickupStreet, pickupZipcode, pickupCity);
        }

        private String deliveryAddressLabel() {
            return joinAddress(deliveryStreet, deliveryZipcode, deliveryCity);
        }

        private String joinAddress(String street, String zipcode, String city) {
            List<String> parts = new ArrayList<>();
            if (street != null && !street.isBlank()) {
                parts.add(street);
            }
            String zipcodeCity = ((zipcode != null ? zipcode : "") + " " + (city != null ? city : "")).trim();
            if (!zipcodeCity.isBlank()) {
                parts.add(zipcodeCity);
            }
            return parts.isEmpty() ? "-" : String.join(", ", parts);
        }
    }

    private record VehicleRow(Long courierId, String courierSid, String courierEid, String available,
                              Long courierVehicleId, String courierVehicleSid, Integer vehicleTypeId,
                              Long assignedJobs) {

        private String vehicleDisplay() {
            if (courierVehicleSid != null && !courierVehicleSid.isBlank()) {
                return courierVehicleSid;
            }
            return "Hauptfahrzeug";
        }
    }

    private record StationRow(Long jobId, String orderTime, Integer stopSort, Integer stopStatus, String customerName,
                              String commissionNo, String stopName, String street, String zipcode, String city,
                              String country, String remark, String phone, String courierSid) {

        private String addressLabel() {
            List<String> parts = new ArrayList<>();
            if (street != null && !street.isBlank()) {
                parts.add(street);
            }
            String zipcodeCity = ((zipcode != null ? zipcode : "") + " " + (city != null ? city : "")).trim();
            if (!zipcodeCity.isBlank()) {
                parts.add(zipcodeCity);
            }
            if (country != null && !country.isBlank()) {
                parts.add(country);
            }
            return parts.isEmpty() ? "-" : String.join(", ", parts);
        }

        private String mapUrl() {
            if (addressLabel().equals("-")) {
                return "";
            }
            return "https://www.google.com/maps/search/?api=1&query="
                    + URLEncoder.encode(addressLabel(), StandardCharsets.UTF_8);
        }
    }
}
