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.dialog.Dialog;
import com.vaadin.flow.component.grid.Grid;
import com.vaadin.flow.component.html.Div;
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.html.Span;
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.router.PageTitle;
import com.vaadin.flow.router.Route;
import de.votian.web.model.SessionData;
import de.votian.web.service.RestClientService;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;

@Route(value = "longhaul", layout = MainLayout.class)
@PageTitle("Votian - Ferntouren")
public class LonghaulView extends VerticalLayout {

    private final RestClientService restClient;
    private final SessionData sessionData;

    private final Span pendingCount = new Span("-");
    private final Span acceptedCount = new Span("-");
    private final Span returnCount = new Span("-");
    private final Span sourceInfo = new Span("-");
    private final Span modeInfo = new Span("-");
    private final Span thresholdInfo = new Span("-");
    private final Span generatedAtInfo = new Span("-");

    private final Paragraph hint = new Paragraph();
    private final Grid<LonghaulRow> grid = new Grid<>(LonghaulRow.class, false);

    private final Button reloadButton = new Button("Neu laden", VaadinIcon.REFRESH.create());
    private final Button remoteDbToggleButton = new Button();
    private final Button openJobButton = new Button("Auftrag oeffnen", VaadinIcon.EXTERNAL_LINK.create());
    private final Button returnButton = new Button("An Niederlassung zurueckgeben");

    private final Span detailJob = new Span("-");
    private final Span detailStatus = new Span("-");
    private final Span detailLonghaul = new Span("-");
    private final Span detailCustomer = new Span("-");
    private final Span detailCostCenter = new Span("-");
    private final Span detailCommission = new Span("-");
    private final Span detailPickup = new Span("-");
    private final Span detailDelivery = new Span("-");
    private final Span detailCourier = new Span("-");
    private final Span detailVehicle = new Span("-");
    private final Span detailTimes = new Span("-");
    private final Span detailDistance = new Span("-");
    private final Span detailPrice = new Span("-");
    private final Span mapInfo = new Span("-");
    private final Div mapView = new Div();

    private final Tab pendingTab = new Tab("Zu vermitteln");
    private final Tab acceptedTab = new Tab("Angenommen");
    private final Tab returnTab = new Tab("Ruecktouren");
    private final Tabs tabs = new Tabs(pendingTab, acceptedTab, returnTab);

    private final List<LonghaulRow> pendingRows = new ArrayList<>();
    private final List<LonghaulRow> acceptedRows = new ArrayList<>();
    private final List<LonghaulRow> returnRows = new ArrayList<>();

    private LonghaulRow selectedRow;
    private boolean mediationActive;
    private boolean remoteDbAvailable;
    private boolean remoteDbActive;

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

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

        configureGrid();
        configureMapView();
        configureActions();
        clearDetail();

        HorizontalLayout cards = new HorizontalLayout(
                createCard("Zu vermitteln", pendingCount),
                createCard("Angenommen", acceptedCount),
                createCard("Ruecktouren", returnCount),
                createCard("Datenquelle", sourceInfo),
                createCard("Modus", modeInfo),
                createCard("Mindestdistanz", thresholdInfo)
        );
        cards.setWidthFull();

        HorizontalLayout toolbar = new HorizontalLayout(reloadButton, remoteDbToggleButton, openJobButton, returnButton, generatedAtInfo);
        toolbar.setAlignItems(Alignment.CENTER);

        VerticalLayout listColumn = new VerticalLayout(tabs, hint, grid);
        listColumn.setPadding(false);
        listColumn.setSpacing(true);
        listColumn.setSizeFull();
        listColumn.expand(grid);

        VerticalLayout sideColumn = new VerticalLayout(buildMapPanel(), buildDetailPanel());
        sideColumn.setPadding(false);
        sideColumn.setSpacing(true);
        sideColumn.setWidth("520px");
        sideColumn.setFlexShrink(0);

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

        add(new H3("Ferntouren"), cards, toolbar, workspace);
        expand(workspace);

