import { html, type TemplateResult } from '@design.estate/dees-element';
import maplibregl from 'maplibre-gl';
import { renderIcon } from './geo-map.icons.js';
import { type INominatimResult } from './geo-map.search.js';
import type { ITrafficAwareRoute } from './geo-map.traffic.providers.js';
import type { INavigationGuideState } from './geo-map.navigation-guide.js';

// ─── Navigation/Routing Types ────────────────────────────────────────────────

export type TNavigationMode = 'driving' | 'walking' | 'cycling';

export interface IOSRMRoute {
  geometry: GeoJSON.LineString;
  distance: number;  // meters
  duration: number;  // seconds
  legs: IOSRMLeg[];
}

export interface IOSRMLeg {
  steps: IOSRMStep[];
  distance: number;
  duration: number;
}

export interface IOSRMStep {
  geometry: GeoJSON.LineString;
  maneuver: {
    type: string;       // 'turn', 'depart', 'arrive', etc.
    modifier?: string;  // 'left', 'right', 'straight', etc.
    location: [number, number];
  };
  name: string;         // Street name
  ref?: string;         // Road reference (A1, M25, etc.)
  distance: number;
  duration: number;
  driving_side: string;
}

export interface INavigationState {
  startPoint: [number, number] | null;
  endPoint: [number, number] | null;
  startAddress: string;
  endAddress: string;
  route: IOSRMRoute | null;
  trafficRoute: ITrafficAwareRoute | null;
  isLoading: boolean;
  error: string | null;
}

export interface IRouteCalculatedEvent {
  route: IOSRMRoute;
  startPoint: [number, number];
  endPoint: [number, number];
  mode: TNavigationMode;
}

/**
 * Callbacks for NavigationController events
 */
export interface INavigationControllerCallbacks {
  onRouteCalculated: (event: IRouteCalculatedEvent) => void;
  onRequestUpdate: () => void;
  getMap: () => maplibregl.Map | null;
  /** Optional callback to fetch traffic-aware route */
  getTrafficRoute?: (
    start: [number, number],
    end: [number, number],
    mode: TNavigationMode
  ) => Promise<ITrafficAwareRoute | null>;
  /** Optional callback when traffic toggle is changed */
  onTrafficToggle?: (enabled: boolean) => void;
  /** Optional callback to get current traffic state */
  getTrafficEnabled?: () => boolean;
  /** Optional callback to get current guidance state for turn-by-turn synchronization */
  getGuidanceState?: () => INavigationGuideState | null;
}

/**
 * Controller for A-to-B navigation functionality
 * Handles routing, markers, search inputs, and turn-by-turn directions
 */
export class NavigationController {
  // State
  public navigationState: INavigationState = {
    startPoint: null,
    endPoint: null,
    startAddress: '',
    endAddress: '',
    route: null,
    trafficRoute: null,
    isLoading: false,
    error: null,
  };

  // Navigation search state
  public navStartSearchQuery: string = '';
  public navEndSearchQuery: string = '';
  public navStartSearchResults: INominatimResult[] = [];
  public navEndSearchResults: INominatimResult[] = [];
  public navActiveInput: 'start' | 'end' | null = null;
  public navClickMode: 'start' | 'end' | null = null;
  public navHighlightedIndex: number = -1;

  // Mode
  public navigationMode: TNavigationMode = 'driving';

  // View mode: 'planning' for route input, 'directions' for turn-by-turn
  public viewMode: 'planning' | 'directions' = 'planning';

  // Internal
  private callbacks: INavigationControllerCallbacks;
  private navSearchDebounceTimer: ReturnType<typeof setTimeout> | null = null;
  private startMarker: maplibregl.Marker | null = null;
  private endMarker: maplibregl.Marker | null = null;

  constructor(callbacks: INavigationControllerCallbacks) {
    this.callbacks = callbacks;
  }

  // ─── Routing ────────────────────────────────────────────────────────────────

