package de.assecutor.votianlt.pages.view;

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.html.Div;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.dialog.Dialog;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.html.H4;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.notification.NotificationVariant;
import com.vaadin.flow.component.icon.Icon;
import com.vaadin.flow.component.icon.VaadinIcon;
import com.vaadin.flow.component.AttachEvent;
import com.vaadin.flow.component.DetachEvent;
import com.vaadin.flow.component.UI;
import com.vaadin.flow.router.BeforeEvent;
import com.vaadin.flow.router.HasUrlParameter;
import com.vaadin.flow.router.HasDynamicTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.shared.Registration;
import com.vaadin.flow.theme.lumo.LumoUtility;
import de.assecutor.votianlt.model.CargoItem;
import de.assecutor.votianlt.model.DeliveryStation;
import de.assecutor.votianlt.model.Job;
import de.assecutor.votianlt.model.JobServiceSelection;
import de.assecutor.votianlt.model.LocationPosition;
import de.assecutor.votianlt.model.task.BaseTask;
import de.assecutor.votianlt.model.task.TodoListTask;
import de.assecutor.votianlt.model.task.PhotoTask;
import de.assecutor.votianlt.model.task.SignatureTask;
import de.assecutor.votianlt.model.task.ConfirmationTask;
import de.assecutor.votianlt.model.task.BarcodeTask;
import de.assecutor.votianlt.model.task.CommentTask;
import de.assecutor.votianlt.model.AppUser;
import de.assecutor.votianlt.pages.base.ui.component.StationTile;
import de.assecutor.votianlt.pages.base.ui.component.DialogStylingHelper;
import de.assecutor.votianlt.pages.base.ui.component.ViewToolbar;
import lombok.extern.slf4j.Slf4j;
import de.assecutor.votianlt.repository.CargoItemRepository;
import de.assecutor.votianlt.repository.JobRepository;
import de.assecutor.votianlt.repository.SignatureRepository;
import de.assecutor.votianlt.repository.BarcodeRepository;
import de.assecutor.votianlt.repository.PhotoRepository;
import de.assecutor.votianlt.repository.CommentRepository;
import de.assecutor.votianlt.repository.ServiceRepository;
import de.assecutor.votianlt.model.Service;
import de.assecutor.votianlt.model.Signature;
import de.assecutor.votianlt.model.Barcode;
import de.assecutor.votianlt.model.Photo;
import de.assecutor.votianlt.model.Comment;
import de.assecutor.votianlt.model.JobStatus;
import de.assecutor.votianlt.pages.service.AppUserService;
import de.assecutor.votianlt.service.JobHistoryService;
import de.assecutor.votianlt.service.JobUpdateBroadcaster;
import de.assecutor.votianlt.service.LocationService;
import de.assecutor.votianlt.service.MessageService;
import de.assecutor.votianlt.service.TaskAssignmentService;
import de.assecutor.votianlt.util.DateTimeFormatUtil;
import jakarta.annotation.security.RolesAllowed;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Value;