        tabs.addSelectedChangeListener(event -> showCurrentTab(null));
        loadDashboard(null);
    }

    private void configureGrid() {
        grid.addColumn(LonghaulRow::jobId).setHeader("Auftrag").setAutoWidth(true).setFrozen(true);
        grid.addColumn(row -> statusLabel(row.status())).setHeader("Status").setAutoWidth(true);
        grid.addColumn(row -> formatDateTime(row.orderTime())).setHeader("Zeit").setAutoWidth(true);
        grid.addColumn(LonghaulRow::customerName).setHeader("Kunde").setFlexGrow(1);
        grid.addColumn(LonghaulRow::commissionNo).setHeader("Kommission").setAutoWidth(true);
        grid.addColumn(LonghaulRow::pickupLabel).setHeader("Start").setFlexGrow(1);
        grid.addColumn(LonghaulRow::deliveryLabel).setHeader("Ziel").setFlexGrow(1);
        grid.addColumn(row -> formatDistance(row.airDistanceKm())).setHeader("Luftlinie").setAutoWidth(true);
        grid.addColumn(LonghaulRow::courierSid).setHeader("Kurier").setAutoWidth(true);
        grid.addColumn(LonghaulRow::vehicleDisplay).setHeader("Fahrzeug").setAutoWidth(true);
        grid.addColumn(LonghaulRow::longhaulLabel).setHeader("FT").setAutoWidth(true);
        grid.addColumn(row -> formatAmount(row.totalPrice())).setHeader("Preis").setAutoWidth(true);
        grid.setSizeFull();
        grid.asSingleSelect().addValueChangeListener(event -> {
            selectedRow = event.getValue();
            updateDetail();
            updateMap();
            refreshActionState();
        });
    }

    private void configureMapView() {
        mapView.setWidthFull();
        mapView.setHeight("360px");
        mapView.getStyle()
                .set("border", "1px solid var(--lumo-contrast-10pct)")
                .set("border-radius", "var(--lumo-border-radius-m)")
                .set("overflow", "hidden")
                .set("background", "linear-gradient(180deg, #edf6ff 0%, #f7f9fc 100%)");
        mapInfo.getStyle()
                .set("font-size", "var(--lumo-font-size-s)")
                .set("color", "var(--lumo-secondary-text-color)");
    }

    private void configureActions() {
        reloadButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        reloadButton.addClickListener(event -> loadDashboard(selectedRow != null ? selectedRow.jobId() : null));

        remoteDbToggleButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
        remoteDbToggleButton.addClickListener(event -> {
            remoteDbActive = !remoteDbActive;
            loadDashboard(selectedRow != null ? selectedRow.jobId() : null);
        });

        openJobButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY);
        openJobButton.addClickListener(event -> openSelectedJobOrDetail());

        returnButton.addThemeVariants(ButtonVariant.LUMO_ERROR);
        returnButton.addClickListener(event -> returnSelectedJob());
        refreshActionState();
    }

    private VerticalLayout buildDetailPanel() {
        VerticalLayout panel = new VerticalLayout(
                new H4("Details"),
                detailLine("Auftrag", detailJob),
                detailLine("Status", detailStatus),
                detailLine("Ferntour", detailLonghaul),
                detailLine("Kunde", detailCustomer),
                detailLine("Kostenstelle", detailCostCenter),
                detailLine("Kommission", detailCommission),
                detailLine("Start", detailPickup),
                detailLine("Ziel", detailDelivery),
                detailLine("Kurier", detailCourier),
                detailLine("Fahrzeug", detailVehicle),
                detailLine("Zeiten", detailTimes),
                detailLine("Luftlinie", detailDistance),
                detailLine("Preis", detailPrice)
        );
        panel.setPadding(false);
        panel.setSpacing(true);
        return panel;
    }

    private VerticalLayout buildMapPanel() {
        VerticalLayout panel = new VerticalLayout(
                new H4("Karte"),
                mapInfo,
                mapView,
                buildMapLegend()
        );
        panel.setPadding(false);
        panel.setSpacing(true);
        return panel;
    }

    private HorizontalLayout buildMapLegend() {
        HorizontalLayout legend = new HorizontalLayout(
                legendItem("#b42318", "Zu vermitteln"),
                legendItem("#175cd3", "Angenommen"),
                legendItem("#087443", "Ruecktour"),
                legendItem("#b54708", "Man. FT")
        );
        legend.setSpacing(true);
        legend.setAlignItems(Alignment.CENTER);
        legend.getStyle().set("flex-wrap", "wrap");
        return legend;
    }

    private Component legendItem(String color, String label) {
        Span marker = new Span();
        marker.getStyle()
                .set("display", "inline-block")
                .set("width", "12px")
                .set("height", "12px")
                .set("border-radius", "999px")
                .set("background", color)
                .set("box-shadow", "0 0 0 2px rgba(255,255,255,0.85) inset");
        Span text = new Span(label);
        text.getStyle().set("font-size", "var(--lumo-font-size-s)");
        HorizontalLayout item = new HorizontalLayout(marker, text);
        item.setSpacing(true);
        item.setPadding(false);
        item.setAlignItems(Alignment.CENTER);
        return item;
    }

    private HorizontalLayout detailLine(String label, Span value) {
        Span title = new Span(label + ":");
        title.getStyle().set("font-weight", "600").set("min-width", "110px");
        HorizontalLayout line = new HorizontalLayout(title, value);
        line.setWidthFull();
        line.setAlignItems(Alignment.BASELINE);
        return line;
    }

    private Component createCard(String title, Span value) {
        Span heading = new Span(title);
        heading.getStyle().set("font-size", "var(--lumo-font-size-s)");
        value.getStyle().set("font-size", "var(--lumo-font-size-l)").set("font-weight", "700");
        VerticalLayout card = new VerticalLayout(heading, value);
        card.setPadding(true);
        card.setSpacing(false);
        card.getStyle()
                .set("border", "1px solid var(--lumo-contrast-10pct)")
                .set("border-radius", "var(--lumo-border-radius-m)")
                .set("background", "var(--lumo-base-color)");
        return card;
    }

    private void loadDashboard(Long preferredJobId) {
        if (sessionData.getHeadquartersId() == null) {
            return;
        }

        try {
            String path = "/longhaul/dashboard?hqId=" + sessionData.getHeadquartersId();
            if (remoteDbActive) {
                path += "&remoteDb=true";
            }
            Map<String, Object> data = restClient.getMap(path);
            mediationActive = bool(data.get("mediationActive"));
            remoteDbAvailable = bool(data.get("remoteDbAvailable"));
            remoteDbActive = bool(data.get("remoteDbActive"));
            pendingRows.clear();
            pendingRows.addAll(parseRows(data.get("pendingJobs")));
            acceptedRows.clear();
            acceptedRows.addAll(parseRows(data.get("acceptedJobs")));
            returnRows.clear();
            returnRows.addAll(parseRows(data.get("returnJobs")));

            pendingCount.setText(str(data.get("pendingCount"), pendingRows.size()));
            acceptedCount.setText(str(data.get("acceptedCount"), acceptedRows.size()));
            returnCount.setText(str(data.get("returnCount"), returnRows.size()));
            Long effectiveHeadquartersId = toLong(data.get("effectiveHeadquartersId"));
            sourceInfo.setText(remoteDbActive
                    ? "Remote-DB" + (effectiveHeadquartersId != null ? " / HQ " + effectiveHeadquartersId : "")
                    : "Lokale DB");
            modeInfo.setText(mediationActive ? "Vermittlung aktiv" : "Nur Sichtmodus");
            thresholdInfo.setText(str(data.get("minimumDistanceKm"), 80) + " km");
            generatedAtInfo.setText("Stand: " + formatDateTime(str(data.get("generatedAt"))));

            String sourceHint = remoteDbActive
                    ? "Remote-DB aktiv. Die angezeigten Ferntouren stammen aus der verbundenen Fremdinstanz. "
                    : "";
            String detailHint = remoteDbActive
                    ? " Der lokale Auftragseditor bleibt fuer diese Fremddaten gesperrt; Details und letzter Kurierstandort stehen read-only direkt hier zur Verfuegung."
                    : "";
            hint.setText(sourceHint + (mediationActive
                    ? "Aktive Ferntour-Vermittlung: automatische und manuelle FT-Auftraege koennen hier disponiert und automatische FT-Auftraege an die Niederlassung zurueckgegeben werden."
                    : "Sichtmodus gemaess Distanzkriterium. Rueckgabe an die Niederlassung ist in diesem Modus deaktiviert.") + detailHint);

            showCurrentTab(preferredJobId);
        } catch (Exception e) {
            Notification.show("Ferntouren konnten nicht geladen werden: " + e.getMessage(), 5000, Notification.Position.MIDDLE)
                    .addThemeVariants(NotificationVariant.LUMO_ERROR);
        }
    }

    private void openSelectedJobOrDetail() {
        if (selectedRow == null) {
            return;
        }
        if (!remoteDbActive && sessionData.canViewJobs()) {
            getUI().ifPresent(ui -> ui.navigate("jobs/" + selectedRow.jobId()));
            return;
        }
        openDetailDialog();
    }

    private void openDetailDialog() {
        if (selectedRow == null) {
            return;
        }
        try {
            String path = "/longhaul/jobs/" + selectedRow.jobId() + "/detail?hqId=" + sessionData.getHeadquartersId();
            if (remoteDbActive) {
                path += "&remoteDb=true";
            }
            LonghaulDetail detail = parseDetail(restClient.getMap(path));

            Dialog dialog = new Dialog();
            dialog.setHeaderTitle("Auftrag " + detail.jobId() + (detail.remoteDbActive()
                    ? " / Remote-DB" + (detail.effectiveHeadquartersId() != null ? " / HQ " + detail.effectiveHeadquartersId() : "")
                    : ""));
            dialog.setWidth("760px");
            dialog.setMaxWidth("95vw");

            VerticalLayout content = new VerticalLayout(
                    buildDialogSection("Allgemein",
                            detailValueLine("Status", statusLabel(detail.status())),
                            detailValueLine("Ferntour", detail.longhaulLabel()),
                            detailValueLine("Tour", orDash(detail.tourName())),
                            detailValueLine("Kunde", orDash(detail.customerName())),
                            detailValueLine("Kostenstelle", orDash(detail.costCenterName())),
                            detailValueLine("Kommission", orDash(detail.commissionNo())),
                            detailValueLine("Kurier", orDash(detail.courierSid())),
                            detailValueLine("Fahrzeug", orDash(detail.vehicleDisplay())),
                            detailValueLine("Zeiten", orDash(formatTimes(detail))),
                            detailValueLine("Luftlinie", orDash(formatDistance(detail.airDistanceKm()))),
                            detailValueLine("Preis", orDash(formatAmount(detail.totalPrice())))
                    ),
                    buildDialogSection("Abholung",
                            detailValueLine("Name", orDash(detail.pickupName())),
                            detailValueLine("Person", orDash(detail.pickupPerson())),
                            detailValueLine("Telefon", orDash(detail.pickupPhone())),
                            detailValueLine("Adresse", orDash(detail.pickupAddress())),
                            detailValueLine("Hinweis", orDash(detail.pickupRemark()))
                    ),
                    buildDialogSection("Zustellung",
                            detailValueLine("Name", orDash(detail.deliveryName())),
                            detailValueLine("Person", orDash(detail.deliveryPerson())),
                            detailValueLine("Telefon", orDash(detail.deliveryPhone())),
                            detailValueLine("Adresse", orDash(detail.deliveryAddress())),
                            detailValueLine("Hinweis", orDash(detail.deliveryRemark()))
                    ),
                    buildDialogSection("Locating",
                            detailValueLine("Letzte Ortung", orDash(formatDateTime(detail.locatingTime()))),
                            detailValueLine("Ortungstyp", orDash(detail.locatingTypeLabel())),
                            detailValueLine("Freimeldung", orDash(detail.locatingZipcode())),
                            detailValueLine("Verfuegbar seit", orDash(formatDateTime(detail.availableTime()))),
                            detailValueLine("Koordinaten", orDash(detail.locatingCoordinates()))
                    )
            );
            content.setPadding(false);
            content.setSpacing(true);
            content.setWidth("700px");

            Button closeButton = new Button("Schliessen", event -> dialog.close());
            dialog.getFooter().add(closeButton);
            dialog.add(content);
            dialog.open();
        } catch (Exception e) {
            Notification.show("Details konnten nicht geladen werden: " + e.getMessage(), 5000, Notification.Position.MIDDLE)
                    .addThemeVariants(NotificationVariant.LUMO_ERROR);
        }
    }

    private void showCurrentTab(Long preferredJobId) {
        List<LonghaulRow> rows = currentRows();
        grid.setItems(rows);

        LonghaulRow rowToSelect = null;
        if (preferredJobId != null) {
            rowToSelect = rows.stream().filter(row -> preferredJobId.equals(row.jobId())).findFirst().orElse(null);
        }
        if (rowToSelect == null && selectedRow != null) {
            Long selectedJobId = selectedRow.jobId();
            rowToSelect = rows.stream().filter(row -> selectedJobId.equals(row.jobId())).findFirst().orElse(null);
        }

        selectedRow = rowToSelect;
        grid.asSingleSelect().setValue(rowToSelect);
        updateDetail();
        updateMap();
        refreshActionState();
    }

    private List<LonghaulRow> currentRows() {
        if (tabs.getSelectedTab() == acceptedTab) {
            return acceptedRows;
        }
        if (tabs.getSelectedTab() == returnTab) {
            return returnRows;
        }
        return pendingRows;
    }

    private void returnSelectedJob() {
        if (selectedRow == null || !selectedRow.returnable()) {
            return;
        }

        try {
            String path = "/longhaul/jobs/" + selectedRow.jobId() + "/return?hqId=" + sessionData.getHeadquartersId();
            if (remoteDbActive) {
                path += "&remoteDb=true";
            }
            restClient.put(path, Map.of());
            Notification.show("Auftrag wurde an die Niederlassung zurueckgegeben.", 3000, Notification.Position.MIDDLE)
                    .addThemeVariants(NotificationVariant.LUMO_SUCCESS);
            loadDashboard(selectedRow.jobId());
        } catch (Exception e) {
            Notification.show("Rueckgabe fehlgeschlagen: " + e.getMessage(), 5000, Notification.Position.MIDDLE)
                    .addThemeVariants(NotificationVariant.LUMO_ERROR);
        }
    }

    private List<LonghaulRow> parseRows(Object rawValue) {
        if (!(rawValue instanceof List<?> rawList)) {
            return List.of();
        }

        List<LonghaulRow> rows = new ArrayList<>();
        for (Object entry : rawList) {
            if (!(entry instanceof Map<?, ?> rawMap)) {
                continue;
            }
            rows.add(new LonghaulRow(
                    toLong(rawMap.get("jobId")),
                    toInteger(rawMap.get("status")),
                    toInteger(rawMap.get("longhaulState")),
                    str(rawMap.get("orderTime")),
                    str(rawMap.get("takeTime")),
                    str(rawMap.get("finishTime")),
                    str(rawMap.get("customerName")),
                    str(rawMap.get("costCenterName")),
                    str(rawMap.get("commissionNo")),
                    str(rawMap.get("pickupName")),
                    str(rawMap.get("pickupZipcode")),
                    str(rawMap.get("pickupCity")),
                    toDouble(rawMap.get("pickupLatitude")),
                    toDouble(rawMap.get("pickupLongitude")),
                    str(rawMap.get("deliveryName")),
                    str(rawMap.get("deliveryZipcode")),
                    str(rawMap.get("deliveryCity")),
                    toDouble(rawMap.get("deliveryLatitude")),
                    toDouble(rawMap.get("deliveryLongitude")),
                    str(rawMap.get("courierSid")),
                    str(rawMap.get("courierVehicleSid")),
                    toInteger(rawMap.get("vehicleTypeId")),
                    str(rawMap.get("vehicleTypeName")),
                    toBigDecimal(rawMap.get("totalPrice")),
                    toDouble(rawMap.get("airDistanceKm")),
                    bool(rawMap.get("manualLonghaul")),
                    bool(rawMap.get("returnable"))
            ));
        }
        return rows;
    }

    private void refreshActionState() {
        boolean selected = selectedRow != null;
        remoteDbToggleButton.setVisible(remoteDbAvailable);
        remoteDbToggleButton.setEnabled(remoteDbAvailable);
        remoteDbToggleButton.setText(remoteDbActive ? "Lokale DB laden" : "Remote-DB laden");
        openJobButton.setVisible(sessionData.canViewLonghaul());
        openJobButton.setEnabled(selected);
        openJobButton.setText(remoteDbActive || !sessionData.canViewJobs() ? "Details anzeigen" : "Auftrag oeffnen");
        returnButton.setVisible(sessionData.canManageLonghaul());
        returnButton.setEnabled(selected
                && tabs.getSelectedTab() == pendingTab
                && mediationActive
                && selectedRow.returnable());
    }

    private void updateDetail() {
        if (selectedRow == null) {
            clearDetail();
            return;
        }

        detailJob.setText(str(selectedRow.jobId()));
        detailStatus.setText(statusLabel(selectedRow.status()));
        detailLonghaul.setText(selectedRow.longhaulLabel());
        detailCustomer.setText(selectedRow.customerName());
        detailCostCenter.setText(selectedRow.costCenterName());
        detailCommission.setText(selectedRow.commissionNo());
        detailPickup.setText(selectedRow.pickupLabel());
        detailDelivery.setText(selectedRow.deliveryLabel());
        detailCourier.setText(selectedRow.courierSid().isBlank() ? "-" : selectedRow.courierSid());
        detailVehicle.setText(selectedRow.vehicleDisplay());
        detailTimes.setText(formatTimes(selectedRow));
        detailDistance.setText(formatDistance(selectedRow.airDistanceKm()));
        detailPrice.setText(formatAmount(selectedRow.totalPrice()));
    }

    private void clearDetail() {
        detailJob.setText("-");
        detailStatus.setText("-");
        detailLonghaul.setText("-");
        detailCustomer.setText("-");
        detailCostCenter.setText("-");
        detailCommission.setText("-");
        detailPickup.setText("-");
        detailDelivery.setText("-");
        detailCourier.setText("-");
        detailVehicle.setText("-");
        detailTimes.setText("-");
        detailDistance.setText("-");
        detailPrice.setText("-");
    }

    private String formatTimes(LonghaulRow row) {
        List<String> values = new ArrayList<>();
        if (!row.orderTime().isBlank()) {
            values.add("Auftrag " + formatDateTime(row.orderTime()));
        }
        if (!row.takeTime().isBlank()) {
            values.add("Uebernommen " + formatDateTime(row.takeTime()));
        }
        if (!row.finishTime().isBlank()) {
            values.add("Abschluss " + formatDateTime(row.finishTime()));
        }
        return values.isEmpty() ? "-" : String.join(" / ", values);
    }

    private String formatTimes(LonghaulDetail detail) {
        List<String> values = new ArrayList<>();
        if (!detail.orderTime().isBlank()) {
            values.add("Auftrag " + formatDateTime(detail.orderTime()));
        }
        if (!detail.takeTime().isBlank()) {
            values.add("Uebernommen " + formatDateTime(detail.takeTime()));
        }
        if (!detail.finishTime().isBlank()) {
            values.add("Abschluss " + formatDateTime(detail.finishTime()));
        }
        return values.isEmpty() ? "" : String.join(" / ", values);
    }

    private String statusLabel(Integer status) {
        if (status == null) {
            return "";
        }
        return switch (status) {
            case 0 -> "Blockiert";
            case 1 -> "Angenommen";
            case 2 -> "Ruecktour";
            case 8 -> "Zugestellt";
            case 9 -> "Zu vermitteln";
            case 10 -> "Storniert";
            default -> String.valueOf(status);
        };
    }

    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 formatDistance(Double value) {
        return value != null ? String.format(Locale.GERMAN, "%.1f km", value) : "";
    }

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

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

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

    private Double toDouble(Object value) {
        if (value instanceof Number number) {
            return number.doubleValue();
        }
        try {
            return value != null ? Double.parseDouble(value.toString()) : null;
        } catch (NumberFormatException e) {
            return null;
        }
    }

    private boolean bool(Object value) {
        return Boolean.TRUE.equals(value) || "true".equalsIgnoreCase(str(value));
    }

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

    private String str(Object value, Object fallback) {
        String text = str(value);
        return text.isBlank() ? String.valueOf(fallback) : text;
    }

    private String orDash(String value) {
        return value == null || value.isBlank() ? "-" : value;
    }

    private Component buildDialogSection(String title, Component... components) {
        VerticalLayout section = new VerticalLayout();
        section.setPadding(false);
        section.setSpacing(false);
        section.add(new H4(title));
        for (Component component : components) {
            section.add(component);
        }
        return section;
    }

    private HorizontalLayout detailValueLine(String label, String value) {
        return detailLine(label, new Span(value));
    }

    private LonghaulDetail parseDetail(Map<String, Object> rawMap) {
        return new LonghaulDetail(
                toLong(rawMap.get("jobId")),
                toInteger(rawMap.get("status")),
                toInteger(rawMap.get("longhaulState")),
                str(rawMap.get("orderTime")),
                str(rawMap.get("takeTime")),
                str(rawMap.get("finishTime")),
                str(rawMap.get("tourName")),
                str(rawMap.get("customerName")),
                str(rawMap.get("costCenterName")),
                str(rawMap.get("commissionNo")),
                str(rawMap.get("pickupName")),
                str(rawMap.get("pickupPerson")),
                str(rawMap.get("pickupPhone")),
                str(rawMap.get("pickupRemark")),
                str(rawMap.get("pickupStreet")),
                str(rawMap.get("pickupZipcode")),
                str(rawMap.get("pickupCity")),
                str(rawMap.get("pickupCountry")),
                toDouble(rawMap.get("pickupLatitude")),
                toDouble(rawMap.get("pickupLongitude")),
                str(rawMap.get("deliveryName")),
                str(rawMap.get("deliveryPerson")),
                str(rawMap.get("deliveryPhone")),
                str(rawMap.get("deliveryRemark")),
                str(rawMap.get("deliveryStreet")),
                str(rawMap.get("deliveryZipcode")),
                str(rawMap.get("deliveryCity")),
                str(rawMap.get("deliveryCountry")),
                toDouble(rawMap.get("deliveryLatitude")),
                toDouble(rawMap.get("deliveryLongitude")),
                str(rawMap.get("courierSid")),
                str(rawMap.get("courierVehicleSid")),
                str(rawMap.get("vehicleTypeName")),
                toBigDecimal(rawMap.get("totalPrice")),
                toDouble(rawMap.get("airDistanceKm")),
                bool(rawMap.get("manualLonghaul")),
                toDouble(rawMap.get("locatingLatitude")),
                toDouble(rawMap.get("locatingLongitude")),
                str(rawMap.get("locatingTime")),
                str(rawMap.get("locatingTypeLabel")),
                str(rawMap.get("locatingZipcode")),
                str(rawMap.get("availableTime")),
                bool(rawMap.get("remoteDbActive")),
                toLong(rawMap.get("effectiveHeadquartersId"))
        );
    }

    private void updateMap() {
        List<LonghaulRow> rows = allRows();
        long mappedRows = rows.stream().filter(LonghaulRow::hasMapRoute).count();
        long unmappedRows = rows.size() - mappedRows;
        if (rows.isEmpty()) {
            mapInfo.setText("Keine Ferntouren im aktuellen Dashboard.");
            mapView.getElement().setProperty("innerHTML", buildEmptyMapSvg("Keine Ferntouren"));
            return;
        }

        String infoText = mappedRows + " Touren in der Kartenansicht";
        if (unmappedRows > 0) {
            infoText += ", " + unmappedRows + " ohne Geokoordinaten";
        }
        if (selectedRow != null && !selectedRow.hasMapRoute()) {
            infoText += ". Der ausgewaehlte Auftrag ist nur in der Liste sichtbar.";
        }
        mapInfo.setText(infoText);
        mapView.getElement().setProperty("innerHTML", buildMapSvg(rows));
    }

    private List<LonghaulRow> allRows() {
        List<LonghaulRow> rows = new ArrayList<>(pendingRows.size() + acceptedRows.size() + returnRows.size());
        rows.addAll(pendingRows);
        rows.addAll(acceptedRows);
        rows.addAll(returnRows);
        return rows;
    }

    private String buildMapSvg(List<LonghaulRow> rows) {
        List<LonghaulRow> mappedRows = rows.stream().filter(LonghaulRow::hasMapRoute).toList();
        if (mappedRows.isEmpty()) {
            return buildEmptyMapSvg("Keine geocodierten Touren");
        }

        double minLongitude = mappedRows.stream()
                .flatMapToDouble(row -> java.util.stream.DoubleStream.of(row.pickupLongitude(), row.deliveryLongitude()))
                .min()
                .orElse(5.5d);
        double maxLongitude = mappedRows.stream()
                .flatMapToDouble(row -> java.util.stream.DoubleStream.of(row.pickupLongitude(), row.deliveryLongitude()))
                .max()
                .orElse(15.5d);
        double minLatitude = mappedRows.stream()
                .flatMapToDouble(row -> java.util.stream.DoubleStream.of(row.pickupLatitude(), row.deliveryLatitude()))
                .min()
                .orElse(47.0d);
        double maxLatitude = mappedRows.stream()
                .flatMapToDouble(row -> java.util.stream.DoubleStream.of(row.pickupLatitude(), row.deliveryLatitude()))
                .max()
                .orElse(55.0d);

        double longitudeSpan = Math.max(maxLongitude - minLongitude, 4.0d);
        double latitudeSpan = Math.max(maxLatitude - minLatitude, 3.0d);
        double longitudePadding = longitudeSpan * 0.12d;
        double latitudePadding = latitudeSpan * 0.12d;
        minLongitude -= longitudePadding;
        maxLongitude += longitudePadding;
        minLatitude -= latitudePadding;
        maxLatitude += latitudePadding;

        int width = 520;
        int height = 360;
        int padding = 26;
        StringBuilder svg = new StringBuilder();
        svg.append("<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 ").append(width).append(' ').append(height).append("'>");
        svg.append("<defs>");
        svg.append("<linearGradient id='bg' x1='0' y1='0' x2='0' y2='1'>")
                .append("<stop offset='0%' stop-color='#edf6ff'/>")
                .append("<stop offset='100%' stop-color='#f7f9fc'/>")
                .append("</linearGradient>");
        svg.append("</defs>");
        svg.append("<rect width='").append(width).append("' height='").append(height).append("' fill='url(#bg)'/>");
        svg.append("<rect x='").append(padding).append("' y='").append(padding).append("' width='")
                .append(width - (padding * 2)).append("' height='").append(height - (padding * 2))
                .append("' rx='18' fill='#ffffff' fill-opacity='0.55' stroke='#0f172a' stroke-opacity='0.10'/>");

        for (int step = 1; step < 5; step++) {
            double verticalX = padding + ((width - (padding * 2)) / 5.0d) * step;
            double horizontalY = padding + ((height - (padding * 2)) / 5.0d) * step;
            svg.append("<line x1='").append(roundSvg(verticalX)).append("' y1='").append(padding)
                    .append("' x2='").append(roundSvg(verticalX)).append("' y2='").append(height - padding)
                    .append("' stroke='#0f172a' stroke-opacity='0.08' stroke-dasharray='4 8'/>");
            svg.append("<line x1='").append(padding).append("' y1='").append(roundSvg(horizontalY))
                    .append("' x2='").append(width - padding).append("' y2='").append(roundSvg(horizontalY))
                    .append("' stroke='#0f172a' stroke-opacity='0.08' stroke-dasharray='4 8'/>");
        }

        svg.append("<text x='").append(width - 40).append("' y='").append(padding + 16)
                .append("' fill='#344054' font-size='12' font-family='Arial, sans-serif'>N</text>");

        for (LonghaulRow row : mappedRows) {
            appendRoute(svg, row, minLongitude, maxLongitude, minLatitude, maxLatitude, width, height, padding);
        }

        if (selectedRow != null && selectedRow.hasMapRoute()) {
            appendSelectedLabel(svg, selectedRow, minLongitude, maxLongitude, minLatitude, maxLatitude, width, height, padding);
        }

        svg.append("</svg>");
        return svg.toString();
    }

    private void appendRoute(StringBuilder svg,
                             LonghaulRow row,
                             double minLongitude,
                             double maxLongitude,
                             double minLatitude,
                             double maxLatitude,
                             int width,
                             int height,
                             int padding) {
        double x1 = projectLongitude(row.pickupLongitude(), minLongitude, maxLongitude, width, padding);
        double y1 = projectLatitude(row.pickupLatitude(), minLatitude, maxLatitude, height, padding);
        double x2 = projectLongitude(row.deliveryLongitude(), minLongitude, maxLongitude, width, padding);
        double y2 = projectLatitude(row.deliveryLatitude(), minLatitude, maxLatitude, height, padding);
        String color = routeColor(row);
        String dash = row.manualLonghaul() ? " stroke-dasharray='8 6'" : "";
        String tooltip = escapeSvgText("Auftrag " + row.jobId() + " | " + row.pickupLabel() + " -> " + row.deliveryLabel());

        if (selectedRow != null && row.jobId().equals(selectedRow.jobId())) {
            svg.append("<line x1='").append(roundSvg(x1)).append("' y1='").append(roundSvg(y1))
                    .append("' x2='").append(roundSvg(x2)).append("' y2='").append(roundSvg(y2))
                    .append("' stroke='#101828' stroke-opacity='0.22' stroke-width='8' stroke-linecap='round'/>");
        }

        svg.append("<line x1='").append(roundSvg(x1)).append("' y1='").append(roundSvg(y1))
                .append("' x2='").append(roundSvg(x2)).append("' y2='").append(roundSvg(y2))
                .append("' stroke='").append(color).append("' stroke-width='")
                .append(selectedRow != null && row.jobId().equals(selectedRow.jobId()) ? "4.5" : "3")
                .append("' stroke-linecap='round' stroke-opacity='0.90'").append(dash).append(">");
        svg.append("<title>").append(tooltip).append("</title></line>");

        svg.append("<circle cx='").append(roundSvg(x1)).append("' cy='").append(roundSvg(y1))
                .append("' r='").append(selectedRow != null && row.jobId().equals(selectedRow.jobId()) ? "5.5" : "4")
                .append("' fill='#ffffff' stroke='").append(color).append("' stroke-width='2'/>");
        svg.append("<circle cx='").append(roundSvg(x2)).append("' cy='").append(roundSvg(y2))
                .append("' r='").append(selectedRow != null && row.jobId().equals(selectedRow.jobId()) ? "5.5" : "4.5")
                .append("' fill='").append(color).append("' stroke='#ffffff' stroke-width='1.5'/>");
    }

    private void appendSelectedLabel(StringBuilder svg,
                                     LonghaulRow row,
                                     double minLongitude,
                                     double maxLongitude,
                                     double minLatitude,
                                     double maxLatitude,
                                     int width,
                                     int height,
                                     int padding) {
        double x1 = projectLongitude(row.pickupLongitude(), minLongitude, maxLongitude, width, padding);
        double y1 = projectLatitude(row.pickupLatitude(), minLatitude, maxLatitude, height, padding);
        double x2 = projectLongitude(row.deliveryLongitude(), minLongitude, maxLongitude, width, padding);
        double y2 = projectLatitude(row.deliveryLatitude(), minLatitude, maxLatitude, height, padding);
        double labelX = Math.min(Math.max(((x1 + x2) / 2.0d) + 14.0d, padding + 8.0d), width - 150.0d);
        double labelY = Math.min(Math.max(((y1 + y2) / 2.0d) - 10.0d, padding + 18.0d), height - 18.0d);
        String text = escapeSvgText("Auftrag " + row.jobId() + " | " + formatDistance(row.airDistanceKm()));
        svg.append("<rect x='").append(roundSvg(labelX - 8.0d)).append("' y='").append(roundSvg(labelY - 14.0d))
                .append("' width='138' height='22' rx='11' fill='#ffffff' fill-opacity='0.92' stroke='#101828' stroke-opacity='0.12'/>");
        svg.append("<text x='").append(roundSvg(labelX)).append("' y='").append(roundSvg(labelY + 1.0d))
                .append("' fill='#101828' font-size='12' font-family='Arial, sans-serif'>")
                .append(text)
                .append("</text>");
    }

    private String buildEmptyMapSvg(String message) {
        return "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 520 360'>"
                + "<rect width='520' height='360' fill='#f7f9fc'/>"
                + "<rect x='26' y='26' width='468' height='308' rx='18' fill='#ffffff' stroke='#0f172a' stroke-opacity='0.10'/>"
                + "<text x='260' y='180' text-anchor='middle' fill='#475467' font-size='16' font-family='Arial, sans-serif'>"
                + escapeSvgText(message)
                + "</text></svg>";
    }

    private String routeColor(LonghaulRow row) {
        if (row.manualLonghaul()) {
            return "#b54708";
        }
        if (Integer.valueOf(2).equals(row.status())) {
            return "#087443";
        }
        if (Integer.valueOf(1).equals(row.status())) {
            return "#175cd3";
        }
        return "#b42318";
    }

    private double projectLongitude(Double longitude, double minLongitude, double maxLongitude, int width, int padding) {
        double usableWidth = width - (padding * 2.0d);
        return padding + ((longitude - minLongitude) / (maxLongitude - minLongitude)) * usableWidth;
    }

    private double projectLatitude(Double latitude, double minLatitude, double maxLatitude, int height, int padding) {
        double usableHeight = height - (padding * 2.0d);
        return padding + (1.0d - ((latitude - minLatitude) / (maxLatitude - minLatitude))) * usableHeight;
    }

    private String roundSvg(double value) {
        return String.format(Locale.ROOT, "%.2f", value);
    }

    private String escapeSvgText(String value) {
        return value
                .replace("&", "&amp;")
                .replace("<", "&lt;")
                .replace(">", "&gt;")
                .replace("\"", "&quot;")
                .replace("'", "&apos;");
    }

    private record LonghaulRow(Long jobId,
                               Integer status,
                               Integer longhaulState,
                               String orderTime,
                               String takeTime,
                               String finishTime,
                               String customerName,
                               String costCenterName,
                               String commissionNo,
                               String pickupName,
                               String pickupZipcode,
                               String pickupCity,
                               Double pickupLatitude,
                               Double pickupLongitude,
                               String deliveryName,
                               String deliveryZipcode,
                               String deliveryCity,
                               Double deliveryLatitude,
                               Double deliveryLongitude,
                               String courierSid,
                               String courierVehicleSid,
                               Integer vehicleTypeId,
                               String vehicleTypeName,
                               BigDecimal totalPrice,
                               Double airDistanceKm,
                               boolean manualLonghaul,
                               boolean returnable) {

        private String pickupLabel() {
            return joinStop(pickupName, pickupZipcode, pickupCity);
        }

        private String deliveryLabel() {
            return joinStop(deliveryName, deliveryZipcode, deliveryCity);
        }

        private String vehicleDisplay() {
            if (!courierVehicleSid.isBlank() && !vehicleTypeName.isBlank()) {
                return courierVehicleSid + " / " + vehicleTypeName;
            }
            if (!courierVehicleSid.isBlank()) {
                return courierVehicleSid;
            }
            return vehicleTypeName;
        }

        private String longhaulLabel() {
            if (manualLonghaul) {
                return "Man. FT";
            }
            if (Integer.valueOf(1).equals(longhaulState)) {
                return "FT";
            }
            if (Integer.valueOf(2).equals(longhaulState)) {
                return "Rueckgabe";
            }
            if (longhaulState != null && longhaulState > 0) {
                return "FT";
            }
            return "";
        }

        private boolean hasMapRoute() {
            return pickupLatitude != null
                    && pickupLongitude != null
                    && deliveryLatitude != null
                    && deliveryLongitude != null;
        }

        private static String joinStop(String name, String zipcode, String city) {
            StringBuilder builder = new StringBuilder();
            if (name != null && !name.isBlank()) {
                builder.append(name);
            }
            String location = ((zipcode != null ? zipcode : "") + " " + (city != null ? city : "")).trim();
            if (!location.isBlank()) {
                if (builder.length() > 0) {
                    builder.append(" | ");
                }
                builder.append(location);
            }
            return builder.length() > 0 ? builder.toString() : "-";
        }
    }

    private record LonghaulDetail(Long jobId,
                                  Integer status,
                                  Integer longhaulState,
                                  String orderTime,
                                  String takeTime,
                                  String finishTime,
                                  String tourName,
                                  String customerName,
                                  String costCenterName,
                                  String commissionNo,
                                  String pickupName,
                                  String pickupPerson,
                                  String pickupPhone,
                                  String pickupRemark,
                                  String pickupStreet,
                                  String pickupZipcode,
                                  String pickupCity,
                                  String pickupCountry,
                                  Double pickupLatitude,
                                  Double pickupLongitude,
                                  String deliveryName,
                                  String deliveryPerson,
                                  String deliveryPhone,
                                  String deliveryRemark,
                                  String deliveryStreet,
                                  String deliveryZipcode,
                                  String deliveryCity,
                                  String deliveryCountry,
                                  Double deliveryLatitude,
                                  Double deliveryLongitude,
                                  String courierSid,
                                  String courierVehicleSid,
                                  String vehicleTypeName,
                                  BigDecimal totalPrice,
                                  Double airDistanceKm,
                                  boolean manualLonghaul,
                                  Double locatingLatitude,
                                  Double locatingLongitude,
                                  String locatingTime,
                                  String locatingTypeLabel,
                                  String locatingZipcode,
                                  String availableTime,
                                  boolean remoteDbActive,
                                  Long effectiveHeadquartersId) {

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

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

        private String vehicleDisplay() {
            if (!courierVehicleSid.isBlank() && !vehicleTypeName.isBlank()) {
                return courierVehicleSid + " / " + vehicleTypeName;
            }
            if (!courierVehicleSid.isBlank()) {
                return courierVehicleSid;
            }
            return vehicleTypeName;
        }

        private String longhaulLabel() {
            if (manualLonghaul) {
                return "Man. FT";
            }
            if (Integer.valueOf(1).equals(longhaulState)) {
                return "FT";
            }
            if (Integer.valueOf(2).equals(longhaulState)) {
                return "Rueckgabe";
            }
            if (longhaulState != null && longhaulState > 0) {
                return "FT";
            }
            return "";
        }

        private String locatingCoordinates() {
            if (locatingLatitude == null || locatingLongitude == null) {
                return "";
            }
            return String.format(Locale.GERMAN, "%.5f / %.5f", locatingLatitude, locatingLongitude);
        }

        private static String joinAddress(String street, String zipcode, String city, String country) {
            StringBuilder builder = new StringBuilder();
            if (street != null && !street.isBlank()) {
                builder.append(street);
            }
            String location = ((zipcode != null ? zipcode : "") + " " + (city != null ? city : "")).trim();
            if (!location.isBlank()) {
                if (builder.length() > 0) {
                    builder.append(", ");
                }
                builder.append(location);
            }
            if (country != null && !country.isBlank()) {
                if (builder.length() > 0) {
                    builder.append(", ");
                }
                builder.append(country);
            }
            return builder.toString();
        }
    }
}