  /**
   * Fetch a route from OSRM API
   */
  public async fetchRoute(
    start: [number, number],
    end: [number, number],
    mode: TNavigationMode
  ): Promise<IOSRMRoute | null> {
    const profile = mode === 'cycling' ? 'bike' : mode === 'walking' ? 'foot' : 'car';
    const coords = `${start[0]},${start[1]};${end[0]},${end[1]}`;
    const url = `https://router.project-osrm.org/route/v1/${profile}/${coords}?geometries=geojson&steps=true&overview=full`;

    try {
      const response = await fetch(url);
      if (!response.ok) {
        throw new Error(`OSRM API error: ${response.status}`);
      }
      const data = await response.json();

      if (data.code !== 'Ok' || !data.routes || data.routes.length === 0) {
        throw new Error(data.message || 'No route found');
      }

      const route = data.routes[0];
      return {
        geometry: route.geometry,
        distance: route.distance,
        duration: route.duration,
        legs: route.legs,
      };
    } catch (error) {
      console.error('Route fetch error:', error);
      throw error;
    }
  }

  /**
   * Calculate and display route
   */
  public async calculateRoute(): Promise<void> {
    const { startPoint, endPoint } = this.navigationState;

    if (!startPoint || !endPoint) {
      this.navigationState = {
        ...this.navigationState,
        error: 'Please set both start and end points',
      };
      this.callbacks.onRequestUpdate();
      return;
    }

    this.navigationState = {
      ...this.navigationState,
      isLoading: true,
      error: null,
      trafficRoute: null,
    };
    this.callbacks.onRequestUpdate();

    try {
      // Fetch both regular route and traffic-aware route in parallel
      const [route, trafficRoute] = await Promise.all([
        this.fetchRoute(startPoint, endPoint, this.navigationMode),
        this.callbacks.getTrafficRoute
          ? this.callbacks.getTrafficRoute(startPoint, endPoint, this.navigationMode)
          : Promise.resolve(null),
      ]);

      if (route) {
        this.navigationState = {
          ...this.navigationState,
          route,
          trafficRoute,
          isLoading: false,
        };

        // Use traffic route geometry if available, otherwise use regular route
        const routeToRender = trafficRoute || route;
        this.renderRouteOnMap(routeToRender);

        // Dispatch route-calculated event
        this.callbacks.onRouteCalculated({
          route,
          startPoint,
          endPoint,
          mode: this.navigationMode,
        });

        // Fit map to route bounds
        this.fitToRoute(routeToRender);

        // Switch to directions view after successful route calculation
        this.viewMode = 'directions';
      }
    } catch (error) {
      this.navigationState = {
        ...this.navigationState,
        isLoading: false,
        error: error instanceof Error ? error.message : 'Failed to calculate route',
      };
    }
    this.callbacks.onRequestUpdate();
  }

  // ─── Point Management ───────────────────────────────────────────────────────

  /**
   * Set navigation start point
   */
  public setNavigationStart(coords: [number, number], address?: string): void {
    this.navigationState = {
      ...this.navigationState,
      startPoint: coords,
      startAddress: address || `${coords[1].toFixed(5)}, ${coords[0].toFixed(5)}`,
      error: null,
    };
    this.navStartSearchQuery = this.navigationState.startAddress;
    this.navStartSearchResults = [];
    this.updateNavigationMarkers();
    this.callbacks.onRequestUpdate();

    // Auto-calculate if both points are set
    if (this.navigationState.endPoint) {
      this.calculateRoute();
    }
  }

  /**
   * Set navigation end point
   */
  public setNavigationEnd(coords: [number, number], address?: string): void {
    this.navigationState = {
      ...this.navigationState,
      endPoint: coords,
      endAddress: address || `${coords[1].toFixed(5)}, ${coords[0].toFixed(5)}`,
      error: null,
    };
    this.navEndSearchQuery = this.navigationState.endAddress;
    this.navEndSearchResults = [];
    this.updateNavigationMarkers();
    this.callbacks.onRequestUpdate();

    // Auto-calculate if both points are set
    if (this.navigationState.startPoint) {
      this.calculateRoute();
    }
  }

