package de.assecutor.votianlt.pages.view;

import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.combobox.ComboBox;
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.Main;
import com.vaadin.flow.component.html.Span;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.notification.NotificationVariant;
import com.vaadin.flow.component.orderedlayout.FlexComponent;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.IntegerField;
import com.vaadin.flow.component.textfield.NumberField;
import com.vaadin.flow.component.textfield.TextArea;
import com.vaadin.flow.router.BeforeEvent;
import com.vaadin.flow.router.HasDynamicTitle;
import com.vaadin.flow.router.HasUrlParameter;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.theme.lumo.LumoUtility;
import de.assecutor.votianlt.model.DeliveryStation;
import de.assecutor.votianlt.model.Job;
import de.assecutor.votianlt.model.JobHistoryType;
import de.assecutor.votianlt.model.JobServiceSelection;
import de.assecutor.votianlt.model.JobStatus;
import de.assecutor.votianlt.model.Service;
import de.assecutor.votianlt.model.User;
import de.assecutor.votianlt.messaging.MessagingPublisher;
import de.assecutor.votianlt.pages.base.ui.component.DialogStylingHelper;
import de.assecutor.votianlt.pages.base.ui.component.ViewToolbar;
import de.assecutor.votianlt.repository.ServiceRepository;
import de.assecutor.votianlt.repository.JobRepository;
import de.assecutor.votianlt.security.SecurityService;
import de.assecutor.votianlt.service.ClientConnectionService;
import de.assecutor.votianlt.service.JobHistoryService;
import jakarta.annotation.security.RolesAllowed;
import lombok.extern.slf4j.Slf4j;
import org.bson.types.ObjectId;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;

@Route(value = "job_manual_complete", layout = de.assecutor.votianlt.pages.base.ui.view.MainLayout.class)
@RolesAllowed("USER")
@Slf4j
public class JobManualCompleteView extends Main implements HasUrlParameter<String>, HasDynamicTitle {

    private static final class ServiceRow {
        private final Service service;
        private final JobServiceSelection selection;

        private ServiceRow(Service service, JobServiceSelection selection) {
            this.service = service;
            this.selection = selection;
        }
    }

    private final JobRepository jobRepository;
    private final JobHistoryService jobHistoryService;
    private final SecurityService securityService;
    private final ServiceRepository serviceRepository;
    private final ClientConnectionService clientConnectionService;
    private final MessagingPublisher messagingPublisher;
    private final VerticalLayout content;

    private Job job;
    private final List<ServiceRow> serviceRows = new ArrayList<>();
    private Grid<ServiceRow> servicesGrid;
    private Span netTotalLabel;
    private Span grossTotalLabel;
    private TextArea remarkArea;
    private BigDecimal vatRate = Service.FIXED_VAT_RATE;
    private Double manualDistanceKm;
    private Integer manualDurationSeconds;

    public JobManualCompleteView(JobRepository jobRepository, JobHistoryService jobHistoryService,
            SecurityService securityService, ServiceRepository serviceRepository,
            ClientConnectionService clientConnectionService, MessagingPublisher messagingPublisher) {
        this.jobRepository = jobRepository;
        this.jobHistoryService = jobHistoryService;
        this.securityService = securityService;
        this.serviceRepository = serviceRepository;
        this.clientConnectionService = clientConnectionService;
        this.messagingPublisher = messagingPublisher;

        setSizeFull();
        addClassNames(LumoUtility.BoxSizing.BORDER, LumoUtility.Display.FLEX, LumoUtility.FlexDirection.COLUMN,
                LumoUtility.Padding.MEDIUM, LumoUtility.Gap.SMALL);
        addClassName("data-view");

        add(new ViewToolbar(getTranslation("jobsummary.dialog.manualcomplete.title")));

        content = new VerticalLayout();
        content.setSpacing(true);
        content.setPadding(true);
        content.setWidthFull();
        content.addClassNames("form-shell", "form-card");
        add(content);
    }

    @Override
    public String getPageTitle() {
        return getTranslation("jobsummary.dialog.manualcomplete.title");
    }

