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.router.BeforeEvent;
import com.vaadin.flow.router.HasUrlParameter;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.theme.lumo.LumoUtility;
import de.assecutor.votianlt.model.CargoItem;
import de.assecutor.votianlt.model.Job;
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.ViewToolbar;
import lombok.extern.slf4j.Slf4j;
import de.assecutor.votianlt.repository.CargoItemRepository;
import de.assecutor.votianlt.repository.JobRepository;
import de.assecutor.votianlt.repository.TaskRepository;
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.LocationService;
import de.assecutor.votianlt.service.MessageService;
import de.assecutor.votianlt.util.DateTimeFormatUtil;
import com.vaadin.flow.component.confirmdialog.ConfirmDialog;
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)
@PageTitle("Zusammenfassung")
@RolesAllowed("USER")
@Slf4j
public class JobSummaryView extends Main implements HasUrlParameter<String> {

    private final JobRepository jobRepository;
    private final CargoItemRepository cargoItemRepository;
    private final TaskRepository taskRepository;
    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 LocationService locationService;
    private final ServiceRepository serviceRepository;

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

    private final VerticalLayout content;
    private final List<Div> taskCards = new ArrayList<>();

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

        setSizeFull();
        addClassNames(LumoUtility.BoxSizing.BORDER, LumoUtility.Display.FLEX, LumoUtility.FlexDirection.COLUMN,
                LumoUtility.Padding.MEDIUM, LumoUtility.Gap.SMALL);