  /**
   * Clear all navigation state
   */
  public clearNavigation(): void {
    this.navigationState = {
      startPoint: null,
      endPoint: null,
      startAddress: '',
      endAddress: '',
      route: null,
      trafficRoute: null,
      isLoading: false,
      error: null,
    };
    this.navStartSearchQuery = '';
    this.navEndSearchQuery = '';
    this.navStartSearchResults = [];
    this.navEndSearchResults = [];
    this.navClickMode = null;
    this.viewMode = 'planning';

    // Remove markers
    if (this.startMarker) {
      this.startMarker.remove();
      this.startMarker = null;
    }
    if (this.endMarker) {
      this.endMarker.remove();
      this.endMarker = null;
    }

    // Remove route layer and source
    const map = this.callbacks.getMap();
    if (map) {
      if (map.getLayer('route-layer')) {
        map.removeLayer('route-layer');
      }
      if (map.getLayer('route-outline-layer')) {
        map.removeLayer('route-outline-layer');
      }
      if (map.getSource('route-source')) {
        map.removeSource('route-source');
      }
    }
    this.callbacks.onRequestUpdate();
  }

  /**
   * Clear a specific navigation point
   */
  public clearNavPoint(pointType: 'start' | 'end'): void {
    if (pointType === 'start') {
      this.navigationState = {
        ...this.navigationState,
        startPoint: null,
        startAddress: '',
        route: null,
      };
      this.navStartSearchQuery = '';
      if (this.startMarker) {
        this.startMarker.remove();
        this.startMarker = null;
      }
    } else {
      this.navigationState = {
        ...this.navigationState,
        endPoint: null,
        endAddress: '',
        route: null,
      };
      this.navEndSearchQuery = '';
      if (this.endMarker) {
        this.endMarker.remove();
        this.endMarker = null;
      }
    }

    // Remove route display
    const map = this.callbacks.getMap();
    if (map) {
      if (map.getLayer('route-layer')) {
        map.removeLayer('route-layer');
      }
      if (map.getLayer('route-outline-layer')) {
        map.removeLayer('route-outline-layer');
      }
      if (map.getSource('route-source')) {
        map.removeSource('route-source');
      }
    }
    this.callbacks.onRequestUpdate();
  }

  // ─── Map Interaction ────────────────────────────────────────────────────────

  /**
   * Handle map click for navigation point selection
   */
  public handleMapClickForNavigation(e: maplibregl.MapMouseEvent): void {
    if (!this.navClickMode) return;

    const coords: [number, number] = [e.lngLat.lng, e.lngLat.lat];

    if (this.navClickMode === 'start') {
      this.setNavigationStart(coords);
    } else if (this.navClickMode === 'end') {
      this.setNavigationEnd(coords);
    }

    // Exit click mode
    this.navClickMode = null;

    // Re-enable map interactions
    const map = this.callbacks.getMap();
    if (map) {
      map.getCanvas().style.cursor = '';
    }
  }

  /**
   * Toggle map click mode for setting navigation points
   */
  public toggleNavClickMode(mode: 'start' | 'end'): void {
    const map = this.callbacks.getMap();

    if (this.navClickMode === mode) {
      // Cancel click mode
      this.navClickMode = null;
      if (map) {
        map.getCanvas().style.cursor = '';
      }
    } else {
      this.navClickMode = mode;
      if (map) {
        map.getCanvas().style.cursor = 'crosshair';
      }
    }
    this.callbacks.onRequestUpdate();
  }

  /**
   * Update navigation markers on the map
   */
  public updateNavigationMarkers(): void {
    const map = this.callbacks.getMap();
    if (!map) return;

    // Update start marker
    if (this.navigationState.startPoint) {
      if (!this.startMarker) {
        const el = document.createElement('div');
        el.className = 'nav-marker nav-marker-start';
        el.innerHTML = `<svg viewBox="0 0 24 24" fill="#22c55e" stroke="#fff" stroke-width="2"><circle cx="12" cy="12" r="8"/></svg>`;
        el.style.cssText = 'width: 24px; height: 24px; cursor: pointer;';
        this.startMarker = new maplibregl.Marker({ element: el, anchor: 'center', pitchAlignment: 'viewport' })
          .setLngLat(this.navigationState.startPoint)
          .addTo(map);
      } else {
        this.startMarker.setLngLat(this.navigationState.startPoint);
      }
    } else if (this.startMarker) {
      this.startMarker.remove();
      this.startMarker = null;
    }

    // Update end marker
    if (this.navigationState.endPoint) {
      if (!this.endMarker) {
        const el = document.createElement('div');
        el.className = 'nav-marker nav-marker-end';
        el.innerHTML = `<svg viewBox="0 0 24 24" fill="#ef4444" stroke="#fff" stroke-width="2"><circle cx="12" cy="12" r="8"/></svg>`;
        el.style.cssText = 'width: 24px; height: 24px; cursor: pointer;';
        this.endMarker = new maplibregl.Marker({ element: el, anchor: 'center', pitchAlignment: 'viewport' })
          .setLngLat(this.navigationState.endPoint)
          .addTo(map);
      } else {
        this.endMarker.setLngLat(this.navigationState.endPoint);
      }
    } else if (this.endMarker) {
      this.endMarker.remove();
      this.endMarker = null;
    }
  }