    @Override
    public void setParameter(BeforeEvent event, String parameter) {
        content.removeAll();
        serviceRows.clear();
        job = null;

        if (parameter == null || parameter.isBlank()) {
            content.add(new Span(getTranslation("jobhistory.error.no.id")));
            return;
        }

        ObjectId jobId;
        try {
            jobId = new ObjectId(parameter);
        } catch (Exception e) {
            content.add(new Span(getTranslation("jobhistory.error.invalid.id", parameter)));
            return;
        }

        Job loaded = jobRepository.findById(jobId).orElse(null);
        if (loaded == null) {
            content.add(new Span(getTranslation("jobhistory.error.not.found", parameter)));
            return;
        }

        job = loaded;
        loadVatRate();
        render();
    }

    private void loadVatRate() {
        try {
            User user = securityService.getCurrentDatabaseUser();
            if (user != null && user.getVatRate() != null) {
                vatRate = user.getVatRate();
            }
        } catch (Exception e) {
            log.warn("Could not load user VAT rate, falling back to default: {}", e.getMessage());
        }
    }

    private void render() {
        manualDistanceKm = job.getRouteDistanceKm();
        manualDurationSeconds = job.getRouteDurationSeconds();

        Span warningText = new Span(getTranslation("jobsummary.dialog.manualcomplete.text", job.getJobNumber()));
        warningText.getStyle().set("color", "var(--lumo-error-text-color)");

        TextArea reasonField = new TextArea(getTranslation("jobsummary.dialog.manualcomplete.reason"));
        reasonField.setWidthFull();
        reasonField.setMinHeight("100px");
        reasonField.setRequired(true);

        content.add(warningText, reasonField);

        boolean hasRouteData = manualDistanceKm != null && manualDistanceKm > 0;
        content.add(hasRouteData ? createRouteSection() : createManualRouteSection());

        content.add(createServicesSection());
        content.add(createSummarySection());
        content.add(createRemarkSection());

        HorizontalLayout buttonBar = new HorizontalLayout();
        buttonBar.setWidthFull();
        buttonBar.setJustifyContentMode(FlexComponent.JustifyContentMode.END);
        buttonBar.setSpacing(true);

        Button cancelButton = new Button(getTranslation("jobsummary.dialog.manualcomplete.cancel"),
                e -> getUI().ifPresent(ui -> ui.navigate("job_summary/" + job.getId().toHexString())));

        Button confirmButton = new Button(getTranslation("jobsummary.dialog.manualcomplete.confirm"));
        confirmButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_ERROR);
        confirmButton.addClickListener(e -> confirm(reasonField));

        buttonBar.add(cancelButton, confirmButton);
        content.add(buttonBar);