import java.time.LocalDateTime;
import java.math.BigDecimal;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

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

    private final JobRepository jobRepository;
    private final CargoItemRepository cargoItemRepository;
    private final SignatureRepository signatureRepository;
    private final BarcodeRepository barcodeRepository;
    private final PhotoRepository photoRepository;
    private final CommentRepository commentRepository;
    private final AppUserService appUserService;
    private final JobHistoryService jobHistoryService;
    private final JobUpdateBroadcaster jobUpdateBroadcaster;
    private final LocationService locationService;
    private final ServiceRepository serviceRepository;
    private final TaskAssignmentService taskAssignmentService;

    @Value("${app.google.maps.api-key}")
    private String googleMapsApiKey;

    private final VerticalLayout content;
    private final List<Div> taskCards = new ArrayList<>();
    private Registration jobUpdateRegistration;
    private ObjectId currentJobId;
    private Div stationTilesSection;

    public JobSummaryView(JobRepository jobRepository, CargoItemRepository cargoItemRepository,
            SignatureRepository signatureRepository, BarcodeRepository barcodeRepository,
            PhotoRepository photoRepository, CommentRepository commentRepository, AppUserService appUserService,
            MessageService messageService, JobHistoryService jobHistoryService,
            JobUpdateBroadcaster jobUpdateBroadcaster, LocationService locationService,
            ServiceRepository serviceRepository, TaskAssignmentService taskAssignmentService) {
        this.jobRepository = jobRepository;
        this.cargoItemRepository = cargoItemRepository;
        this.signatureRepository = signatureRepository;
        this.barcodeRepository = barcodeRepository;
        this.photoRepository = photoRepository;
        this.commentRepository = commentRepository;
        this.appUserService = appUserService;
        this.jobHistoryService = jobHistoryService;
        this.jobUpdateBroadcaster = jobUpdateBroadcaster;
        this.locationService = locationService;
        this.serviceRepository = serviceRepository;
        this.taskAssignmentService = taskAssignmentService;

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

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

    @Override
    public void setParameter(BeforeEvent event, String parameter) {
        if (parameter == null || parameter.isBlank()) {
            content.removeAll();
            removeAll();
            add(new ViewToolbar(getTranslation("jobsummary.title")));
            content.add(new Span(getTranslation("jobsummary.error.noid")));
            add(content);
            return;
        }

        try {
            currentJobId = new ObjectId(parameter);
        } catch (Exception e) {
            content.removeAll();
            removeAll();
            add(new ViewToolbar(getTranslation("jobsummary.title")));
            content.add(new Span(getTranslation("jobsummary.error.invalidid", parameter)));
            add(content);
            return;
        }

        refreshCurrentJobSummary();
    }

    @Override
    protected void onAttach(AttachEvent attachEvent) {
        super.onAttach(attachEvent);
        UI ui = attachEvent.getUI();
        jobUpdateRegistration = jobUpdateBroadcaster.register(jobId -> {
            if (currentJobId == null || jobId == null || !currentJobId.equals(jobId)) {
                return;
            }
            ui.access(this::refreshStationTilesOnly);
        });
    }

    @Override
    protected void onDetach(DetachEvent detachEvent) {
        if (jobUpdateRegistration != null) {
            jobUpdateRegistration.remove();
            jobUpdateRegistration = null;
        }
        super.onDetach(detachEvent);
    }

    private void refreshCurrentJobSummary() {
        if (currentJobId == null) {
            return;
        }

        content.removeAll();
        removeAll();

        Job job = jobRepository.findById(currentJobId).orElse(null);
        if (job == null) {
            add(new ViewToolbar(getTranslation("jobsummary.title")));
            content.add(new Span(getTranslation("jobsummary.error.notfound", currentJobId.toHexString())));
            add(content);
            return;
        }

        // Create Send Message Button for toolbar
        Button sendMessageButton = new Button(getTranslation("jobsummary.button.sendmessage"));
        sendMessageButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        sendMessageButton.addClickListener(e -> {
            // Check if job has an app user assigned
            if (job.getAppUser() == null || job.getAppUser().isBlank()) {
                Notification
                        .show(getTranslation("jobsummary.notification.noappuser"), 3000, Notification.Position.MIDDLE)
                        .addThemeVariants(NotificationVariant.LUMO_ERROR);
                return;
            }

            String appUserId = job.getAppUser();
            String jobNumber = job.getJobNumber() != null ? job.getJobNumber() : job.getId().toHexString();

            // Navigate to message details view with job conversation
            // Format: message-details/{clientId}/job-{jobNumber}
            String conversationId = "job-" + jobNumber;
            getUI().ifPresent(ui -> ui.navigate("message-details/" + appUserId + "/" + conversationId));
        });

        // Create Manual Completion Button for app jobs (digital processing)
        Button manualCompleteButton = null;
        if (job.isDigitalProcessing() && job.getStatus() != JobStatus.COMPLETED
                && job.getStatus() != JobStatus.CANCELLED) {
            manualCompleteButton = new Button(getTranslation("jobsummary.button.manualcomplete"));
            manualCompleteButton.addThemeVariants(ButtonVariant.LUMO_ERROR);
            manualCompleteButton.addClickListener(e -> getUI()
                    .ifPresent(ui -> ui.navigate("job_manual_complete/" + job.getId().toHexString())));
        }

        // Create Job History Button for toolbar
        Button jobHistoryButton = new Button(getTranslation("jobsummary.button.jobhistory"));
        jobHistoryButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        jobHistoryButton.addClickListener(e -> {
            getUI().ifPresent(ui -> ui.navigate("job_history/" + job.getId().toHexString()));
        });

        // Add toolbar with buttons
        if (manualCompleteButton != null) {
            add(new ViewToolbar(getTranslation("jobsummary.title"), manualCompleteButton, sendMessageButton,
                    jobHistoryButton));
        } else {
            add(new ViewToolbar(getTranslation("jobsummary.title"), sendMessageButton, jobHistoryButton));
        }

        List<CargoItem> cargo = cargoItemRepository.findByJobId(currentJobId);
        List<BaseTask> tasks = taskAssignmentService.findTasksForJob(job);

        render(job, cargo, tasks);
        add(content);
    }

    private void refreshStationTilesOnly() {
        if (currentJobId == null || stationTilesSection == null) {
            return;
        }

        Job job = jobRepository.findById(currentJobId).orElse(null);
        if (job == null) {
            return;
        }

        List<CargoItem> cargo = cargoItemRepository.findByJobId(currentJobId);
        List<BaseTask> tasks = taskAssignmentService.findTasksForJob(job);

        Div updatedSection = createStationTilesSection(job, cargo, tasks);
        content.replace(stationTilesSection, updatedSection);
        stationTilesSection = updatedSection;
    }

    private void render(Job job, List<CargoItem> cargoItems, List<BaseTask> tasks) {
        content.removeAll();

        stationTilesSection = createStationTilesSection(job, cargoItems, tasks);
        content.add(stationTilesSection);

        // Fracht und weitere Infos
        HorizontalLayout midRow = new HorizontalLayout();
        midRow.setWidthFull();
        midRow.setSpacing(true);

        VerticalLayout cargoBox = borderedBox();
        cargoBox.add(new H3(getTranslation("jobsummary.section.cargo")));
        if (cargoItems == null || cargoItems.isEmpty()) {
            cargoBox.add(new Span(getTranslation("jobsummary.cargo.none")));
        } else {
            for (CargoItem ci : cargoItems) {
                if (ci == null)
                    continue;
                String desc = ci.getDescription();
                Integer qty = ci.getQuantity();
                String dims = dimString(ci);
                String weight = ci.getWeightKg() != null ? ci.getWeightKg() + " kg" : "";
                String line = (qty != null ? qty + " x " : "") + (desc != null ? desc : "")
                        + (dims.isBlank() ? "" : " " + dims) + (weight.isBlank() ? "" : " " + weight);
                if (!line.isBlank())
                    cargoBox.add(new Span(line));
            }
        }

        VerticalLayout infoBox = borderedBox();
        infoBox.add(new H3(getTranslation("jobsummary.section.info")));

        // Preis basierend auf den hinterlegten Leistungen berechnen
        PriceCalculationResult priceResult = calculatePriceFromServices(job);

        Div priceTable = new Div();
        priceTable.addClassName("price-table");

        priceTable.add(createPriceRow(getTranslation("jobsummary.info.netto") + ":",
                formatPrice(priceResult.netAmount()), false));
        priceTable.add(createPriceRow(getTranslation("jobsummary.info.gesamt") + ":",
                formatPrice(priceResult.totalAmount()), true));

        infoBox.add(priceTable);

        if (job.getRemark() != null && !job.getRemark().isBlank()) {
            infoBox.add(new Span(getTranslation("jobsummary.info.bemerkung") + ": " + job.getRemark()));
        }
        if (job.isDigitalProcessing()) {
            infoBox.add(new Span(getTranslation("jobsummary.info.digital")));
        }
        if (job.getAppUser() != null && !job.getAppUser().isBlank()) {
            infoBox.add(
                    new Span(getTranslation("jobsummary.info.appuser") + ": " + resolveAppUserName(job.getAppUser())));
        }

        cargoBox.setWidth("50%");
        infoBox.setWidth("50%");
        midRow.add(cargoBox, infoBox);
        content.add(midRow);

        // Google Maps Karte mit Route
        addRouteMap(job);

        // Manual completion button for jobs without digital processing
        if (!job.isDigitalProcessing() && job.getStatus() != JobStatus.COMPLETED
                && job.getStatus() != JobStatus.CANCELLED) {
            HorizontalLayout buttonRow = new HorizontalLayout();
            buttonRow.setWidthFull();
            buttonRow.setJustifyContentMode(HorizontalLayout.JustifyContentMode.CENTER);
            buttonRow.getStyle().set("margin-top", "var(--lumo-space-l)");

            Button completeButton = new Button(getTranslation("jobsummary.button.complete"),
                    new Icon(VaadinIcon.CHECK_CIRCLE));
            completeButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_SUCCESS);
            completeButton.addClickListener(e -> {
                Dialog dialog = DialogStylingHelper.createConfirmationDialog(
                        getTranslation("jobsummary.dialog.complete.title"),
                        getTranslation("jobsummary.dialog.complete.text", job.getJobNumber()),
                        "560px",
                        getTranslation("jobsummary.dialog.complete.cancel"),
                        getTranslation("jobsummary.dialog.complete.confirm"),
                        () -> {
                            try {
                                JobStatus oldStatus = job.getStatus();
                                job.setStatus(JobStatus.COMPLETED);
                                job.setUpdatedAt(LocalDateTime.now());
                                jobRepository.save(job);
                                jobHistoryService.logStatusChange(job, oldStatus, JobStatus.COMPLETED, "Manuell");
                                Notification
                                        .show(getTranslation("jobsummary.notification.completed", job.getJobNumber()),
                                                3000, Notification.Position.BOTTOM_END)
                                        .addThemeVariants(NotificationVariant.LUMO_SUCCESS);
                                // Re-render the page
                                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);
                            }
                        },
                        ButtonVariant.LUMO_PRIMARY);
                dialog.open();
            });

            buttonRow.add(completeButton);
            content.add(buttonRow);
        }
    }

    private VerticalLayout borderedBox() {
        VerticalLayout box = new VerticalLayout();
        box.addClassName("summary-card");
        box.setPadding(true);
        box.setSpacing(false);
        return box;
    }

    private Div createStationTilesSection(Job job, List<CargoItem> cargoItems, List<BaseTask> tasks) {
        Div stationGrid = new Div();
        stationGrid.setWidthFull();
        stationGrid.addClassName("stations-grid");

        stationGrid.add(createPickupSummaryTile(job, cargoItems, tasks));

        List<DeliveryStation> stations = job.getDeliveryStations();
        if (stations != null && !stations.isEmpty()) {
            for (int i = 0; i < stations.size(); i++) {
                stationGrid.add(createDeliverySummaryTile(stations.get(i), i, stations.size(), tasks));
            }
        } else {
            stationGrid.add(createLegacyDeliverySummaryTile(job, tasks));
        }

        return stationGrid;
    }

    private StationTile createPickupSummaryTile(Job job, List<CargoItem> cargoItems, List<BaseTask> tasks) {
        String title = getTranslation("jobsummary.section.pickup") + " "
                + formatDateWithTime(job.getPickupDate(), job.getPickupTime());
        List<String> additionalLines = buildPickupSummaryDetails(job, cargoItems);
        if (job.getPickupPhone() != null && !job.getPickupPhone().isBlank()) {
            additionalLines.add(getTranslation("jobsummary.station.phone") + ": " + job.getPickupPhone());
        }

        StationTile tile = createSummaryTile(StationTile.StationType.PICKUP, 0, title, job.getPickupCompany(),
                buildDisplayName(job.getPickupSalutation(), job.getPickupFirstName(), job.getPickupLastName()),
                job.getPickupStreet(), job.getPickupHouseNumber(), job.getPickupZip(), job.getPickupCity(),
                additionalLines);
        tile.setAddressValidated(areAllTasksCompleted(sortVisibleTasks(tasks)));
        return tile;
    }

    private StationTile createDeliverySummaryTile(DeliveryStation station, int index, int stationCount,
            List<BaseTask> tasks) {
        String title = getTranslation("jobsummary.section.delivery") + " " + (stationCount > 1 ? (index + 1) + " " : "")
                + formatDateWithTime(station.getDeliveryDate(), station.getDeliveryTime());
        List<BaseTask> stationTasks = getTasksForStation(station, tasks, false);
        List<String> additionalLines = buildDeliverySummaryDetails(stationTasks);
        if (station.getPhone() != null && !station.getPhone().isBlank()) {
            additionalLines.add(getTranslation("jobsummary.station.phone") + ": " + station.getPhone());
        }

        StationTile tile = createSummaryTile(StationTile.StationType.DELIVERY, index + 1, title, station.getCompany(),
                buildDisplayName(station.getSalutation(), station.getFirstName(), station.getLastName()),
                station.getStreet(), station.getHouseNumber(), station.getZip(), station.getCity(), additionalLines);
        tile.setAddressValidated(areAllTasksCompleted(stationTasks));
        tile.setInteractive(true);
        tile.setClickListener(clickedTile -> showStationTasksDialog(title, stationTasks));
        return tile;
    }

    private StationTile createLegacyDeliverySummaryTile(Job job, List<BaseTask> tasks) {
        String title = getTranslation("jobsummary.section.delivery") + " "
                + formatDateWithTime(job.getDeliveryDate(), job.getDeliveryTime());
        List<BaseTask> stationTasks = getTasksForStation(null, tasks, true);
        List<String> additionalLines = buildDeliverySummaryDetails(stationTasks);
        if (job.getDeliveryPhone() != null && !job.getDeliveryPhone().isBlank()) {
            additionalLines.add(getTranslation("jobsummary.station.phone") + ": " + job.getDeliveryPhone());
        }

        StationTile tile = createSummaryTile(StationTile.StationType.DELIVERY, 1, title, job.getDeliveryCompany(),
                buildDisplayName(job.getDeliverySalutation(), job.getDeliveryFirstName(), job.getDeliveryLastName()),
                job.getDeliveryStreet(), job.getDeliveryHouseNumber(), job.getDeliveryZip(), job.getDeliveryCity(),
                additionalLines);
        tile.setAddressValidated(areAllTasksCompleted(stationTasks));
        tile.setInteractive(true);
        tile.setClickListener(clickedTile -> showStationTasksDialog(title, stationTasks));
        return tile;
    }

    private StationTile createSummaryTile(StationTile.StationType type, int stationNumber, String title, String company,
            String displayName, String street, String houseNumber, String zip, String city,
            List<String> additionalLines) {
        StationTile tile = new StationTile(type, stationNumber, title.trim(), false);
        tile.setInteractive(false);
        tile.updatePreview(company, displayName, null, street, houseNumber, zip, city, additionalLines);
        return tile;
    }

    private List<String> buildPickupSummaryDetails(Job job, List<CargoItem> cargoItems) {
        List<String> additionalLines = new ArrayList<>();
        additionalLines.add(getTranslation("addjob.tab.cargo") + ": " + summarizeCargoItems(cargoItems));

        String pickupAppointment = formatPickupAppointment(job.getPickupDate(), job.getPickupTime());
        if (pickupAppointment != null) {
            additionalLines.add(getTranslation("addjob.appointment.pickup") + ": " + pickupAppointment);
        }

        additionalLines.add(buildDigitalProcessingPreview(job.isDigitalProcessing(), job.getAppUser()));
        return additionalLines;
    }

    private String summarizeCargoItems(List<CargoItem> cargoItems) {
        if (cargoItems == null || cargoItems.isEmpty()) {
            return getTranslation("jobsummary.cargo.none");
        }

        List<String> summaries = new ArrayList<>();
        for (CargoItem cargoItem : cargoItems) {
            if (cargoItem == null) {
                continue;
            }

            String description = cargoItem.getDescription() != null ? cargoItem.getDescription().trim() : "";
            Integer quantity = cargoItem.getQuantity();
            if (description.isEmpty() && quantity == null) {
                continue;
            }

            StringBuilder summary = new StringBuilder();
            if (quantity != null) {
                summary.append(quantity).append("x ");
            }
            summary.append(description.isEmpty() ? getTranslation("addjob.tab.cargo") : description);
            summaries.add(summary.toString().trim());
        }

        if (summaries.isEmpty()) {
            return getTranslation("jobsummary.cargo.none");
        }
        if (summaries.size() <= 2) {
            return String.join(", ", summaries);
        }
        return String.join(", ", summaries.subList(0, 2)) + " +" + (summaries.size() - 2);
    }

    private String formatPickupAppointment(java.time.LocalDate appointmentDate, java.time.LocalTime appointmentTime) {
        if (appointmentDate == null) {
            return null;
        }

        String formattedDate = formatLocalDate(appointmentDate);
        if (appointmentTime == null) {
            return formattedDate;
        }
        return formattedDate + " " + formatLocalTime(appointmentTime);
    }

    private String buildDigitalProcessingPreview(boolean digitalProcessingEnabled, String appUserId) {
        StringBuilder preview = new StringBuilder();
        preview.append(getTranslation("profile.settings.digitalprocess")).append(": ")
                .append(getTranslation(digitalProcessingEnabled ? "common.yes" : "common.no"));

        if (digitalProcessingEnabled && appUserId != null && !appUserId.isBlank()) {
            preview.append(" (").append(resolveAppUserName(appUserId)).append(")");
        }
        return preview.toString();
    }

    private List<String> buildDeliverySummaryDetails(List<BaseTask> tasks) {
        if (tasks == null || tasks.isEmpty()) {
            return new ArrayList<>(
                    List.of(getTranslation("addjob.tab.tasks") + ": " + getTranslation("jobsummary.tasks.none")));
        }

        List<String> summaries = new ArrayList<>();
        for (BaseTask task : tasks) {
            if (task != null) {
                summaries.add(summarizeDeliveryTask(task));
            }
        }

        if (summaries.isEmpty()) {
            return new ArrayList<>(
                    List.of(getTranslation("addjob.tab.tasks") + ": " + getTranslation("jobsummary.tasks.none")));
        }
        return summaries;
    }

    private String summarizeDeliveryTask(BaseTask task) {
        if (task instanceof ConfirmationTask confirmationTask) {
            String buttonText = trimToNull(confirmationTask.getButtonText());
            if (buttonText != null) {
                return confirmationTask.getDisplayName() + " \"" + buttonText + "\"";
            }

            String description = trimToNull(confirmationTask.getDescription());
            return description != null ? confirmationTask.getDisplayName() + " \"" + description + "\""
                    : confirmationTask.getDisplayName();
        }

        if (task instanceof TodoListTask todoListTask) {
            long itemCount = todoListTask.getTodoItems() == null ? 0
                    : todoListTask.getTodoItems().stream().filter(item -> item != null && !item.trim().isEmpty())
                            .count();
            return itemCount > 0 ? task.getDisplayName() + " (" + itemCount + ")" : task.getDisplayName();
        }

        if (task instanceof PhotoTask photoTask) {
            String range = formatMinMaxRange(photoTask.getMinPhotoCount(), photoTask.getMaxPhotoCount());
            return range.isBlank() ? task.getDisplayName() : task.getDisplayName() + " " + range;
        }

        if (task instanceof BarcodeTask barcodeTask) {
            String range = formatMinMaxRange(barcodeTask.getMinBarcodeCount(), barcodeTask.getMaxBarcodeCount());
            return range.isBlank() ? task.getDisplayName() : task.getDisplayName() + " " + range;
        }

        if (task instanceof CommentTask commentTask) {
            String commentText = trimToNull(commentTask.getCommentText());
            if (commentText != null) {
                return task.getDisplayName() + " \"" + commentText + "\"";
            }
            return commentTask.isRequired() ? task.getDisplayName() + " (" + getTranslation("common.required") + ")"
                    : task.getDisplayName();
        }

        if (task instanceof SignatureTask) {
            return task.getDisplayName();
        }

        String description = trimToNull(task.getDescription());
        return description != null ? task.getDisplayName() + " \"" + description + "\"" : task.getDisplayName();
    }

    private String formatMinMaxRange(Integer minValue, Integer maxValue) {
        if (minValue != null && maxValue != null) {
            return minValue.equals(maxValue) ? String.valueOf(minValue) : minValue + "-" + maxValue;
        }
        if (minValue != null) {
            return String.valueOf(minValue);
        }
        if (maxValue != null) {
            return String.valueOf(maxValue);
        }
        return "";
    }

    private String trimToNull(String value) {
        if (value == null) {
            return null;
        }
        String trimmed = value.trim();
        return trimmed.isEmpty() ? null : trimmed;
    }

    private String buildDisplayName(String salutation, String firstName, String lastName) {
        List<String> parts = new ArrayList<>();
        if (salutation != null && !salutation.isBlank()) {
            parts.add(salutation.trim());
        }
        if (firstName != null && !firstName.isBlank()) {
            parts.add(firstName.trim());
        }
        if (lastName != null && !lastName.isBlank()) {
            parts.add(lastName.trim());
        }
        return String.join(" ", parts);
    }

    private List<BaseTask> getTasksForStation(DeliveryStation station, List<BaseTask> tasks, boolean legacyMode) {
        int stationOrder = station != null ? station.getStationOrder() : 0;
        String stationId = station != null ? station.getStationIdAsString() : null;

        if (tasks != null && !tasks.isEmpty()) {
            List<BaseTask> stationTasks = new ArrayList<>();
            for (BaseTask task : tasks) {
                if (task == null) {
                    continue;
                }

                String taskStationId = task.getStationIdAsString();
                Integer taskStationOrder = task.getStationOrder();
                if (stationId != null && stationId.equals(taskStationId)) {
                    stationTasks.add(task);
                } else if (legacyMode) {
                    if (taskStationOrder == null || taskStationOrder == stationOrder) {
                        stationTasks.add(task);
                    }
                } else if (taskStationOrder != null && taskStationOrder == stationOrder) {
                    stationTasks.add(task);
                }
            }
            if (!stationTasks.isEmpty()) {
                return sortVisibleTasks(stationTasks);
            }
        }

        if (!legacyMode && station != null && station.getTasks() != null && !station.getTasks().isEmpty()) {
            return sortVisibleTasks(station.getTasks());
        }

        return new ArrayList<>();
    }

    private List<BaseTask> sortVisibleTasks(List<BaseTask> tasks) {
        return tasks.stream()
                .filter(task -> task != null && task.getDisplayName() != null && !task.getDisplayName().isBlank())
                .sorted((left, right) -> Integer.compare(left.getTaskOrder() != null ? left.getTaskOrder() : 0,
                        right.getTaskOrder() != null ? right.getTaskOrder() : 0))
                .toList();
    }

    private boolean areAllTasksCompleted(List<BaseTask> tasks) {
        return tasks != null && !tasks.isEmpty() && tasks.stream().allMatch(task -> task != null && task.isCompleted());
    }

    private void showStationTasksDialog(String stationTitle, List<BaseTask> tasks) {
        Dialog dialog = DialogStylingHelper.createStyledDialog(stationTitle, "720px");
        dialog.setMaxHeight("85vh");
        dialog.setResizable(true);
        dialog.setHeaderTitle(stationTitle);
        dialog.addDialogCloseActionListener(e -> resetAllTaskCardHoverStates());

        taskCards.clear();

        VerticalLayout dialogContent = DialogStylingHelper.createContentLayout("620px");
        dialogContent.getStyle().set("min-width", "0");
        dialogContent.addClassName("dialog-content-panel");

        H4 header = new H4(getTranslation("jobsummary.section.tasks"));
        header.getStyle().set("margin", "0");
        dialogContent.add(header);

        if (tasks == null || tasks.isEmpty()) {
            dialogContent.add(new Span(getTranslation("jobsummary.tasks.none")));
        } else {
            for (BaseTask task : tasks) {
                String displayName = task.getDisplayName();
                if (displayName == null || displayName.isBlank()) {
                    continue;
                }
                Div taskCard = createTaskCard(task, displayName);
                taskCards.add(taskCard);
                dialogContent.add(taskCard);
            }
        }

        Button closeButton = new Button("Schließen", e -> {
            dialog.close();
            resetAllTaskCardHoverStates();
        });
        closeButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        dialog.getFooter().add(closeButton);

        dialog.add(DialogStylingHelper.wrapContent(dialogContent));
        dialog.open();
    }

    private String formatLocalDate(java.time.LocalDate date) {
        try {
            return DateTimeFormatUtil.formatDate(date);
        } catch (Exception e) {
            return "";
        }
    }

    private String formatLocalTime(java.time.LocalTime time) {
        try {
            return DateTimeFormatUtil.formatTime(time);
        } catch (Exception e) {
            return "";
        }
    }

    private String formatDateWithTime(java.time.LocalDate date, java.time.LocalTime time) {
        StringBuilder sb = new StringBuilder();
        if (date != null) {
            sb.append(formatLocalDate(date));
            if (time != null) {
                sb.append(", ").append(formatLocalTime(time));
            }
        }
        return sb.toString();
    }

    private String valueOrEmpty(String v) {
        return v == null ? "" : v;
    }

    private String concatAddress(String street, String house) {
        String s = valueOrEmpty(street);
        String h = valueOrEmpty(house);
        return (s + (h.isBlank() ? "" : " " + h)).trim();
    }

    private String concatZipCity(String zip, String city) {
        String z = valueOrEmpty(zip);
        String c = valueOrEmpty(city);
        if (!z.isBlank() && !c.isBlank())
            return z + " " + c;
        return (z + " " + c).trim();
    }

    private String dimString(CargoItem ci) {
        // Values are stored in cm (not mm), so display directly without division
        String len = ci.getLengthMm() != null ? ci.getLengthMm().intValue() + " cm" : "";
        String wid = ci.getWidthMm() != null ? ci.getWidthMm().intValue() + " cm" : "";
        String hei = ci.getHeightMm() != null ? ci.getHeightMm().intValue() + " cm" : "";
        String combined = String.join(" x ",
                java.util.stream.Stream.of(len, wid, hei).filter(s -> !s.isBlank()).toList());
        return combined.isBlank() ? "" : combined;
    }

    private String formatPrice(java.math.BigDecimal price) {
        java.text.NumberFormat nf = java.text.NumberFormat.getCurrencyInstance(Locale.GERMANY);
        return nf.format(price);
    }

    private Div createPriceRow(String label, String value, boolean bold) {
        Div row = new Div();
        row.addClassName("price-row");
        if (bold) {
            row.addClassName("price-row--strong");
        }

        Span labelSpan = new Span(label);
        labelSpan.addClassName("price-row-label");

        Span valueSpan = new Span(value);
        valueSpan.addClassName("price-row-value");

        row.add(labelSpan, valueSpan);
        return row;
    }

    private String resolveAppUserName(String appUserIdString) {
        try {
            ObjectId id = new ObjectId(appUserIdString);
            AppUser au = appUserService.findById(id);
            if (au != null) {
                String fn = au.getVorname();
                String ln = au.getNachname();
                String name = (fn != null ? fn : "").trim() + (fn != null && ln != null ? " " : "")
                        + (ln != null ? ln : "");
                if (!name.isBlank())
                    return name;
                if (au.getBezeichnung() != null && !au.getBezeichnung().isBlank())
                    return au.getBezeichnung();
                if (au.getEmail() != null && !au.getEmail().isBlank())
                    return au.getEmail();
            }
        } catch (Exception e) {
            log.debug("Failed to resolve AppUser name for ID {}: {}", appUserIdString, e.getMessage());
        }
        return appUserIdString; // Fallback: show raw string if lookup fails
    }

    private void addRouteMap(Job job) {
        // Baue Adress-Strings
        String origin = (concatAddress(job.getPickupStreet(), job.getPickupHouseNumber()) + ", "
                + concatZipCity(job.getPickupZip(), job.getPickupCity())).trim();

        // Build destination and waypoints from delivery stations
        List<DeliveryStation> stations = job.getDeliveryStations();
        String destination;
        List<String> waypoints = new ArrayList<>();

        if (stations != null && !stations.isEmpty()) {
            // Last station is the final destination
            DeliveryStation lastStation = stations.get(stations.size() - 1);
            destination = (concatAddress(lastStation.getStreet(), lastStation.getHouseNumber()) + ", "
                    + concatZipCity(lastStation.getZip(), lastStation.getCity())).trim();

            // Intermediate stations are waypoints
            for (int i = 0; i < stations.size() - 1; i++) {
                DeliveryStation station = stations.get(i);
                String wp = (concatAddress(station.getStreet(), station.getHouseNumber()) + ", "
                        + concatZipCity(station.getZip(), station.getCity())).trim();
                if (!wp.isBlank()) {
                    waypoints.add(wp);
                }
            }
        } else {
            // Fallback to flat delivery fields
            destination = (concatAddress(job.getDeliveryStreet(), job.getDeliveryHouseNumber()) + ", "
                    + concatZipCity(job.getDeliveryZip(), job.getDeliveryCity())).trim();
        }

        if (origin.isBlank() || destination.isBlank()) {
            return;
        }

        // Prüfe ob App-Tracking aktiviert ist und Job nicht erledigt/storniert
        LocationPosition appUserPosition = null;
        boolean showAppUserPosition = job.isDigitalProcessing() && job.getStatus() != JobStatus.COMPLETED
                && job.getStatus() != JobStatus.CANCELLED && job.getAppUser() != null && !job.getAppUser().isBlank();

        if (showAppUserPosition) {
            appUserPosition = locationService.getLatestPosition(job.getAppUser());
        }

        Div map = new Div();
        map.setWidthFull();
        map.setHeight("520px");
        map.addClassName("job-summary-map");

        Div routeInfo = new Div();
        routeInfo.setWidthFull();
        routeInfo.addClassName("route-card");

        content.add(map, routeInfo);

        // Position für JavaScript vorbereiten
        final LocationPosition position = appUserPosition;
        final boolean hasPosition = position != null && position.getLatitude() != null
                && position.getLongitude() != null;
        final String appUserId = showAppUserPosition ? job.getAppUser() : "";
        final boolean shouldUpdate = showAppUserPosition;

        // Gespeicherte Route-Daten aus dem Job holen
        Double savedDistance = job.getRouteDistanceKm();
        Integer savedDuration = job.getRouteDurationSeconds();
        boolean hasSavedRouteData = savedDistance != null && savedDuration != null;

        String js = buildMapJs(origin, destination, waypoints, hasPosition, position, appUserId, shouldUpdate,
                hasSavedRouteData, savedDistance != null ? savedDistance : 0.0,
                savedDuration != null ? savedDuration : 0);

        map.getElement().executeJs(js, map.getElement(), routeInfo.getElement());
    }

    private String buildMapJs(String origin, String destination, List<String> waypoints, boolean hasPosition,
            LocationPosition position, String appUserId, boolean shouldUpdate, boolean hasSavedRouteData,
            double savedDistance, int savedDuration) {
        String apiKey = getGoogleMapsApiKey();
        // Explizit mit Punkt als Dezimaltrennzeichen formatieren
        String lat = hasPosition ? String.format(java.util.Locale.US, "%.6f", position.getLatitude()) : "0";
        String lng = hasPosition ? String.format(java.util.Locale.US, "%.6f", position.getLongitude()) : "0";
        // Distanz mit Punkt als Dezimaltrennzeichen für JavaScript
        String savedDistanceStr = String.format(java.util.Locale.US, "%.1f", savedDistance);

        // Gespeicherte Dauer formatieren
        int hours = savedDuration / 3600;
        int minutes = (savedDuration % 3600) / 60;
        String savedDurationText = formatDurationShort(hours, minutes);
        String plannedRouteLabel = escapeJs(getTranslation("jobsummary.route.planned"));
        String durationLabel = escapeJs(getTranslation("createinvoice.route.duration"));

        // Build waypoints JS array
        StringBuilder waypointsJs = new StringBuilder("[");
        if (waypoints != null && !waypoints.isEmpty()) {
            for (int i = 0; i < waypoints.size(); i++) {
                if (i > 0)
                    waypointsJs.append(",");
                waypointsJs.append("{location:'").append(escapeJs(waypoints.get(i))).append("',stopover:true}");
            }
        }
        waypointsJs.append("]");

        return """
                (function(){
                	var host = $0;
                	var infoEl = $1;
                	var origin = '%s';
                	var destination = '%s';
                	var apiKey = '%s';
                	var appUserLat = %s;
                	var appUserLng = %s;
                	var hasAppUserPos = %s;
                	var appUserId = '%s';
                	var shouldUpdate = %s;
                	var hasSavedRouteData = %s;
                	var savedDistance = %s;
                	var savedDurationText = '%s';
                                var plannedRouteLabel = '%s';
                                var durationLabel = '%s';
                	var waypoints = %s;

                	var appUserMarker = null;
                	var updateInterval = null;
                	var map = null;

                	function init(){
                		map = new google.maps.Map(host, {
                			center: {lat: 51.163, lng: 10.447},
                			zoom: 6,
                			mapTypeControl: false
                		});

                		var trafficLayer = new google.maps.TrafficLayer();
                		trafficLayer.setMap(map);

                		var ds = new google.maps.DirectionsService();
                		var routeRequest = {
                			origin: origin,
                			destination: destination,
                			travelMode: google.maps.TravelMode.DRIVING,
                			provideRouteAlternatives: false,
                			drivingOptions: {
                				departureTime: new Date(),
                				trafficModel: google.maps.TrafficModel.BEST_GUESS
                			}
                		};
                		if(waypoints.length > 0){
                			routeRequest.waypoints = waypoints;
                			routeRequest.optimizeWaypoints = false;
                		}
                		ds.route(routeRequest, function(res, status){
                			if(status === 'OK'){
                				infoEl.innerHTML = '';

                				// Gespeicherte Route-Daten anzeigen (wenn vorhanden)
                				if(hasSavedRouteData){
                					var savedRouteDiv = document.createElement('div');
                					savedRouteDiv.style.margin = '4px 0 12px 0';
                					savedRouteDiv.style.padding = '8px';
                					savedRouteDiv.style.backgroundColor = '#e3f2fd';
                					savedRouteDiv.style.borderRadius = '4px';
                					savedRouteDiv.style.fontWeight = 'bold';
                                        savedRouteDiv.textContent = '📍 ' + plannedRouteLabel + ': ' + savedDistance.toFixed(1) + ' km • ' + durationLabel + ': ' + savedDurationText;
                					infoEl.appendChild(savedRouteDiv);
                				}

                				var bounds = new google.maps.LatLngBounds();

                				// Nur die erste (beste) Route anzeigen
                				var route = res.routes[0];
                				var dr = new google.maps.DirectionsRenderer({
                					map: map,
                					preserveViewport: false,
                					suppressMarkers: false,
                					suppressPolylines: true
                				});
                				dr.setDirections(res);

                				var path = route.overview_path || [];
                				var poly = new google.maps.Polyline({
                					path: path,
                					strokeColor: '#1976d2',
                					strokeOpacity: 0.95,
                					strokeWeight: 6
                				});
                				poly.setMap(map);

                				// Bounds aus der Route erstellen
                				if(path && path.length){
                					path.forEach(function(pt){ bounds.extend(pt); });
                				}

                				// App-Nutzer Position Marker
                				if(hasAppUserPos){
                					createOrUpdateAppUserMarker(appUserLat, appUserLng);
                					bounds.extend({lat: appUserLat, lng: appUserLng});
                				}

                				if(!bounds.isEmpty()){
                					map.fitBounds(bounds);
                				}

                				// Alle 30 Sekunden aktualisieren
                				if(shouldUpdate && appUserId){
                					startPositionUpdates(appUserId);
                				}
                			}
                		});
                	}

                	function createOrUpdateAppUserMarker(lat, lng){
                		if(appUserMarker){
                			appUserMarker.setPosition({lat: lat, lng: lng});
                		} else {
                			appUserMarker = new google.maps.Marker({
                				position: {lat: lat, lng: lng},
                				map: map,
                				title: 'Position App-Nutzer',
                				icon: {
                					path: google.maps.SymbolPath.CIRCLE,
                					scale: 10,
                					fillColor: '#4caf50',
                					fillOpacity: 1,
                					strokeColor: '#ffffff',
                					strokeWeight: 2
                				}
                			});

                			var infoWindow = new google.maps.InfoWindow({
                				content: '<div style="font-weight:bold;">Position App-Nutzer</div>'
                			});

                			appUserMarker.addListener('click', function(){
                				infoWindow.open(map, appUserMarker);
                			});
                		}
                	}

                	function startPositionUpdates(userId){
                		updateInterval = setInterval(function(){
                			fetch('/api/location/' + encodeURIComponent(userId))
                				.then(function(response){
                					if(!response.ok) throw new Error('No position');
                					return response.json();
                				})
                				.then(function(data){
                					if(data && data.latitude && data.longitude){
                						createOrUpdateAppUserMarker(data.latitude, data.longitude);
                					}
                				})
                				.catch(function(err){
                					console.log('Location update failed:', err);
                				});
                		}, 30000);
                	}

                	if(!(window.google && window.google.maps)){
                		var s = document.createElement('script');
                		s.src = 'https://maps.googleapis.com/maps/api/js?key=' + apiKey + '&libraries=places';
                		s.onload = init;
                		document.head.appendChild(s);
                	} else {
                		init();
                	}
                })();
                """
                .formatted(escapeJs(origin), escapeJs(destination), escapeJs(apiKey), lat, lng,
                        Boolean.toString(hasPosition), escapeJs(appUserId), Boolean.toString(shouldUpdate),
                        Boolean.toString(hasSavedRouteData), savedDistanceStr, escapeJs(savedDurationText),
                        plannedRouteLabel, durationLabel, waypointsJs.toString());
    }

    private String formatDurationShort(int hours, int minutes) {
        String hourUnit = getTranslation("duration.hours.short");
        String minuteUnit = getTranslation("duration.minutes.short");
        if (hours > 0) {
            return String.format("%d %s %d %s", hours, hourUnit, minutes, minuteUnit);
        }
        return String.format("%d %s", minutes, minuteUnit);
    }

    // Hilfsfunktion zum einfachen Escapen von JS-Zeichen in Strings
    private String escapeJs(String s) {
        if (s == null)
            return "";
        return s.replace("\\", "\\\\").replace("'", "\\'").replace("\n", " ").replace("\r", " ");
    }

    @Override
    public String getPageTitle() {
        return getTranslation("page.title.job.summary");
    }

    private void showTaskDetailsDialog(BaseTask task) {
        Dialog dialog = DialogStylingHelper.createStyledDialog("Aufgaben-Details", "560px");
        dialog.setResizable(true);
        dialog.setDraggable(true);

        // Reset all task card hover states when dialog closes
        dialog.addDialogCloseActionListener(e -> resetAllTaskCardHoverStates());

        VerticalLayout dialogContent = DialogStylingHelper.createContentLayout("420px");
        dialogContent.addClassName("dialog-content-panel");

        // Task type and status
        Span typeSpan = new Span("Typ: " + task.getDisplayName());
        typeSpan.getStyle().set("font-weight", "bold");
        dialogContent.add(typeSpan);

        Span statusSpan = new Span("Status: " + (task.isCompleted() ? "Abgeschlossen" : "Offen"));
        if (task.isCompleted()) {
            statusSpan.getStyle().set("color", "var(--lumo-success-text-color)");
        } else {
            statusSpan.getStyle().set("color", "var(--lumo-error-text-color)");
        }
        dialogContent.add(statusSpan);

        // Task-specific details
        addTaskSpecificDetails(dialogContent, task);

        // Completion details if completed
        if (task.isCompleted()) {
            dialogContent.add(new Span("")); // Spacer
            if (task.getCompletedAt() != null) {
                dialogContent.add(new Span("Abgeschlossen am: " + formatDateTime(task.getCompletedAt())));
            }
            if (task.getCompletedBy() != null && !task.getCompletedBy().isBlank()) {
                dialogContent.add(new Span("Abgeschlossen von: " + task.getCompletedBy()));
            }
        }

        // Close button
        Button closeButton = new Button("Schließen", e -> {
            dialog.close();
            resetAllTaskCardHoverStates();
        });
        closeButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);

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

    private void addTaskSpecificDetails(VerticalLayout content, BaseTask task) {
        if (task instanceof TodoListTask todoTask) {
            content.add(new Span("To-Do Items:"));
            if (todoTask.getTodoItems() != null && !todoTask.getTodoItems().isEmpty()) {
                for (String item : todoTask.getTodoItems()) {
                    if (item != null && !item.isBlank()) {
                        Span itemSpan = new Span("  • " + item);
                        itemSpan.getStyle().set("margin-left", "20px");
                        content.add(itemSpan);
                    }
                }
            } else {
                content.add(new Span("  Keine Items definiert"));
            }
        } else if (task instanceof PhotoTask photoTask) {
            if (photoTask.getMinPhotoCount() != null || photoTask.getMaxPhotoCount() != null) {
                String photoInfo = "Fotos: ";
                if (photoTask.getMinPhotoCount() != null && photoTask.getMaxPhotoCount() != null) {
                    photoInfo += photoTask.getMinPhotoCount() + " - " + photoTask.getMaxPhotoCount()
                            + " Fotos erforderlich";
                } else if (photoTask.getMinPhotoCount() != null) {
                    photoInfo += "Mindestens " + photoTask.getMinPhotoCount() + " Fotos erforderlich";
                } else if (photoTask.getMaxPhotoCount() != null) {
                    photoInfo += "Maximal " + photoTask.getMaxPhotoCount() + " Fotos erlaubt";
                }
                content.add(new Span(photoInfo));
            }

            // Show photos if task is completed
            if (task.isCompleted()) {
                try {
                    ObjectId taskId = new ObjectId(task.getIdAsString());
                    List<Photo> photos = photoRepository.findByTaskId(taskId);

                    if (!photos.isEmpty()) {
                        content.add(new Span("")); // Spacer

                        // Collect all photos from all Photo entries
                        List<String> allPhotos = new ArrayList<>();
                        for (Photo photo : photos) {
                            if (photo.getPhoto() != null && !photo.getPhoto().isBlank()) {
                                allPhotos.add(photo.getPhoto());
                            }
                        }

                        if (!allPhotos.isEmpty()) {
                            content.add(new Span("Aufgenommene Fotos (" + allPhotos.size() + "):"));

                            // Create photo gallery container
                            Div photoGallery = createPhotoGallery(allPhotos);
                            content.add(photoGallery);
                        }
                    }
                } catch (Exception e) {
                    log.debug("Failed to load photos for task {}: {}", task.getId(), e.getMessage());
                }
            }
        } else if (task instanceof ConfirmationTask confirmationTask) {
            if (confirmationTask.getButtonText() != null && !confirmationTask.getButtonText().isBlank()) {
                content.add(new Span("Button-Text: " + confirmationTask.getButtonText()));
            }
        } else if (task instanceof SignatureTask) {
            content.add(new Span("Unterschrift erforderlich"));

            // Show signature if task is completed
            if (task.isCompleted()) {
                try {
                    ObjectId taskId = new ObjectId(task.getIdAsString());
                    List<Signature> signatures = signatureRepository.findByTaskId(taskId);

                    if (!signatures.isEmpty()) {
                        content.add(new Span("")); // Spacer
                        content.add(new Span("Gespeicherte Unterschrift:"));

                        // Display the latest signature (assuming one signature per task)
                        Signature signature = signatures.get(signatures.size() - 1);
                        String svgContent = signature.getSignatureSvg();

                        if (svgContent != null && !svgContent.isBlank()) {
                            // Create a div to hold the SVG
                            Div svgContainer = new Div();
                            svgContainer.addClassNames("detail-card", "detail-card--asset");
                            svgContainer.getStyle().set("width", "100%").set("max-width", "450px").set("overflow",
                                    "hidden");

                            // Process SVG to make it responsive
                            String responsiveSvg = makeResponsiveSvg(svgContent);
                            svgContainer.getElement().setProperty("innerHTML", responsiveSvg);
                            content.add(svgContainer);
                        }
                    }
                } catch (Exception e) {
                    log.debug("Failed to load signature for task {}: {}", task.getId(), e.getMessage());
                }
            }
        } else if (task instanceof BarcodeTask) {
            content.add(new Span("Barcode-Scan erforderlich"));

            // Show barcodes if task is completed
            if (task.isCompleted()) {
                try {
                    ObjectId taskId = new ObjectId(task.getIdAsString());
                    List<Barcode> barcodes = barcodeRepository.findByTaskId(taskId);

                    if (!barcodes.isEmpty()) {
                        content.add(new Span("")); // Spacer
                        content.add(new Span("Gescannte Barcodes (" + barcodes.size() + "):"));

                        // Display all scanned barcodes
                        for (int i = 0; i < barcodes.size(); i++) {
                            Barcode barcode = barcodes.get(i);
                            String barcodeValue = barcode.getBarcode();

                            if (barcodeValue != null && !barcodeValue.isBlank()) {
                                // Create a styled container for each barcode
                                Div barcodeContainer = new Div();
                                barcodeContainer.addClassNames("detail-card", "detail-card--code");
                                barcodeContainer.getStyle().set("margin", "var(--lumo-space-xs) 0").set("font-size",
                                        "var(--lumo-font-size-s)");

                                Span barcodeSpan = new Span((i + 1) + ". " + barcodeValue);
                                barcodeContainer.add(barcodeSpan);
                                content.add(barcodeContainer);
                            }
                        }
                    }
                } catch (Exception e) {
                    log.debug("Failed to load barcodes for task {}: {}", task.getId(), e.getMessage());
                }
            }
        } else if (task instanceof CommentTask commentTask) {
            content.add(new Span("Kommentar erforderlich"));

            if (commentTask.getCommentText() != null && !commentTask.getCommentText().isBlank()) {
                content.add(new Span("Hinweis: " + commentTask.getCommentText()));
            }

            if (commentTask.isRequired()) {
                content.add(new Span("Pflichtfeld"));
            }

            // Show comments if task is completed
            if (task.isCompleted()) {
                try {
                    ObjectId taskId = new ObjectId(task.getIdAsString());
                    List<Comment> comments = commentRepository.findByTaskIdOrderByCreatedAtDesc(taskId);

                    if (!comments.isEmpty()) {
                        content.add(new Span("Abgegebene Kommentare (" + comments.size() + "):"));

                        for (Comment comment : comments) {
                            Div commentContainer = new Div();
                            commentContainer.addClassNames("detail-card", "detail-card--comment");
                            commentContainer.getStyle().set("margin", "4px 0");

                            Span commentText = new Span(comment.getCommentText());
                            commentContainer.add(commentText);
                            content.add(commentContainer);
                        }
                    }
                } catch (Exception e) {
                    log.debug("Failed to load comments for task {}: {}", task.getId(), e.getMessage());
                }
            }
        }
    }

    private String formatDateTime(java.time.LocalDateTime dateTime) {
        return DateTimeFormatUtil.formatDateTime(dateTime);
    }

    private Div createTaskCard(BaseTask task, String displayName) {
        Div taskCard = new Div();
        taskCard.addClassName("job-task-card");
        if (task.isCompleted()) {
            taskCard.addClassName("completed");
        }

        // Task icon based on type
        Icon taskIcon = getTaskIcon(task);
        taskIcon.addClassName("job-task-icon");

        // Task content
        VerticalLayout taskContent = new VerticalLayout();
        taskContent.setPadding(false);
        taskContent.setSpacing(false);
        taskContent.addClassName("job-task-content");

        // Task name with order number (display as 1-based instead of 0-based)
        String taskNameWithOrder = (task.getTaskOrder() != null ? (task.getTaskOrder() + 1) + ". " : "") + displayName;
        Span taskName = new Span(taskNameWithOrder);
        taskName.addClassName("job-task-name");

        Span statusBadge = new Span(getTaskStatusLabel(task));
        statusBadge.addClassNames("job-task-status", task.isCompleted() ? "completed" : "open");

        HorizontalLayout headerRow = new HorizontalLayout(taskName, statusBadge);
        headerRow.setWidthFull();
        headerRow.setPadding(false);
        headerRow.setSpacing(true);
        headerRow.setAlignItems(HorizontalLayout.Alignment.CENTER);
        headerRow.setJustifyContentMode(HorizontalLayout.JustifyContentMode.BETWEEN);

        // Task status/description
        Span taskDescription = new Span(getTaskDescription(task));
        taskDescription.addClassName("job-task-description");

        taskContent.add(headerRow, taskDescription);

        // Status indicator
        Div statusIndicator = new Div();
        statusIndicator.addClassName("job-task-indicator");

        taskCard.add(taskIcon, taskContent, statusIndicator);

        taskCard.addClickListener(event -> {
            showTaskDetailsDialog(task);
        });

        return taskCard;
    }

    private Icon getTaskIcon(BaseTask task) {
        if (task instanceof TodoListTask) {
            return new Icon(VaadinIcon.LIST);
        } else if (task instanceof PhotoTask) {
            return new Icon(VaadinIcon.CAMERA);
        } else if (task instanceof SignatureTask) {
            return new Icon(VaadinIcon.EDIT);
        } else if (task instanceof ConfirmationTask) {
            return new Icon(VaadinIcon.CHECK_CIRCLE);
        } else if (task instanceof BarcodeTask) {
            return new Icon(VaadinIcon.BARCODE);
        } else if (task instanceof CommentTask) {
            return new Icon(VaadinIcon.COMMENT);
        } else {
            return new Icon(VaadinIcon.TASKS);
        }
    }

    private String getTaskStatusLabel(BaseTask task) {
        return task.isCompleted() ? "Abgeschlossen" : "Offen";
    }

    private String getTaskDescription(BaseTask task) {
        if (task.isCompleted()) {
            return "Erledigt"
                    + (task.getCompletedAt() != null ? " am " + formatLocalDate(task.getCompletedAt().toLocalDate())
                            : "");
        }

        if (task instanceof TodoListTask todoTask) {
            int itemCount = todoTask.getTodoItems() != null ? todoTask.getTodoItems().size() : 0;
            return itemCount + " Aufgabe" + (itemCount != 1 ? "n" : "") + " zu erledigen";
        } else if (task instanceof PhotoTask photoTask) {
            if (photoTask.getMinPhotoCount() != null && photoTask.getMaxPhotoCount() != null) {
                return photoTask.getMinPhotoCount() + "-" + photoTask.getMaxPhotoCount() + " Fotos erforderlich";
            } else if (photoTask.getMinPhotoCount() != null) {
                return "Mind. " + photoTask.getMinPhotoCount() + " Foto"
                        + (photoTask.getMinPhotoCount() != 1 ? "s" : "");
            } else if (photoTask.getMaxPhotoCount() != null) {
                return "Max. " + photoTask.getMaxPhotoCount() + " Foto"
                        + (photoTask.getMaxPhotoCount() != 1 ? "s" : "");
            } else {
                return "Foto erforderlich";
            }
        } else if (task instanceof SignatureTask) {
            return "Unterschrift erforderlich";
        } else if (task instanceof ConfirmationTask confirmationTask) {
            if (confirmationTask.getButtonText() != null && !confirmationTask.getButtonText().isBlank()) {
                return "Bestätigung: " + confirmationTask.getButtonText();
            } else {
                return "Bestätigung erforderlich";
            }
        } else if (task instanceof BarcodeTask) {
            return "Barcode-Scan erforderlich";
        } else if (task instanceof CommentTask commentTask) {
            if (commentTask.getCommentText() != null && !commentTask.getCommentText().isBlank()) {
                return "Kommentar: " + commentTask.getCommentText();
            } else {
                return "Kommentar erforderlich";
            }
        }

        return "Aufgabe offen";
    }

    private Div createPhotoGallery(List<String> photos) {
        Div galleryContainer = new Div();
        galleryContainer.addClassName("job-photo-gallery");

        if (photos.size() == 1) {
            // Single photo - no navigation needed
            Div photoContainer = createPhotoContainer(photos.get(0));
            galleryContainer.add(photoContainer);
        } else {
            // Multiple photos - add navigation
            final int[] currentIndex = { 0 }; // Use array to make it effectively final

            // Photo counter
            Span photoCounter = new Span((currentIndex[0] + 1) + " / " + photos.size());
            photoCounter.addClassName("job-photo-counter");

            // Photo container
            Div photoContainer = createPhotoContainer(photos.get(0));
            photoContainer.getStyle().set("margin", "0 40px") // Space for buttons
                    .set("flex", "1").set("display", "flex").set("align-items", "center")
                    .set("justify-content", "center");

            // Previous button
            Button prevButton = new Button(new Icon(VaadinIcon.CHEVRON_LEFT));
            prevButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY, ButtonVariant.LUMO_ICON);
            prevButton.addClassNames("job-photo-nav", "prev");

            // Next button
            Button nextButton = new Button(new Icon(VaadinIcon.CHEVRON_RIGHT));
            nextButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY, ButtonVariant.LUMO_ICON);
            nextButton.addClassNames("job-photo-nav", "next");

            // Navigation logic
            prevButton.addClickListener(e -> {
                if (currentIndex[0] > 0) {
                    currentIndex[0]--;
                    updatePhotoDisplay(photoContainer, photos.get(currentIndex[0]), photoCounter, currentIndex[0] + 1,
                            photos.size());
                }
                prevButton.setEnabled(currentIndex[0] > 0);
                nextButton.setEnabled(currentIndex[0] < photos.size() - 1);
            });

            nextButton.addClickListener(e -> {
                if (currentIndex[0] < photos.size() - 1) {
                    currentIndex[0]++;
                    updatePhotoDisplay(photoContainer, photos.get(currentIndex[0]), photoCounter, currentIndex[0] + 1,
                            photos.size());
                }
                prevButton.setEnabled(currentIndex[0] > 0);
                nextButton.setEnabled(currentIndex[0] < photos.size() - 1);
            });

            // Initial button states
            prevButton.setEnabled(false);
            nextButton.setEnabled(photos.size() > 1);

            galleryContainer.add(photoCounter, photoContainer, prevButton, nextButton);
        }

        return galleryContainer;
    }

    private Div createPhotoContainer(String base64Photo) {
        Div photoContainer = new Div();
        photoContainer.addClassName("job-photo-container");

        // Create image element
        String imgSrc = base64Photo.startsWith("data:") ? base64Photo : "data:image/jpeg;base64," + base64Photo;

        photoContainer.getElement().setProperty("innerHTML", "<img src='" + imgSrc
                + "' style='max-width: 100%; max-height: 450px; object-fit: contain; border-radius: var(--lumo-border-radius-s); box-shadow: 0 2px 8px rgba(0,0,0,0.1);' />");

        return photoContainer;
    }

    private void updatePhotoDisplay(Div photoContainer, String base64Photo, Span counter, int current, int total) {
        String imgSrc = base64Photo.startsWith("data:") ? base64Photo : "data:image/jpeg;base64," + base64Photo;

        photoContainer.getElement().setProperty("innerHTML", "<img src='" + imgSrc
                + "' style='max-width: 100%; max-height: 450px; object-fit: contain; border-radius: var(--lumo-border-radius-s); box-shadow: 0 2px 8px rgba(0,0,0,0.1);' />");

        counter.setText(current + " / " + total);
    }

    private String makeResponsiveSvg(String svgContent) {
        if (svgContent == null || svgContent.isBlank()) {
            return svgContent;
        }

        // Remove any existing width and height attributes and add responsive styling
        String responsiveSvg = svgContent.replaceAll("width\\s*=\\s*[\"'][^\"']*[\"']", "")
                .replaceAll("height\\s*=\\s*[\"'][^\"']*[\"']", "").replaceAll("style\\s*=\\s*[\"'][^\"']*[\"']", "");

        // Add responsive styling - preserve viewBox if it exists, otherwise try to
        // extract from width/height
        if (!responsiveSvg.contains("viewBox")) {
            // Try to extract original dimensions for viewBox
            String widthMatch = extractAttribute(svgContent, "width");
            String heightMatch = extractAttribute(svgContent, "height");

            if (widthMatch != null && heightMatch != null) {
                try {
                    // Clean numbers (remove px, pt, etc.)
                    String cleanWidth = widthMatch.replaceAll("[^0-9.]", "");
                    String cleanHeight = heightMatch.replaceAll("[^0-9.]", "");

                    if (!cleanWidth.isEmpty() && !cleanHeight.isEmpty()) {
                        responsiveSvg = responsiveSvg.replaceFirst("<svg",
                                "<svg viewBox=\"0 0 " + cleanWidth + " " + cleanHeight + "\"");
                    }
                } catch (Exception e) {
                    log.debug("Failed to extract SVG dimensions for viewBox: {}", e.getMessage());
                }
            }
        }

        // Add responsive styling
        responsiveSvg = responsiveSvg.replaceFirst("<svg",
                "<svg style=\"max-width: 100%; max-height: 200px; width: auto; height: auto;\"");

        return responsiveSvg;
    }

    private String extractAttribute(String svg, String attributeName) {
        String pattern = attributeName + "\\s*=\\s*[\"']([^\"']*)[\"']";
        java.util.regex.Pattern p = java.util.regex.Pattern.compile(pattern, java.util.regex.Pattern.CASE_INSENSITIVE);
        java.util.regex.Matcher matcher = p.matcher(svg);
        if (matcher.find()) {
            return matcher.group(1);
        }
        return null;
    }

    private void resetAllTaskCardHoverStates() {
        // Hover states are handled purely in CSS.
    }

    private String getGoogleMapsApiKey() {
        return googleMapsApiKey != null ? googleMapsApiKey : "";
    }

    /**
     * Berechnet den Preis basierend auf den hinterlegten Leistungen des Jobs.
     */
    private PriceCalculationResult calculatePriceFromServices(Job job) {
        BigDecimal netTotal = BigDecimal.ZERO;
        BigDecimal vatTotal = BigDecimal.ZERO;

        List<JobServiceSelection> selectedServices = job.getSelectedServices();
        if (selectedServices != null && !selectedServices.isEmpty()) {
            for (JobServiceSelection selectedService : selectedServices) {
                if (selectedService.getServiceId() == null) {
                    continue;
                }

                Service service = serviceRepository.findById(selectedService.getServiceId()).orElse(null);
                if (service == null) {
                    continue;
                }

                BigDecimal price = calculateServicePrice(service, selectedService.getRouteDistanceKm(),
                        selectedService.getRouteDurationSeconds());
                BigDecimal vatRate = Service.FIXED_VAT_RATE;

                netTotal = netTotal.add(price);
                vatTotal = vatTotal.add(price.multiply(vatRate));
            }

            BigDecimal totalAmount = netTotal.add(vatTotal);
            return new PriceCalculationResult(netTotal, vatTotal, totalAmount);
        }

        List<String> serviceIds = job.getServiceIds();
        if (serviceIds == null || serviceIds.isEmpty()) {
            // Fallback auf gespeicherten Preis
            BigDecimal price = job.getPrice() != null ? job.getPrice() : BigDecimal.ZERO;
            return new PriceCalculationResult(price, BigDecimal.ZERO, price);
        }

        // Routendaten für die Berechnung
        Double routeDistance = job.getRouteDistanceKm();
        Integer durationSeconds = job.getRouteDurationSeconds();

        for (String serviceId : serviceIds) {
            Service service = serviceRepository.findById(serviceId).orElse(null);
            if (service == null)
                continue;

            BigDecimal price = calculateServicePrice(service, routeDistance, durationSeconds);
            BigDecimal vatRate = Service.FIXED_VAT_RATE;

            netTotal = netTotal.add(price);
            vatTotal = vatTotal.add(price.multiply(vatRate));
        }

        BigDecimal totalAmount = netTotal.add(vatTotal);
        return new PriceCalculationResult(netTotal, vatTotal, totalAmount);
    }

    /**
     * Berechnet den Preis für eine einzelne Leistung basierend auf ihrer
     * Berechnungsgrundlage.
     */
    private BigDecimal calculateServicePrice(Service service, Double routeDistance, Integer durationSeconds) {
        if (service.getCalculationBasis() == null) {
            return BigDecimal.ZERO;
        }

        switch (service.getCalculationBasis()) {
        case FLAT_RATE:
            return service.getPrice() != null ? service.getPrice() : BigDecimal.ZERO;

        case DISTANCE:
            if (service.getPricePerKilometer() != null && routeDistance != null && routeDistance > 0) {
                return service.getPricePerKilometer().multiply(BigDecimal.valueOf(routeDistance));
            }
            return BigDecimal.ZERO;

        case TIME:
            if (service.getPricePer15Minutes() != null && durationSeconds != null && durationSeconds > 0) {
                // Dauer in 15-Minuten-Einheiten umrechnen
                int units = durationSeconds / 900; // 900 Sekunden = 15 Minuten
                if (durationSeconds % 900 > 0)
                    units++; // Aufrunden
                return service.getPricePer15Minutes().multiply(BigDecimal.valueOf(units));
            }
            return BigDecimal.ZERO;

        default:
            return BigDecimal.ZERO;
        }
    }

    /**
     * Record für die Preisberechnungsergebnisse.
     */
    private record PriceCalculationResult(BigDecimal netAmount, BigDecimal vatAmount, BigDecimal totalAmount) {
    }
}