  /**
   * Render route on the map
   */
  public renderRouteOnMap(route: IOSRMRoute): void {
    const map = this.callbacks.getMap();
    if (!map) return;

    const sourceId = 'route-source';
    const layerId = 'route-layer';
    const outlineLayerId = 'route-outline-layer';

    // Remove existing layers/source
    if (map.getLayer(layerId)) {
      map.removeLayer(layerId);
    }
    if (map.getLayer(outlineLayerId)) {
      map.removeLayer(outlineLayerId);
    }
    if (map.getSource(sourceId)) {
      map.removeSource(sourceId);
    }

    // Add route source
    map.addSource(sourceId, {
      type: 'geojson',
      data: {
        type: 'Feature',
        properties: {},
        geometry: route.geometry,
      },
    });

    // Add outline layer (for border effect)
    map.addLayer({
      id: outlineLayerId,
      type: 'line',
      source: sourceId,
      layout: {
        'line-join': 'round',
        'line-cap': 'round',
      },
      paint: {
        'line-color': '#1e40af',
        'line-width': 8,
        'line-opacity': 0.8,
      },
    });

    // Add main route layer
    map.addLayer({
      id: layerId,
      type: 'line',
      source: sourceId,
      layout: {
        'line-join': 'round',
        'line-cap': 'round',
      },
      paint: {
        'line-color': '#3b82f6',
        'line-width': 5,
        'line-opacity': 1,
      },
    });
  }

  /**
   * Fit map to show the entire route
   */
  public fitToRoute(route: IOSRMRoute): void {
    const map = this.callbacks.getMap();
    if (!map || !route.geometry.coordinates.length) return;

    const bounds = new maplibregl.LngLatBounds();
    for (const coord of route.geometry.coordinates) {
      bounds.extend(coord as [number, number]);
    }

    map.fitBounds(bounds, { padding: 80 });
  }

  /**
   * Fly to a specific navigation step location
   */
  public flyToStep(step: IOSRMStep): void {
    const map = this.callbacks.getMap();
    if (!map) return;

    map.flyTo({
      center: step.maneuver.location,
      zoom: 17,
      duration: 1000,
    });
  }

  // ─── Search within Navigation ───────────────────────────────────────────────

  /**
   * Search Nominatim API for addresses
   */
  private async searchNominatim(query: string): Promise<INominatimResult[]> {
    if (query.length < 3) return [];

    const params = new URLSearchParams({
      q: query,
      format: 'json',
      limit: '5',
      addressdetails: '1',
    });

    const response = await fetch(`https://nominatim.openstreetmap.org/search?${params}`, {
      headers: {
        'User-Agent': 'dees-geo-map/1.0',
      },
    });

    if (!response.ok) return [];
    return response.json();
  }

  /**
   * Handle navigation input search
   */
  public handleNavSearchInput(event: Event, inputType: 'start' | 'end'): void {
    const input = event.target as HTMLInputElement;
    const query = input.value;

    if (inputType === 'start') {
      this.navStartSearchQuery = query;
    } else {
      this.navEndSearchQuery = query;
    }

    this.navActiveInput = inputType;
    this.navHighlightedIndex = -1;

    // Clear previous debounce
    if (this.navSearchDebounceTimer) {
      clearTimeout(this.navSearchDebounceTimer);
    }

    if (query.length < 3) {
      if (inputType === 'start') {
        this.navStartSearchResults = [];
      } else {
        this.navEndSearchResults = [];
      }
      this.callbacks.onRequestUpdate();
      return;
    }

    // Debounce API calls
    this.navSearchDebounceTimer = setTimeout(async () => {
      const results = await this.searchNominatim(query);
      if (inputType === 'start') {
        this.navStartSearchResults = results;
      } else {
        this.navEndSearchResults = results;
      }
      this.callbacks.onRequestUpdate();
    }, 500);
  }