        content = new VerticalLayout();
        content.setSpacing(true);
        content.setPadding(true);
        content.setWidthFull();
    }

    @Override
    public void setParameter(BeforeEvent event, String parameter) {
        content.removeAll();
        removeAll(); // Remove existing toolbar

        if (parameter == null || parameter.isBlank()) {
            add(new ViewToolbar("Zusammenfassung"));
            content.add(new Span("Fehler: Keine Job-ID angegeben"));
            add(content);
            return;
        }

        ObjectId jobId;
        try {
            jobId = new ObjectId(parameter);
        } catch (Exception e) {
            add(new ViewToolbar("Zusammenfassung"));
            content.add(new Span("Fehler: Ungültige Job-ID Format: " + parameter));
            add(content);
            return;
        }

        Job job = jobRepository.findById(jobId).orElse(null);
        if (job == null) {
            add(new ViewToolbar("Zusammenfassung"));
            content.add(new Span("Fehler: Job mit ID " + parameter + " nicht gefunden"));
            add(content);
            return;
        }

        // Create Send Message Button for toolbar
        Button sendMessageButton = new Button("Nachricht senden");
        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("Diesem Auftrag ist kein App-Nutzer zugeordnet", 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 Job History Button for toolbar
        Button jobHistoryButton = new Button("Job Historie");
        jobHistoryButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        jobHistoryButton.addClickListener(e -> {
            getUI().ifPresent(ui -> ui.navigate("job_history/" + job.getId().toHexString()));
        });

        // Add toolbar with both buttons in top right (Send Message button on the left)
        add(new ViewToolbar("Zusammenfassung", sendMessageButton, jobHistoryButton));

        List<CargoItem> cargo = cargoItemRepository.findByJobId(jobId);
        List<BaseTask> tasks = taskRepository.findByJobIdOrderByTaskOrderAsc(jobId);

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

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

        // Kopfzeile: Abholung/Lieferung
        HorizontalLayout topRow = new HorizontalLayout();
        topRow.setWidthFull();
        topRow.setSpacing(true);

        VerticalLayout pickupBox = borderedBox();
        pickupBox.add(new H3("Abholung " + formatDateWithTime(job.getPickupDate(), job.getPickupTime())));
        pickupBox.add(new Span(valueOrEmpty(job.getPickupCompany())));
        pickupBox.add(new Span(valueOrEmpty(job.getPickupSalutation()) + (job.getPickupSalutation() != null ? " " : "")
                + valueOrEmpty(job.getPickupFirstName()) + (job.getPickupFirstName() != null ? " " : "")
                + valueOrEmpty(job.getPickupLastName())));
        pickupBox.add(new Span(concatAddress(job.getPickupStreet(), job.getPickupHouseNumber())));
        pickupBox.add(new Span(concatZipCity(job.getPickupZip(), job.getPickupCity())));

        VerticalLayout deliveryBox = borderedBox();
        deliveryBox.add(new H3("Lieferung " + formatDateWithTime(job.getDeliveryDate(), job.getDeliveryTime())));
        deliveryBox.add(new Span(valueOrEmpty(job.getDeliveryCompany())));
        deliveryBox.add(new Span(valueOrEmpty(job.getDeliverySalutation())
                + (job.getDeliverySalutation() != null ? " " : "") + valueOrEmpty(job.getDeliveryFirstName())
                + (job.getDeliveryFirstName() != null ? " " : "") + valueOrEmpty(job.getDeliveryLastName())));
        deliveryBox.add(new Span(concatAddress(job.getDeliveryStreet(), job.getDeliveryHouseNumber())));
        deliveryBox.add(new Span(concatZipCity(job.getDeliveryZip(), job.getDeliveryCity())));

        pickupBox.setWidth("50%");
        deliveryBox.setWidth("50%");
        topRow.add(pickupBox, deliveryBox);
        content.add(topRow);

        // Aufgaben
        VerticalLayout tasksBox = borderedBox();
        tasksBox.add(new H3("Zu quittierende Aufgaben"));

        // Ensure consistent spacing and width for task cards
        tasksBox.setSpacing(false);
        tasksBox.getStyle().set("gap", "var(--lumo-space-xs)");

        // Clear previous task cards
        taskCards.clear();

        if (tasks == null || tasks.isEmpty()) {
            tasksBox.add(new Span("Keine Aufgaben"));
        } else {
            for (BaseTask task : tasks) {
                if (task != null) {
                    // Use getDisplayName() instead of getText() for task display
                    String displayName = task.getDisplayName();
                    if (displayName != null && !displayName.isBlank()) {
                        Div taskCard = createTaskCard(task, displayName);
                        taskCards.add(taskCard); // Keep reference for hover reset
                        tasksBox.add(taskCard);
                    }
                }
            }
        }
        content.add(tasksBox);

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

        VerticalLayout cargoBox = borderedBox();
        cargoBox.add(new H3("Zu transportierende Fracht"));
        if (cargoItems == null || cargoItems.isEmpty()) {
            cargoBox.add(new Span("Keine Frachtangaben"));
        } 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("Weitere Informationen"));
        
        // Preis basierend auf den hinterlegten Leistungen berechnen
        PriceCalculationResult priceResult = calculatePriceFromServices(job);
        infoBox.add(new Span("Netto: " + formatPrice(priceResult.netAmount())));
        infoBox.add(new Span("USt: " + formatPrice(priceResult.vatAmount())));
        infoBox.add(new Span("Gesamt: " + formatPrice(priceResult.totalAmount())));
        
        if (job.getRemark() != null && !job.getRemark().isBlank()) {
            infoBox.add(new Span("Bemerkung: " + job.getRemark()));
        }
        if (job.isDigitalProcessing()) {
            infoBox.add(new Span("Digitale Abwicklung per App: aktiviert"));
        }
        if (job.getAppUser() != null && !job.getAppUser().isBlank()) {
            infoBox.add(new Span("App-Nutzer: " + 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("Auftrag manuell abschließen", new Icon(VaadinIcon.CHECK_CIRCLE));
            completeButton.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_SUCCESS);
            completeButton.addClickListener(e -> {
                ConfirmDialog dialog = new ConfirmDialog();
                dialog.setHeader("Auftrag abschließen");
                dialog.setText("Möchten Sie den Auftrag " + job.getJobNumber() + " manuell abschließen?");
                dialog.setCancelable(true);
                dialog.setCancelText("Abbrechen");
                dialog.setConfirmText("Abschließen");
                dialog.setConfirmButtonTheme("primary");
                dialog.addConfirmListener(ev -> {
                    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("Auftrag " + job.getJobNumber() + " wurde abgeschlossen.", 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("Fehler beim Abschließen: " + ex.getMessage(), 5000,
                                        Notification.Position.BOTTOM_END)
                                .addThemeVariants(NotificationVariant.LUMO_ERROR);
                    }
                });
                dialog.open();
            });

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

    private VerticalLayout borderedBox() {
        VerticalLayout box = new VerticalLayout();
        box.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
        box.getStyle().set("border-radius", "var(--lumo-border-radius-m)");
        box.getStyle().set("background-color", "var(--lumo-base-color)");
        box.setPadding(true);
        box.setSpacing(false);
        return box;
    }

    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 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();
        String 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.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
        map.getStyle().set("border-radius", "var(--lumo-border-radius-m)");

        Div routeInfo = new Div();
        routeInfo.setWidthFull();
        routeInfo.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)");
        routeInfo.getStyle().set("border-radius", "var(--lumo-border-radius-m)");
        routeInfo.getStyle().set("padding", "var(--lumo-space-m)");
        routeInfo.getStyle().set("background-color", "var(--lumo-base-color)");

        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, 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, 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 = hours > 0 
            ? String.format("%d Std. %d Min.", hours, minutes) 
            : String.format("%d Min.", minutes);

        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 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();
                		ds.route({
                			origin: origin,
                			destination: destination,
                			travelMode: google.maps.TravelMode.DRIVING,
                			provideRouteAlternatives: false,
                			drivingOptions: {
                				departureTime: new Date(),
                				trafficModel: google.maps.TrafficModel.BEST_GUESS
                			}
                		}, 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 = '📍 Geplante Route: ' + savedDistance.toFixed(1) + ' km • Fahrtzeit: ' + 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));
    }

    // 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", " ");
    }

    private void showTaskDetailsDialog(BaseTask task) {
        Dialog dialog = new Dialog();
        dialog.setWidth("500px");
        dialog.setResizable(true);
        dialog.setDraggable(true);

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

        VerticalLayout dialogContent = new VerticalLayout();
        dialogContent.setPadding(true);
        dialogContent.setSpacing(true);

        // Header
        H4 header = new H4("Aufgaben-Details");
        dialogContent.add(header);

        // 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);

        HorizontalLayout buttonLayout = new HorizontalLayout(closeButton);
        buttonLayout.setJustifyContentMode(HorizontalLayout.JustifyContentMode.END);
        dialogContent.add(buttonLayout);

        dialog.add(dialogContent);
        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.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)")
                                    .set("border-radius", "var(--lumo-border-radius-m)")
                                    .set("padding", "var(--lumo-space-s)").set("background-color", "white")
                                    .set("width", "100%").set("max-width", "450px").set("overflow", "hidden")
                                    .set("display", "flex").set("align-items", "center")
                                    .set("justify-content", "center");

                            // 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.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)")
                                        .set("border-radius", "var(--lumo-border-radius-s)")
                                        .set("padding", "var(--lumo-space-s)").set("margin", "var(--lumo-space-xs) 0")
                                        .set("background-color", "var(--lumo-contrast-5pct)")
                                        .set("font-family", "monospace").set("font-size", "var(--lumo-font-size-s)")
                                        .set("word-break", "break-all");

                                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.getStyle().set("background-color", "#f5f5f5")
                                    .set("border", "1px solid #ddd").set("border-radius", "4px").set("padding", "8px")
                                    .set("margin", "4px 0").set("font-family", "monospace")
                                    .set("white-space", "pre-wrap");

                            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();

        // Card styling with fixed width
        taskCard.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)")
                .set("border-radius", "var(--lumo-border-radius-m)").set("padding", "var(--lumo-space-m)")
                .set("margin", "var(--lumo-space-xs) 0").set("background-color", "var(--lumo-base-color)")
                .set("cursor", "pointer").set("transition", "all 0.2s ease")
                .set("box-shadow", "0 1px 3px rgba(0, 0, 0, 0.1)").set("display", "flex").set("align-items", "center")
                .set("gap", "var(--lumo-space-m)").set("width", "100%").set("box-sizing", "border-box");

        // Hover effects
        taskCard.getElement().addEventListener("mouseenter", e -> {
            taskCard.getStyle().set("transform", "translateY(-2px)").set("box-shadow", "0 4px 12px rgba(0, 0, 0, 0.15)")
                    .set("border-color", "var(--lumo-primary-color-50pct)");
        });

        taskCard.getElement().addEventListener("mouseleave", e -> {
            taskCard.getStyle().set("transform", "translateY(0)").set("box-shadow", "0 1px 3px rgba(0, 0, 0, 0.1)")
                    .set("border-color", "var(--lumo-contrast-20pct)");
        });

        // Task icon based on type
        Icon taskIcon = getTaskIcon(task);
        taskIcon.getStyle().set("color",
                task.isCompleted() ? "var(--lumo-success-color)" : "var(--lumo-primary-color)");

        // Task content
        VerticalLayout taskContent = new VerticalLayout();
        taskContent.setPadding(false);
        taskContent.setSpacing(false);
        taskContent.getStyle().set("flex-grow", "1");

        // 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.getStyle().set("font-weight", "500").set("font-size", "var(--lumo-font-size-m)").set("color",
                task.isCompleted() ? "var(--lumo-success-text-color)" : "var(--lumo-body-text-color)");

        // Task status/description
        Span taskDescription = new Span(getTaskDescription(task));
        taskDescription.getStyle().set("font-size", "var(--lumo-font-size-s)")
                .set("color", "var(--lumo-secondary-text-color)").set("margin-top", "var(--lumo-space-xs)");

        taskContent.add(taskName, taskDescription);

        // Status indicator
        Div statusIndicator = new Div();
        statusIndicator.getStyle().set("width", "8px").set("height", "8px").set("border-radius", "50%")
                .set("background-color", task.isCompleted() ? "var(--lumo-success-color)" : "var(--lumo-error-color)");

        taskCard.add(taskIcon, taskContent, statusIndicator);

        // Click handler with hover state reset
        taskCard.addClickListener(event -> {
            showTaskDetailsDialog(task);
            // Reset hover state after dialog interaction
            taskCard.getStyle().set("transform", "translateY(0)").set("box-shadow", "0 1px 3px rgba(0, 0, 0, 0.1)")
                    .set("border-color", "var(--lumo-contrast-20pct)");
        });

        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 getTaskDescription(BaseTask task) {
        if (task.isCompleted()) {
            return "Abgeschlossen"
                    + (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.getStyle().set("border", "1px solid var(--lumo-contrast-20pct)")
                .set("border-radius", "var(--lumo-border-radius-m)").set("padding", "var(--lumo-space-m)")
                .set("background-color", "white").set("max-width", "600px").set("min-height", "500px")
                .set("height", "500px").set("position", "relative").set("display", "flex").set("align-items", "center")
                .set("justify-content", "center");

        if (photos.size() == 1) {
            // Single photo - no navigation needed
            Div photoContainer = createPhotoContainer(photos.get(0));
            photoContainer.getStyle().set("flex", "1").set("display", "flex").set("align-items", "center")
                    .set("justify-content", "center");
            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.getStyle().set("position", "absolute").set("top", "var(--lumo-space-s)")
                    .set("right", "var(--lumo-space-s)").set("background-color", "rgba(0, 0, 0, 0.6)")
                    .set("color", "white").set("padding", "var(--lumo-space-xs) var(--lumo-space-s)")
                    .set("border-radius", "var(--lumo-border-radius-s)").set("font-size", "var(--lumo-font-size-s)")
                    .set("z-index", "10");

            // 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.getStyle().set("position", "absolute").set("left", "var(--lumo-space-s)").set("top", "50%")
                    .set("transform", "translateY(-50%)").set("background-color", "rgba(255, 255, 255, 0.8)")
                    .set("border-radius", "50%").set("z-index", "10");

            // Next button
            Button nextButton = new Button(new Icon(VaadinIcon.CHEVRON_RIGHT));
            nextButton.addThemeVariants(ButtonVariant.LUMO_TERTIARY, ButtonVariant.LUMO_ICON);
            nextButton.getStyle().set("position", "absolute").set("right", "var(--lumo-space-s)").set("top", "50%")
                    .set("transform", "translateY(-50%)").set("background-color", "rgba(255, 255, 255, 0.8)")
                    .set("border-radius", "50%").set("z-index", "10");

            // 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.getStyle().set("width", "100%").set("height", "100%").set("display", "flex")
                .set("align-items", "center").set("justify-content", "center").set("overflow", "hidden");

        // 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() {
        for (Div taskCard : taskCards) {
            taskCard.getStyle().set("transform", "translateY(0)").set("box-shadow", "0 1px 3px rgba(0, 0, 0, 0.1)")
                    .set("border-color", "var(--lumo-contrast-20pct)");
        }
    }

    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<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.getVatRate() != null ? service.getVatRate() : new BigDecimal("0.19");
            
            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) {
    }
}