        loadSelectedServicesFromJob();
    }

    private VerticalLayout createRouteSection() {
        VerticalLayout routeBox = new VerticalLayout();
        routeBox.setPadding(true);
        routeBox.setSpacing(true);
        routeBox.setWidthFull();
        routeBox.getStyle().set("border", "1px solid var(--lumo-primary-color-50pct)");
        routeBox.getStyle().set("border-radius", "var(--lumo-border-radius-m)");
        routeBox.getStyle().set("background-color", "var(--lumo-primary-color-10pct)");
        routeBox.addClassName("route-card");

        H3 routeTitle = new H3(getTranslation("addjob.route.title"));
        routeTitle.getStyle().set("margin", "0");
        routeTitle.getStyle().set("color", "var(--lumo-primary-text-color)");

        HorizontalLayout distanceRow = new HorizontalLayout();
        distanceRow.setWidthFull();
        distanceRow.setJustifyContentMode(FlexComponent.JustifyContentMode.BETWEEN);
        distanceRow.setAlignItems(FlexComponent.Alignment.CENTER);
        Span distanceLabel = new Span(getTranslation("addjob.route.distance") + ":");
        Span distanceValue = new Span(formatDistance(job.getRouteDistanceKm()));
        distanceValue.getStyle().set("font-weight", "bold");
        distanceValue.getStyle().set("font-size", "var(--lumo-font-size-l)");
        distanceValue.getStyle().set("color", "var(--lumo-primary-text-color)");
        distanceRow.add(distanceLabel, distanceValue);

        HorizontalLayout durationRow = new HorizontalLayout();
        durationRow.setWidthFull();
        durationRow.setJustifyContentMode(FlexComponent.JustifyContentMode.BETWEEN);
        durationRow.setAlignItems(FlexComponent.Alignment.CENTER);
        Span durationLabel = new Span(getTranslation("addjob.route.duration") + ":");
        Span durationValue = new Span(formatDuration(job.getRouteDurationSeconds()));
        durationValue.getStyle().set("font-weight", "bold");
        durationValue.getStyle().set("color", "var(--lumo-secondary-text-color)");
        durationRow.add(durationLabel, durationValue);

        routeBox.add(routeTitle, distanceRow, durationRow);
        return routeBox;
    }

    private VerticalLayout createManualRouteSection() {
        VerticalLayout box = new VerticalLayout();
        box.setPadding(true);
        box.setSpacing(true);
        box.setWidthFull();
        box.getStyle().set("border", "1px solid var(--lumo-primary-color-50pct)");
        box.getStyle().set("border-radius", "var(--lumo-border-radius-m)");
        box.getStyle().set("background-color", "var(--lumo-primary-color-10pct)");
        box.addClassName("route-card");

        H3 title = new H3(getTranslation("addjob.route.title"));
        title.getStyle().set("margin", "0");
        title.getStyle().set("color", "var(--lumo-primary-text-color)");

        NumberField distanceField = new NumberField(getTranslation("addjob.route.distance.km"));
        distanceField.setMin(0);
        distanceField.setStep(0.1);
        distanceField.setPlaceholder(getTranslation("addjob.route.distance.placeholder"));
        distanceField.setWidthFull();

        IntegerField hoursField = new IntegerField(getTranslation("jobmanualcomplete.route.hours"));
        hoursField.setMin(0);
        hoursField.setStepButtonsVisible(true);
        hoursField.setWidthFull();

        IntegerField minutesField = new IntegerField(getTranslation("jobmanualcomplete.route.minutes"));
        minutesField.setMin(0);
        minutesField.setMax(59);
        minutesField.setStepButtonsVisible(true);
        minutesField.setWidthFull();

        if (manualDurationSeconds != null && manualDurationSeconds > 0) {
            hoursField.setValue(manualDurationSeconds / 3600);
            minutesField.setValue((manualDurationSeconds % 3600) / 60);
        }

        distanceField.addValueChangeListener(e -> {
            manualDistanceKm = e.getValue();
            servicesGrid.getDataProvider().refreshAll();
            updatePriceSummary();
        });

        Runnable recalcDuration = () -> {
            Integer h = hoursField.getValue();
            Integer m = minutesField.getValue();
            int hours = h != null ? Math.max(0, h) : 0;
            int minutes = m != null ? Math.max(0, Math.min(59, m)) : 0;
            int total = hours * 3600 + minutes * 60;
            manualDurationSeconds = total > 0 ? total : null;
            servicesGrid.getDataProvider().refreshAll();
            updatePriceSummary();
        };
        hoursField.addValueChangeListener(e -> recalcDuration.run());
        minutesField.addValueChangeListener(e -> recalcDuration.run());

        HorizontalLayout durationRow = new HorizontalLayout(hoursField, minutesField);
        durationRow.setWidthFull();
        durationRow.setSpacing(true);
        hoursField.getStyle().set("flex", "1");
        minutesField.getStyle().set("flex", "1");

        Span hint = new Span(getTranslation("jobmanualcomplete.route.manual.hint"));
        hint.getStyle().set("font-size", "var(--lumo-font-size-s)");
        hint.getStyle().set("color", "var(--lumo-secondary-text-color)");
        hint.getStyle().set("font-style", "italic");

        box.add(title, distanceField, durationRow, hint);
        return box;
    }

    private VerticalLayout createServicesSection() {
        VerticalLayout section = new VerticalLayout();
        section.setPadding(false);
        section.setSpacing(true);
        section.setWidthFull();

        H3 title = new H3(getTranslation("addjob.services.title"));
        title.getStyle().set("margin", "0");

        servicesGrid = new Grid<>();
        servicesGrid.setWidthFull();
        servicesGrid.setHeight("250px");
        servicesGrid.setItems(serviceRows);
        servicesGrid.addClassName("data-grid");

        servicesGrid.addColumn(row -> row.service != null ? row.service.getName() : "")
                .setHeader(getTranslation("common.service")).setSortable(true);
        servicesGrid.addColumn(row -> formatDeliveryStationLabel(
                        row.selection != null ? row.selection.getDeliveryStationOrder() : null))
                .setHeader(getTranslation("addjob.services.deliverystation")).setSortable(false);
        servicesGrid.addColumn(row -> formatCalculationBasis(row.service))
                .setHeader(getTranslation("addjob.services.calculation")).setSortable(true);
        servicesGrid.addColumn(this::formatPrice).setHeader(getTranslation("common.price")).setSortable(false);
        servicesGrid.addComponentColumn(row -> {
            if (row.service != null && row.service.isMandatory()) {
                return new Span("");
            }
            Button removeButton = new Button(new Icon(VaadinIcon.TRASH));
            removeButton.addThemeVariants(ButtonVariant.LUMO_ERROR, ButtonVariant.LUMO_TERTIARY,
                    ButtonVariant.LUMO_SMALL);
            removeButton.addClickListener(e -> {
                serviceRows.remove(row);
                servicesGrid.getDataProvider().refreshAll();
                updatePriceSummary();
            });
            return removeButton;
        }).setHeader(getTranslation("common.actions")).setAutoWidth(true).setFlexGrow(0);

        Div gridPanel = new Div(servicesGrid);
        gridPanel.addClassNames("surface-panel", "data-grid-panel");
        gridPanel.setWidthFull();

        Button addButton = new Button(getTranslation("addjob.services.add"), new Icon(VaadinIcon.PLUS));
        addButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        addButton.addClickListener(e -> openAddServiceDialog());

        section.add(title, gridPanel, addButton);
        return section;
    }

    private VerticalLayout createSummarySection() {
        VerticalLayout summary = new VerticalLayout();
        summary.setPadding(true);
        summary.setSpacing(true);
        summary.setWidthFull();
        summary.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
        summary.getStyle().set("border-radius", "var(--lumo-border-radius-m)");
        summary.getStyle().set("background-color", "var(--lumo-contrast-5pct)");
        summary.setDefaultHorizontalComponentAlignment(FlexComponent.Alignment.STRETCH);
        summary.addClassName("summary-card");

        H3 title = new H3(getTranslation("addjob.summary.title"));
        title.getStyle().set("margin", "0");
        summary.add(title);

        Div priceTable = new Div();
        priceTable.getStyle().set("width", "100%");

        Div netRow = new Div();
        netRow.getStyle().set("display", "flex").set("justify-content", "space-between").set("padding", "4px 0");
        Span netLabel = new Span(getTranslation("addjob.summary.net") + ":");
        netLabel.getStyle().set("padding-right", "8px");
        netTotalLabel = new Span("0,00 €");
        netTotalLabel.getStyle().set("font-weight", "bold").set("white-space", "nowrap");
        netRow.add(netLabel, netTotalLabel);

        Div grossRow = new Div();
        grossRow.getStyle().set("display", "flex").set("justify-content", "space-between").set("padding", "4px 0");
        Span grossLabel = new Span(getTranslation("addjob.summary.gross") + ":");
        grossLabel.getStyle().set("padding-right", "8px").set("font-weight", "bold");
        grossTotalLabel = new Span("0,00 €");
        grossTotalLabel.getStyle().set("font-size", "var(--lumo-font-size-l)");
        grossTotalLabel.getStyle().set("font-weight", "bold");
        grossTotalLabel.getStyle().set("color", "var(--lumo-primary-text-color)").set("white-space", "nowrap");
        grossRow.add(grossLabel, grossTotalLabel);

        priceTable.add(netRow, grossRow);
        summary.add(priceTable);
        return summary;
    }

    private VerticalLayout createRemarkSection() {
        VerticalLayout section = new VerticalLayout();
        section.setPadding(false);
        section.setSpacing(true);
        section.setWidthFull();

        H3 title = new H3(getTranslation("addjob.tasks.remark"));
        title.getStyle().set("margin", "0");

        remarkArea = new TextArea();
        remarkArea.setPlaceholder(getTranslation("addjob.tasks.remark.placeholder"));
        remarkArea.setWidthFull();
        remarkArea.setMinHeight("120px");
        if (job.getRemark() != null) {
            remarkArea.setValue(job.getRemark());
        }

        section.add(title, remarkArea);
        return section;
    }

    private void loadSelectedServicesFromJob() {
        serviceRows.clear();

        if (job.getSelectedServices() != null && !job.getSelectedServices().isEmpty()) {
            for (JobServiceSelection selection : job.getSelectedServices()) {
                if (selection.getServiceId() == null) {
                    continue;
                }
                serviceRepository.findById(selection.getServiceId())
                        .ifPresent(service -> serviceRows.add(new ServiceRow(service, selection)));
            }
        } else if (job.getServiceIds() != null && !job.getServiceIds().isEmpty()) {
            for (String serviceId : job.getServiceIds()) {
                serviceRepository.findById(serviceId)
                        .ifPresent(service -> serviceRows.add(new ServiceRow(service, null)));
            }
        }

        servicesGrid.getDataProvider().refreshAll();
        updatePriceSummary();
    }

    private void openAddServiceDialog() {
        Dialog dialog = DialogStylingHelper.createStyledDialog(getTranslation("addjob.services.dialog.title"), "720px");
        dialog.setCloseOnOutsideClick(false);

        VerticalLayout dialogContent = DialogStylingHelper.createContentLayout("620px");

        User currentUser = securityService.getCurrentDatabaseUser();
        List<Service> availableServices = currentUser != null
                ? serviceRepository.findByUserId(currentUser.getId().toString())
                : List.of();

        ComboBox<Service> serviceCombo = new ComboBox<>(getTranslation("common.service"));
        serviceCombo.setWidthFull();
        serviceCombo.setItems(availableServices);
        serviceCombo.setItemLabelGenerator(service -> {
            if (service.getCalculationBasis() == Service.CalculationBasis.FLAT_RATE
                    && service.getEffectivePrice() != null) {
                return service.getName() + " (" + service.getEffectivePrice().setScale(2, RoundingMode.HALF_UP) + " €)";
            }
            return service.getName();
        });
        serviceCombo.setPlaceholder(getTranslation("addjob.services.dialog.placeholder"));
        serviceCombo.setRequired(true);

        List<Integer> stationOrders = availableDeliveryStationOrders();
        ComboBox<Integer> stationCombo = new ComboBox<>(getTranslation("addjob.services.deliverystation"));
        stationCombo.setWidthFull();
        stationCombo.setRequired(true);
        stationCombo.setRequiredIndicatorVisible(true);
        stationCombo.setItems(stationOrders);
        stationCombo.setItemLabelGenerator(this::buildDeliveryStationSelectionLabel);
        stationCombo.setPlaceholder(getTranslation("addjob.services.dialog.station.placeholder"));
        if (!stationOrders.isEmpty()) {
            stationCombo.setValue(0);
        }

        dialogContent.add(serviceCombo, stationCombo);

        Button cancel = new Button(getTranslation("button.cancel"), e -> dialog.close());
        cancel.addThemeVariants(ButtonVariant.LUMO_TERTIARY);

        Button add = new Button(getTranslation("addjob.services.dialog.add"), e -> {
            Service service = serviceCombo.getValue();
            Integer stationOrder = stationCombo.getValue();
            if (service == null || stationOrder == null) {
                return;
            }
            JobServiceSelection selection = new JobServiceSelection();
            selection.setServiceId(service.getId());
            selection.setDeliveryStationOrder(stationOrder);
            selection.setRouteDistanceKm(manualDistanceKm);
            selection.setRouteDurationSeconds(manualDurationSeconds);
            serviceRows.add(new ServiceRow(service, selection));
            servicesGrid.getDataProvider().refreshAll();
            updatePriceSummary();
            dialog.close();
        });
        add.addThemeVariants(ButtonVariant.LUMO_PRIMARY);

        dialog.add(DialogStylingHelper.wrapContent(dialogContent));
        dialog.getFooter().add(cancel, add);
        dialog.open();
    }

    private List<Integer> availableDeliveryStationOrders() {
        List<Integer> orders = new ArrayList<>();
        if (job.getDeliveryStations() != null) {
            for (int i = 0; i < job.getDeliveryStations().size(); i++) {
                orders.add(i);
            }
        }
        return orders;
    }

    private String buildDeliveryStationSelectionLabel(Integer order) {
        if (order == null || job.getDeliveryStations() == null || order < 0
                || order >= job.getDeliveryStations().size()) {
            return "-";
        }
        DeliveryStation station = job.getDeliveryStations().get(order);
        StringBuilder label = new StringBuilder(getTranslation("addjob.station.delivery", order + 1));
        if (station.getCity() != null && !station.getCity().isBlank()) {
            label.append(" - ").append(station.getCity());
        } else if (station.getCompany() != null && !station.getCompany().isBlank()) {
            label.append(" - ").append(station.getCompany());
        }
        return label.toString();
    }

    private String formatDeliveryStationLabel(Integer order) {
        if (order == null || order < 0) {
            return "-";
        }
        return getTranslation("addjob.station.delivery", order + 1);
    }

    private String formatCalculationBasis(Service service) {
        if (service == null || service.getCalculationBasis() == null) {
            return "";
        }
        return switch (service.getCalculationBasis()) {
            case DISTANCE -> getTranslation("addjob.services.basis.distance");
            case TIME -> getTranslation("addjob.services.basis.time");
            case FLAT_RATE -> getTranslation("addjob.services.basis.flatrate");
        };
    }

    private String formatPrice(ServiceRow row) {
        Service service = row.service;
        if (service == null || service.getCalculationBasis() == null) {
            return "";
        }
        BigDecimal price = calculateServicePrice(row);
        if (price != null && price.compareTo(BigDecimal.ZERO) > 0) {
            return price.setScale(2, RoundingMode.HALF_UP) + " €";
        }
        if (service.getCalculationBasis() == Service.CalculationBasis.DISTANCE && service.getPricePerKilometer() != null
                && routeDistanceFor(row.selection) == null) {
            return service.getPricePerKilometer().setScale(2, RoundingMode.HALF_UP) + " €/km ("
                    + getTranslation("addjob.services.route.missing") + ")";
        }
        if (service.getCalculationBasis() == Service.CalculationBasis.TIME && service.getPricePer15Minutes() != null
                && routeDurationFor(row.selection) == null) {
            return service.getPricePer15Minutes().setScale(2, RoundingMode.HALF_UP) + " €/15 Min. ("
                    + getTranslation("addjob.services.route.missing") + ")";
        }
        return service.getEffectivePrice() != null
                ? service.getEffectivePrice().setScale(2, RoundingMode.HALF_UP) + " €"
                : "";
    }

    private BigDecimal calculateServicePrice(ServiceRow row) {
        Service service = row.service;
        if (service == null || service.getCalculationBasis() == null) {
            return BigDecimal.ZERO;
        }
        switch (service.getCalculationBasis()) {
            case FLAT_RATE:
                return service.getPrice() != null ? service.getPrice() : BigDecimal.ZERO;
            case DISTANCE: {
                Double km = routeDistanceFor(row.selection);
                if (service.getPricePerKilometer() != null && km != null && km > 0) {
                    return service.getPricePerKilometer().multiply(BigDecimal.valueOf(km));
                }
                return BigDecimal.ZERO;
            }
            case TIME: {
                Integer seconds = routeDurationFor(row.selection);
                if (service.getPricePer15Minutes() != null && seconds != null && seconds > 0) {
                    int units = seconds / 900;
                    if (seconds % 900 > 0) {
                        units++;
                    }
                    return service.getPricePer15Minutes().multiply(BigDecimal.valueOf(units));
                }
                return BigDecimal.ZERO;
            }
            default:
                return BigDecimal.ZERO;
        }
    }

    private Double routeDistanceFor(JobServiceSelection selection) {
        if (selection != null && selection.getRouteDistanceKm() != null) {
            return selection.getRouteDistanceKm();
        }
        return manualDistanceKm;
    }

    private Integer routeDurationFor(JobServiceSelection selection) {
        if (selection != null && selection.getRouteDurationSeconds() != null) {
            return selection.getRouteDurationSeconds();
        }
        return manualDurationSeconds;
    }

    private void updatePriceSummary() {
        BigDecimal net = BigDecimal.ZERO;
        for (ServiceRow row : serviceRows) {
            net = net.add(calculateServicePrice(row));
        }
        BigDecimal gross = net.add(net.multiply(vatRate));
        netTotalLabel.setText(formatAmount(net));
        grossTotalLabel.setText(formatAmount(gross));
    }

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

    private String formatDistance(Double km) {
        if (km == null) {
            return "-";
        }
        return String.format(Locale.GERMANY, "%.1f km", km);
    }

    private String formatDuration(Integer seconds) {
        if (seconds == null || seconds <= 0) {
            return "-";
        }
        int hours = seconds / 3600;
        int minutes = (seconds % 3600) / 60;
        if (hours > 0) {
            return String.format("%d Std. %d Min.", hours, minutes);
        }
        return String.format("%d Min.", minutes);
    }

    private void confirm(TextArea reasonField) {
        String reason = reasonField.getValue();
        if (reason == null || reason.trim().isEmpty()) {
            reasonField.setInvalid(true);
            reasonField.setErrorMessage(getTranslation("jobsummary.dialog.manualcomplete.reason.required"));
            return;
        }

        try {
            JobStatus oldStatus = job.getStatus();

            List<JobServiceSelection> selections = new ArrayList<>();
            for (ServiceRow row : serviceRows) {
                if (row.service == null) {
                    continue;
                }
                JobServiceSelection selection = row.selection != null ? row.selection : new JobServiceSelection();
                selection.setServiceId(row.service.getId());
                if (selection.getDeliveryStationOrder() == null && row.selection != null) {
                    selection.setDeliveryStationOrder(row.selection.getDeliveryStationOrder());
                }
                selections.add(selection);
            }
            job.setSelectedServices(selections);

            String remark = remarkArea.getValue();
            job.setRemark(remark != null && !remark.isBlank() ? remark.trim() : null);

            if (job.getRouteDistanceKm() == null || job.getRouteDistanceKm() <= 0) {
                job.setRouteDistanceKm(manualDistanceKm);
                job.setRouteDurationSeconds(manualDurationSeconds);
            }

            job.setStatus(JobStatus.COMPLETED);
            job.setUpdatedAt(LocalDateTime.now());
            jobRepository.save(job);

            String currentUser = securityService.getCurrentUsername();
            jobHistoryService.logStatusChange(job, oldStatus, JobStatus.COMPLETED, currentUser);

            String description = String.format("Auftrag manuell beendet von %s. Begründung: %s", currentUser,
                    reason.trim());
            jobHistoryService.logCustomEvent(job.getId(), getTranslation("jobsummary.history.manualcomplete.reason"),
                    description, currentUser, JobHistoryType.STATUS_CHANGE);

            notifyClientJobDeleted(job);

            Notification
                    .show(getTranslation("jobsummary.notification.completed", job.getJobNumber()), 3000,
                            Notification.Position.BOTTOM_END)
                    .addThemeVariants(NotificationVariant.LUMO_SUCCESS);
            getUI().ifPresent(ui -> ui.navigate("job_summary/" + job.getId().toHexString()));
        } catch (Exception ex) {
            Notification
                    .show(getTranslation("jobsummary.notification.complete.error", ex.getMessage()), 5000,
                            Notification.Position.BOTTOM_END)
                    .addThemeVariants(NotificationVariant.LUMO_ERROR);
        }
    }

    private void notifyClientJobDeleted(Job job) {
        if (!job.isDigitalProcessing()) {
            return;
        }

        String appUserId = job.getAppUser();
        if (appUserId == null || appUserId.isBlank()) {
            return;
        }

        if (!clientConnectionService.isClientConnected(appUserId)) {
            log.info("[JOB] Client {} not online, skipping job_deleted notification", appUserId);
            return;
        }

        Map<String, Object> payload = Map.of("type", "job_deleted", "jobId", job.getId().toHexString(), "jobNumber",
                job.getJobNumber() != null ? job.getJobNumber() : "", "deletedAt", LocalDateTime.now().toString());

        log.info("[JOB] Sending job_deleted to {}: {}", appUserId, payload);
        messagingPublisher.publishAsJson(appUserId, "job_deleted", payload);
    }
}