  /**
   * Handle navigation search result selection
   */
  public selectNavSearchResult(result: INominatimResult, inputType: 'start' | 'end'): void {
    const lng = parseFloat(result.lon);
    const lat = parseFloat(result.lat);
    const coords: [number, number] = [lng, lat];
    const address = result.display_name;

    if (inputType === 'start') {
      this.setNavigationStart(coords, address);
      this.navStartSearchResults = [];
    } else {
      this.setNavigationEnd(coords, address);
      this.navEndSearchResults = [];
    }

    this.navActiveInput = null;
  }

  /**
   * Handle keyboard navigation in search results
   */
  public handleNavSearchKeydown(event: KeyboardEvent, inputType: 'start' | 'end'): void {
    const results = inputType === 'start' ? this.navStartSearchResults : this.navEndSearchResults;

    if (results.length === 0) return;

    switch (event.key) {
      case 'ArrowDown':
        event.preventDefault();
        this.navHighlightedIndex = Math.min(this.navHighlightedIndex + 1, results.length - 1);
        this.callbacks.onRequestUpdate();
        break;
      case 'ArrowUp':
        event.preventDefault();
        this.navHighlightedIndex = Math.max(this.navHighlightedIndex - 1, -1);
        this.callbacks.onRequestUpdate();
        break;
      case 'Enter':
        event.preventDefault();
        if (this.navHighlightedIndex >= 0 && results[this.navHighlightedIndex]) {
          this.selectNavSearchResult(results[this.navHighlightedIndex], inputType);
        }
        break;
      case 'Escape':
        event.preventDefault();
        if (inputType === 'start') {
          this.navStartSearchResults = [];
        } else {
          this.navEndSearchResults = [];
        }
        this.navActiveInput = null;
        this.callbacks.onRequestUpdate();
        break;
    }
  }

  // ─── Formatting Utilities ───────────────────────────────────────────────────

  /**
   * Format distance for display
   */
  public formatDistance(meters: number): string {
    if (meters < 1000) {
      return `${Math.round(meters)} m`;
    }
    return `${(meters / 1000).toFixed(1)} km`;
  }

  /**
   * Format duration for display
   */
  public formatDuration(seconds: number): string {
    if (seconds < 60) {
      return `${Math.round(seconds)} sec`;
    }
    if (seconds < 3600) {
      return `${Math.round(seconds / 60)} min`;
    }
    const hours = Math.floor(seconds / 3600);
    const mins = Math.round((seconds % 3600) / 60);
    return mins > 0 ? `${hours} hr ${mins} min` : `${hours} hr`;
  }

  /**
   * Get maneuver icon for turn type
   */
  public getManeuverIcon(type: string, modifier?: string): string {
    const icons: Record<string, string> = {
      'depart': '⬆️',
      'arrive': '🏁',
      'turn-left': '↰',
      'turn-right': '↱',
      'turn-slight left': '↖',
      'turn-slight right': '↗',
      'turn-sharp left': '⬅',
      'turn-sharp right': '➡',
      'continue-straight': '⬆️',
      'continue': '⬆️',
      'roundabout': '🔄',
      'rotary': '🔄',
      'merge': '⤵️',
      'fork-left': '↖',
      'fork-right': '↗',
      'end of road-left': '↰',
      'end of road-right': '↱',
      'new name': '⬆️',
      'notification': 'ℹ️',
    };

    const key = modifier ? `${type}-${modifier}` : type;
    return icons[key] || icons[type] || '➡';
  }

  /**
   * Get congestion label for display
   */
  public getCongestionLabel(level: 'low' | 'moderate' | 'heavy' | 'severe'): string {
    const labels = {
      low: 'Light traffic',
      moderate: 'Moderate traffic',
      heavy: 'Heavy traffic',
      severe: 'Severe congestion',
    };
    return labels[level];
  }

  /**
   * Format step instruction for display
   */
  public formatStepInstruction(step: IOSRMStep): string {
    const { type, modifier } = step.maneuver;
    const name = step.name || step.ref || 'unnamed road';

    switch (type) {
      case 'depart':
        return `Head ${modifier || 'forward'} on ${name}`;
      case 'arrive':
        return modifier === 'left'
          ? `Arrive at your destination on the left`
          : modifier === 'right'
          ? `Arrive at your destination on the right`
          : `Arrive at your destination`;
      case 'turn':
        return `Turn ${modifier || ''} onto ${name}`;
      case 'continue':
        return `Continue on ${name}`;
      case 'merge':
        return `Merge ${modifier || ''} onto ${name}`;
      case 'fork':
        return `Take the ${modifier || ''} fork onto ${name}`;
      case 'roundabout':
      case 'rotary':
        return `At the roundabout, take the exit onto ${name}`;
      case 'end of road':
        return `At the end of the road, turn ${modifier || ''} onto ${name}`;
      case 'new name':
        return `Continue on ${name}`;
      default:
        return `${type} ${modifier || ''} on ${name}`.trim();
    }
  }

  // ─── Mode ───────────────────────────────────────────────────────────────────

  /**
   * Change navigation mode and recalculate route if exists
   */
  public setNavigationMode(mode: TNavigationMode): void {
    this.navigationMode = mode;
    this.callbacks.onRequestUpdate();

    // Recalculate route if we have both points
    if (this.navigationState.startPoint && this.navigationState.endPoint) {
      this.calculateRoute();
    }
  }

  /**
   * Switch between planning and directions view
   */
  public setViewMode(mode: 'planning' | 'directions'): void {
    this.viewMode = mode;
    this.callbacks.onRequestUpdate();
  }

  // ─── Cleanup ────────────────────────────────────────────────────────────────

  /**
   * Clean up markers and resources
   */
  public cleanup(): void {
    if (this.startMarker) {
      this.startMarker.remove();
      this.startMarker = null;
    }
    if (this.endMarker) {
      this.endMarker.remove();
      this.endMarker = null;
    }
    if (this.navSearchDebounceTimer) {
      clearTimeout(this.navSearchDebounceTimer);
      this.navSearchDebounceTimer = null;
    }
  }

  // ─── Rendering ──────────────────────────────────────────────────────────────

  /**
   * Render the navigation panel
   * @param extraClass - Optional CSS class to add to the panel for positioning
   */
  public render(extraClass?: string): TemplateResult {
    // If we have a route and we're in directions view, show that
    if (this.viewMode === 'directions' && this.navigationState.route) {
      return this.renderDirectionsView(extraClass);
    }
    return this.renderPlanningView(extraClass);
  }

  /**
   * Render the route planning view (inputs, mode selector, actions)
   */
  private renderPlanningView(extraClass?: string): TemplateResult {
    const { isLoading, error, startPoint, endPoint } = this.navigationState;
    const canCalculate = startPoint && endPoint && !isLoading;
    const trafficEnabled = this.callbacks.getTrafficEnabled?.() ?? false;

    return html`
      <div class="navigation-panel ${extraClass || ''}">
        <div class="nav-header">
          <div class="nav-header-icon">${renderIcon('navigation')}</div>
          <span class="nav-header-title">Navigation</span>
        </div>

        <div class="nav-mode-selector">
          <button
            class="nav-mode-btn ${this.navigationMode === 'driving' ? 'active' : ''}"
            @click=${() => this.setNavigationMode('driving')}
            title="Driving"
          >
            ${renderIcon('car')}
          </button>
          <button
            class="nav-mode-btn ${this.navigationMode === 'walking' ? 'active' : ''}"
            @click=${() => this.setNavigationMode('walking')}
            title="Walking"
          >
            ${renderIcon('walk')}
          </button>
          <button
            class="nav-mode-btn ${this.navigationMode === 'cycling' ? 'active' : ''}"
            @click=${() => this.setNavigationMode('cycling')}
            title="Cycling"
          >
            ${renderIcon('bike')}
          </button>
        </div>

        <div class="nav-traffic-toggle">
          <div class="nav-traffic-toggle-label">
            ${renderIcon('traffic')}
            <span>Traffic-aware routing</span>
          </div>
          <button
            class="nav-traffic-toggle-btn ${trafficEnabled ? 'active' : ''}"
            @click=${() => this.callbacks.onTrafficToggle?.(!trafficEnabled)}
            title="${trafficEnabled ? 'Disable traffic' : 'Enable traffic'}"
          >
            <span class="toggle-track">
              <span class="toggle-thumb"></span>
            </span>
          </button>
        </div>

        <div class="nav-inputs">
          ${this.renderNavInput('start', 'Start point', this.navStartSearchQuery, this.navStartSearchResults)}
          ${this.renderNavInput('end', 'End point', this.navEndSearchQuery, this.navEndSearchResults)}
        </div>

        <div class="nav-actions">
          <button
            class="nav-action-btn primary"
            ?disabled=${!canCalculate}
            @click=${() => this.calculateRoute()}
          >
            Get Route
          </button>
          <button
            class="nav-action-btn secondary"
            @click=${() => this.clearNavigation()}
          >
            Clear
          </button>
        </div>

        ${error ? html`
          <div class="nav-error">
            ${renderIcon('error')}
            <span>${error}</span>
          </div>
        ` : ''}

        ${isLoading ? html`
          <div class="nav-loading">
            ${renderIcon('spinner')}
            <span>Calculating route...</span>
          </div>
        ` : ''}
      </div>
    `;
  }

  /**
   * Render the directions view (back button, summary, turn-by-turn steps)
   */
  private renderDirectionsView(extraClass?: string): TemplateResult {
    const { route, trafficRoute } = this.navigationState;

    if (!route) {
      // Shouldn't happen, but fallback to planning view
      return this.renderPlanningView(extraClass);
    }

    return html`
      <div class="navigation-panel nav-directions-view ${extraClass || ''}">
        <div class="nav-directions-header">
          <button
            class="nav-back-btn"
            @click=${() => this.setViewMode('planning')}
            title="Back to route planning"
          >
            ${renderIcon('arrowLeft')}
          </button>
          <div class="nav-directions-summary">
            ${renderIcon('ruler')}
            <span>${this.formatDistance(route.distance)}</span>
            <span class="nav-directions-separator">•</span>
            ${renderIcon('clock')}
            <span>${this.formatDuration(trafficRoute?.duration ?? route.duration)}</span>
          </div>
        </div>

        ${trafficRoute ? html`
          <div class="nav-traffic-info ${trafficRoute.congestionLevel}">
            <span class="nav-traffic-indicator ${trafficRoute.congestionLevel}"></span>
            <span class="nav-traffic-text">${this.getCongestionLabel(trafficRoute.congestionLevel)}</span>
            ${trafficRoute.duration > trafficRoute.durationWithoutTraffic ? html`
              <span class="nav-traffic-delay">
                +${this.formatDuration(trafficRoute.duration - trafficRoute.durationWithoutTraffic)} due to traffic
              </span>
            ` : ''}
          </div>
        ` : ''}

        <div class="nav-steps">
          ${this.renderTurnByTurn(route)}
        </div>
      </div>
    `;
  }

  /**
   * Render a navigation input field
   */
  private renderNavInput(
    inputType: 'start' | 'end',
    placeholder: string,
    query: string,
    results: INominatimResult[]
  ): TemplateResult {
    const hasValue = inputType === 'start'
      ? this.navigationState.startPoint !== null
      : this.navigationState.endPoint !== null;
    const isClickMode = this.navClickMode === inputType;

    return html`
      <div class="nav-input-group">
        <div class="nav-input-marker ${inputType}"></div>
        <div class="nav-input-wrapper">
          <input
            type="text"
            class="nav-input ${hasValue ? 'has-value' : ''}"
            placeholder="${placeholder}"
            .value=${query}
            @input=${(e: Event) => this.handleNavSearchInput(e, inputType)}
            @keydown=${(e: KeyboardEvent) => this.handleNavSearchKeydown(e, inputType)}
            @focus=${() => { this.navActiveInput = inputType; this.callbacks.onRequestUpdate(); }}
          />
          ${hasValue ? html`
            <button
              class="nav-input-clear"
              @click=${() => this.clearNavPoint(inputType)}
              title="Clear"
            >
              ${renderIcon('close')}
            </button>
          ` : ''}
          <button
            class="nav-set-map-btn ${isClickMode ? 'active' : ''}"
            @click=${() => this.toggleNavClickMode(inputType)}
            title="Click on map"
          >
            ${renderIcon('mapPin')}
          </button>

          ${results.length > 0 && this.navActiveInput === inputType ? html`
            <div class="nav-search-results">
              ${results.map((result, index) => html`
                <div
                  class="nav-search-result ${index === this.navHighlightedIndex ? 'highlighted' : ''}"
                  @click=${() => this.selectNavSearchResult(result, inputType)}
                  @mouseenter=${() => { this.navHighlightedIndex = index; this.callbacks.onRequestUpdate(); }}
                >
                  <span class="nav-search-result-name">${result.display_name}</span>
                  <span class="nav-search-result-type">${result.type}</span>
                </div>
              `)}
            </div>
          ` : ''}
        </div>
      </div>
    `;
  }

  /**
   * Render turn-by-turn directions
   */
  private renderTurnByTurn(route: IOSRMRoute): TemplateResult {
    if (!route.legs || route.legs.length === 0) {
      return html`<div class="nav-empty-steps">No turn-by-turn directions available</div>`;
    }

    const steps = route.legs.flatMap(leg => leg.steps);
    const guidanceState = this.callbacks.getGuidanceState?.();
    const isNavigating = guidanceState?.isNavigating ?? false;
    const currentStepIndex = guidanceState?.currentStepIndex ?? -1;

    if (steps.length === 0) {
      return html`<div class="nav-empty-steps">No turn-by-turn directions available</div>`;
    }

    return html`
      ${steps.map((step, index) => {
        const icon = this.getManeuverIcon(step.maneuver.type, step.maneuver.modifier);
        const instruction = this.formatStepInstruction(step);
        const distance = this.formatDistance(step.distance);

        // currentStepIndex points to the maneuver we're approaching,
        // but we're traveling on the PREVIOUS step's road
        const isCurrent = isNavigating && index === currentStepIndex - 1;
        const isCompleted = isNavigating && index < currentStepIndex - 1;

        // Calculate progress percentage for current step
        let progressPercent = 0;
        if (isCurrent && step.distance > 0) {
          const distanceRemaining = guidanceState?.distanceToNextManeuver ?? step.distance;
          progressPercent = Math.max(0, Math.min(100,
            ((step.distance - distanceRemaining) / step.distance) * 100
          ));
        }

        return html`
          <div
            class="nav-step ${isCurrent ? 'current' : ''} ${isCompleted ? 'completed' : ''}"
            @click=${() => this.flyToStep(step)}
            data-step-index="${index}"
          >
            ${isCurrent ? html`
              <div class="nav-step-progress-bar" style="width: ${progressPercent}%"></div>
            ` : ''}
            <div class="nav-step-icon">${isCompleted ? '✓' : icon}</div>
            <div class="nav-step-content">
              <div class="nav-step-instruction">${instruction}</div>
              <div class="nav-step-distance">${distance}</div>
            </div>
          </div>
        `;
      })}
    `;
  }

  /**
   * Scroll the turn-by-turn list to show the current step
   * Called externally when guidance state changes
   */
  public scrollToCurrentStep(stepIndex: number): void {
    // Use requestAnimationFrame to ensure DOM is updated
    requestAnimationFrame(() => {
      // Find elements in document - they may be in shadow DOM
      const stepsContainer = document.querySelector('.nav-steps')
        ?? document.querySelector('dees-geo-map')?.shadowRoot?.querySelector('.nav-steps');
      const currentStep = document.querySelector(`.nav-step[data-step-index="${stepIndex}"]`)
        ?? document.querySelector('dees-geo-map')?.shadowRoot?.querySelector(`.nav-step[data-step-index="${stepIndex}"]`);

      if (stepsContainer && currentStep) {
        (currentStep as HTMLElement).scrollIntoView({
          behavior: 'smooth',
          block: 'nearest',
        });
      }
    });
  }
}
