let pyrkMap = null;
let pyrkRouteLine = null;
let pyrkMarkers = [];
let pyrkPoints = [];
let pyrkSearchMarker = null;
let pyrkLastSearchedLatLng = null;
let pyrkRouteSearchBox = null;
let allRoutes = [];
let pendingPointType = 'checkpoint';
let pyrkMapProvider = window.PYRK_MAP_PROVIDER || 'google';
function canEditRoutes(){ return !!window.PYRK_CAN_EDIT_ROUTES; }
function canDeleteRoutes(){ return !!window.PYRK_CAN_DELETE_ROUTES; }

let routesViewMode = localStorage.getItem('pyrkRoutesViewMode') || 'grid';

function pointLabel(type) {
  return ({start:'Start', end:'Finish', checkpoint:'Checkpoint', water_station:'Water', medical_station:'Medical', rest_station:'Rest', station:'Station'}[type] || 'Point');
}

function pointCanRequireCheckin(type) {
  return ['checkpoint', 'station', 'water_station', 'medical_station', 'rest_station'].includes(String(type || ''));
}

function checkinRequirementControl(point, index) {
  if (!pointCanRequireCheckin(point.point_type)) {
    return '<span class="route-point-checkin-note">No station QR check-in</span>';
  }
  return `<label class="route-point-checkin-toggle"><input type="checkbox" data-index="${index}" data-field="is_required_checkpoint" ${point.is_required_checkpoint ? 'checked' : ''}> Requires QR check-in</label>`;
}

function defaultTitle(type, index) {
  if (type === 'start') return 'Start';
  if (type === 'end') return 'Finish';
  return `${pointLabel(type)} ${index}`;
}

function nextPointTypeNumber(type) {
  return pyrkPoints.filter(p => p.point_type === type).length + 1;
}

function refreshMarkerLabels() {
  pyrkMarkers.forEach((marker, index) => {
    if (!marker) return;
    const label = String(index + 1);
    if (pyrkMapProvider === 'osm' && marker.bindTooltip) {
      marker.bindTooltip(label, {permanent: true, direction: 'top'});
    } else if (marker.setLabel) {
      marker.setLabel(label);
    }
  });
}

function normalizeRoutePointNumbers() {
  const typeCounters = {};
  pyrkPoints = pyrkPoints.map((point, index) => {
    const type = point.point_type || 'checkpoint';
    typeCounters[type] = (typeCounters[type] || 0) + 1;

    const normalized = {
      ...point,
      point_type: type,
      sort_order: index + 1,
      is_required_checkpoint: pointCanRequireCheckin(type) && point.is_required_checkpoint ? 1 : 0,
      checkin_radius_meters: Number(point.checkin_radius_meters || 60),
      navigation_note: point.navigation_note || '',
      is_navigation_stop: Object.prototype.hasOwnProperty.call(point, 'is_navigation_stop') ? (point.is_navigation_stop ? 1 : 0) : (['start','checkpoint','end'].includes(type) ? 1 : 0)
    };

    if (type === 'checkpoint') {
      normalized.checkpoint_number = typeCounters[type];
    } else {
      delete normalized.checkpoint_number;
    }

    // Keep automatic titles clean when points are removed or re-ordered.
    const autoTitlePattern = /^(Checkpoint|Water|Medical|Rest|Station)\s+\d+$/i;
    if (!normalized.title || autoTitlePattern.test(String(normalized.title).trim())) {
      normalized.title = defaultTitle(type, typeCounters[type]);
    }

    return normalized;
  });
}

function updatePointsJsonField() {
  normalizeRoutePointNumbers();
  const pointsJson = document.getElementById('pointsJson');
  if (pointsJson) pointsJson.value = JSON.stringify(pyrkPoints, null, 2);
}

function difficultyIcon(difficulty) {
  return ({easy:'🌿', medium:'⛰️', hard:'🔥'}[difficulty] || '🥾');
}

function formatDuration(totalMinutes) {
  const minutes = Math.max(0, Number(totalMinutes || 0));
  if (!minutes) return 'Not set';
  if (minutes < 60) return `${minutes} min`;
  const hours = Math.floor(minutes / 60);
  const remaining = minutes % 60;
  const hourLabel = hours === 1 ? 'hr' : 'hrs';
  return remaining ? `${hours} ${hourLabel} ${remaining} min` : `${hours} ${hourLabel}`;
}

function updateDurationPreview() {
  const input = document.getElementById('durationMinutes');
  const preview = document.getElementById('durationPreview');
  if (!input || !preview) return;
  preview.textContent = `Shown as: ${formatDuration(input.value)}`;
}

function routeStatusClass(status) {
  return status === 'active' ? 'pill-green' : (status === 'archived' ? 'pill-muted' : 'pill-amber');
}

function readLatLng(raw) {
  if (raw && typeof raw.lat === 'function' && typeof raw.lng === 'function') {
    return {lat: raw.lat(), lng: raw.lng()};
  }
  return {lat: Number(raw.lat), lng: Number(raw.lng)};
}

function setGoogleSearchMarker(latLng, title = 'Searched location') {
  if (!window.google || !pyrkMap) return;
  if (pyrkSearchMarker && pyrkSearchMarker.setMap) pyrkSearchMarker.setMap(null);
  pyrkLastSearchedLatLng = readLatLng(latLng);
  pyrkSearchMarker = new google.maps.Marker({
    map: pyrkMap,
    position: pyrkLastSearchedLatLng,
    title,
    animation: google.maps.Animation.DROP
  });
  const addBtn = document.getElementById('addSearchedPlacePointBtn');
  if (addBtn) addBtn.disabled = false;
}

function setupGoogleRouteSearch() {
  const input = document.getElementById('routeMapSearch');
  if (!input || !window.google || !google.maps || !google.maps.places || !pyrkMap) return;
  if (pyrkRouteSearchBox) return;

  pyrkRouteSearchBox = new google.maps.places.SearchBox(input);
  pyrkMap.addListener('bounds_changed', () => {
    try { pyrkRouteSearchBox.setBounds(pyrkMap.getBounds()); } catch (e) {}
  });

  pyrkRouteSearchBox.addListener('places_changed', () => {
    const places = pyrkRouteSearchBox.getPlaces() || [];
    if (!places.length) return;

    const bounds = new google.maps.LatLngBounds();
    let firstPlace = null;

    places.forEach(place => {
      if (!place.geometry || !place.geometry.location) return;
      if (!firstPlace) firstPlace = place;
      if (place.geometry.viewport) bounds.union(place.geometry.viewport);
      else bounds.extend(place.geometry.location);
    });

    if (!firstPlace) return;
    if (!bounds.isEmpty()) pyrkMap.fitBounds(bounds);
    pyrkMap.setZoom(Math.min(pyrkMap.getZoom() || 16, 17));
    setGoogleSearchMarker(firstPlace.geometry.location, firstPlace.name || input.value || 'Searched location');
  });
}

function addSearchedPlaceAsPoint() {
  if (!pyrkLastSearchedLatLng) return;
  addRoutePoint(pendingPointType || 'checkpoint', pyrkLastSearchedLatLng);
  const addBtn = document.getElementById('addSearchedPlacePointBtn');
  if (addBtn) addBtn.disabled = true;
}

window.initPyrkRouteBuilder = function initPyrkRouteBuilder() {
  const center = window.PYRK_MAP_CENTER || {lat: 33.8938, lng: 35.5018};
  if (!document.getElementById('routeBuilderMap')) return;

  if (pyrkMapProvider === 'osm' && window.L) {
    if (pyrkMap) return;
    pyrkMap = L.map('routeBuilderMap').setView([center.lat, center.lng], 14);
    L.tileLayer(window.PYRK_OSM_TILE_URL || 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
      maxZoom: 19,
      attribution: window.PYRK_OSM_ATTRIBUTION || '&copy; OpenStreetMap contributors'
    }).addTo(pyrkMap);
    pyrkRouteLine = L.polyline([], {weight: 5, opacity: 0.9}).addTo(pyrkMap);
    pyrkMap.on('click', (event) => addRoutePoint(pendingPointType, event.latlng));
    return;
  }

  if (window.google) {
    if (pyrkMap) return;
    pyrkMap = new google.maps.Map(document.getElementById('routeBuilderMap'), {
      center, zoom: 14, mapTypeControl: false, streetViewControl: false, fullscreenControl: true
    });
    pyrkRouteLine = new google.maps.Polyline({map: pyrkMap, path: [], strokeOpacity: .9, strokeWeight: 5});
    pyrkMap.addListener('click', (event) => addRoutePoint(pendingPointType, event.latLng));
    setupGoogleRouteSearch();
  }
};

function createMarker(point, index) {
  if (!pyrkMap || !point.latitude || !point.longitude) return null;
  if (pyrkMapProvider === 'osm' && window.L) {
    const marker = L.marker([point.latitude, point.longitude], {draggable: true}).addTo(pyrkMap);
    marker.bindTooltip(String(index + 1), {permanent: true, direction: 'top'});
    marker.on('dragend', () => {
      const pos = marker.getLatLng();
      point.latitude = Number(pos.lat.toFixed(7));
      point.longitude = Number(pos.lng.toFixed(7));
      syncRouteBuilder();
    });
    return marker;
  }
  if (window.google) {
    const marker = new google.maps.Marker({map: pyrkMap, position: {lat:Number(point.latitude), lng:Number(point.longitude)}, draggable: true, label: String(index + 1), title: point.title});
    marker.addListener('dragend', () => {
      const pos = marker.getPosition();
      point.latitude = Number(pos.lat().toFixed(7));
      point.longitude = Number(pos.lng().toFixed(7));
      syncRouteBuilder();
    });
    return marker;
  }
  return null;
}

function clearMarkers() {
  pyrkMarkers.forEach(m => {
    if (!m) return;
    if (pyrkMapProvider === 'osm' && m.remove) m.remove();
    else if (m.setMap) m.setMap(null);
  });
  pyrkMarkers = [];
}

function rebuildMarkers() {
  clearMarkers();
  pyrkMarkers = pyrkPoints.map((p, i) => createMarker(p, i));
}

function addRoutePoint(type, latLngRaw) {
  const latLng = readLatLng(latLngRaw);
  const typeNumber = nextPointTypeNumber(type);
  const point = {
    point_type: type,
    title: defaultTitle(type, typeNumber),
    description: '',
    latitude: Number(latLng.lat.toFixed(7)),
    longitude: Number(latLng.lng.toFixed(7)),
    sort_order: pyrkPoints.length + 1,
    is_required_checkpoint: type === 'checkpoint' ? 1 : 0,
    checkin_radius_meters: 60,
    navigation_note: '',
    is_navigation_stop: ['start','checkpoint','end'].includes(type) ? 1 : 0,
  };
  if (type === 'checkpoint') point.checkpoint_number = typeNumber;
  pyrkPoints.push(point);
  pyrkMarkers.push(createMarker(point, pyrkPoints.length - 1));
  syncRouteBuilder();
}

function drawLineFromPoints() {
  const path = pyrkPoints.filter(p => p.latitude && p.longitude).map(p => ({lat: Number(p.latitude), lng: Number(p.longitude)}));
  if (!pyrkRouteLine) return;
  if (pyrkMapProvider === 'osm' && window.L) {
    pyrkRouteLine.setLatLngs(path.map(p => [p.lat, p.lng]));
    if (path.length > 1) pyrkMap.fitBounds(L.latLngBounds(path.map(p => [p.lat, p.lng])), {padding: [30, 30]});
  } else if (window.google) {
    pyrkRouteLine.setPath(path);
    if (path.length > 1) {
      const bounds = new google.maps.LatLngBounds();
      path.forEach(p => bounds.extend(p));
      pyrkMap.fitBounds(bounds);
    }
  }
}


function normalizeRoutePointOrder() {
  let checkpointNo = 1;
  pyrkPoints.forEach((point, index) => {
    point.sort_order = index + 1;
    if (point.point_type === 'checkpoint') {
      point.checkpoint_number = checkpointNo++;
    } else if (Object.prototype.hasOwnProperty.call(point, 'checkpoint_number')) {
      delete point.checkpoint_number;
    }
  });
}

function moveRoutePoint(fromIndex, toIndex) {
  fromIndex = Number(fromIndex);
  toIndex = Number(toIndex);
  if (Number.isNaN(fromIndex) || Number.isNaN(toIndex)) return;
  if (fromIndex < 0 || fromIndex >= pyrkPoints.length || toIndex < 0 || toIndex >= pyrkPoints.length || fromIndex === toIndex) return;
  const [point] = pyrkPoints.splice(fromIndex, 1);
  pyrkPoints.splice(toIndex, 0, point);
  normalizeRoutePointOrder();
  rebuildMarkers();
  syncRouteBuilder();
}

window.moveRoutePoint = moveRoutePoint;

function attachRoutePointDragHandlers() {
  let dragIndex = null;
  document.querySelectorAll('#routePointsList .route-point-row').forEach(row => {
    row.addEventListener('dragstart', e => {
      dragIndex = Number(row.dataset.pointIndex);
      row.classList.add('is-dragging');
      e.dataTransfer.effectAllowed = 'move';
      e.dataTransfer.setData('text/plain', String(dragIndex));
    });
    row.addEventListener('dragover', e => {
      e.preventDefault();
      row.classList.add('drag-over');
      e.dataTransfer.dropEffect = 'move';
    });
    row.addEventListener('dragleave', () => row.classList.remove('drag-over'));
    row.addEventListener('drop', e => {
      e.preventDefault();
      row.classList.remove('drag-over');
      const from = Number(e.dataTransfer.getData('text/plain') || dragIndex);
      const to = Number(row.dataset.pointIndex);
      moveRoutePoint(from, to);
    });
    row.addEventListener('dragend', () => {
      row.classList.remove('is-dragging');
      document.querySelectorAll('#routePointsList .route-point-row.drag-over').forEach(el => el.classList.remove('drag-over'));
    });
  });
}

function syncRouteBuilder() {
  normalizeRoutePointOrder();
  updatePointsJsonField();
  refreshMarkerLabels();
  drawLineFromPoints();
  const start = pyrkPoints.find(p => p.point_type === 'start') || pyrkPoints[0];
  const end = [...pyrkPoints].reverse().find(p => p.point_type === 'end') || pyrkPoints[pyrkPoints.length - 1];
  if (start && !document.getElementById('startAddress').value) document.getElementById('startAddress').value = `${start.latitude}, ${start.longitude}`;
  if (end && !document.getElementById('endAddress').value) document.getElementById('endAddress').value = `${end.latitude}, ${end.longitude}`;
  document.getElementById('routePointsList').innerHTML = pyrkPoints.map((p, i) => `
    <div class="route-point-row route-point-row-draggable" draggable="true" data-point-index="${i}">
      <span class="drag-handle" title="Drag to reorder">⋮⋮</span>
      <strong>${i+1}. ${Pyrk.esc(pointLabel(p.point_type))}${p.checkpoint_number ? ` #${p.checkpoint_number}` : ''}</strong>
      <input value="${Pyrk.esc(p.title)}" data-index="${i}" data-field="title" aria-label="Point title">
      <select data-index="${i}" data-field="point_type">
        ${['start','checkpoint','water_station','medical_station','rest_station','station','end'].map(t => `<option value="${t}" ${p.point_type===t?'selected':''}>${pointLabel(t)}</option>`).join('')}
      </select>
      ${checkinRequirementControl(p, i)}
      <label><input type="checkbox" data-index="${i}" data-field="is_navigation_stop" ${Object.prototype.hasOwnProperty.call(p, 'is_navigation_stop') ? (p.is_navigation_stop ? 'checked' : '') : (['start','checkpoint','end'].includes(p.point_type) ? 'checked' : '')}> Navigation stop</label>
      <label class="route-point-radius-field">Radius (m)<input type="number" min="10" max="500" step="5" value="${Number(p.checkin_radius_meters || 60)}" data-index="${i}" data-field="checkin_radius_meters" aria-label="Check-in radius meters"></label>
      <input value="${Pyrk.esc(p.navigation_note || '')}" data-index="${i}" data-field="navigation_note" aria-label="Navigation note" placeholder="Optional client note">
      <div class="route-point-actions">
        <button type="button" class="btn btn-light btn-small" onclick="moveRoutePoint(${i}, ${i-1})" ${i === 0 ? 'disabled' : ''}>↑</button>
        <button type="button" class="btn btn-light btn-small" onclick="moveRoutePoint(${i}, ${i+1})" ${i === pyrkPoints.length - 1 ? 'disabled' : ''}>↓</button>
        <button type="button" class="btn btn-light btn-small" onclick="removeRoutePoint(${i})">Remove</button>
      </div>
    </div>`).join('') || '<p class="muted">No points yet. Choose a point type and click the map.</p>';
  document.querySelectorAll('#routePointsList input, #routePointsList select').forEach(el => {
    el.addEventListener('change', (e) => {
      const idx = Number(e.target.dataset.index);
      const field = e.target.dataset.field;
      if (field === 'is_required_checkpoint' || field === 'is_navigation_stop') pyrkPoints[idx][field] = e.target.checked ? 1 : 0;
      else if (field === 'checkin_radius_meters') pyrkPoints[idx][field] = Math.min(500, Math.max(10, Number(e.target.value || 60)));
      else {
        pyrkPoints[idx][field] = e.target.value;
        if (field === 'point_type') {
          if (!pointCanRequireCheckin(e.target.value)) pyrkPoints[idx].is_required_checkpoint = 0;
          else if (e.target.value === 'checkpoint') pyrkPoints[idx].is_required_checkpoint = 1;
        }
      }
      if (pyrkMarkers[idx] && pyrkMarkers[idx].setTitle) pyrkMarkers[idx].setTitle(pyrkPoints[idx].title);
      syncRouteBuilder();
    });
  });
  attachRoutePointDragHandlers();
}

window.removeRoutePoint = function removeRoutePoint(index) {
  const marker = pyrkMarkers[index];
  if (marker) {
    if (pyrkMapProvider === 'osm' && marker.remove) marker.remove();
    else if (marker.setMap) marker.setMap(null);
  }
  pyrkPoints.splice(index, 1);
  pyrkMarkers.splice(index, 1);
  normalizeRoutePointOrder();
  rebuildMarkers();
  syncRouteBuilder();
};

function drawCalculatedGeometry(calc) {
  if (!pyrkRouteLine || !calc) return;
  let coords = [];
  if (calc.geometry_geojson) {
    try {
      const geo = typeof calc.geometry_geojson === 'string' ? JSON.parse(calc.geometry_geojson) : calc.geometry_geojson;
      coords = (geo.coordinates || []).map(pair => ({lat: Number(pair[1]), lng: Number(pair[0])}));
    } catch {}
  }
  if (pyrkMapProvider === 'osm' && window.L) {
    if (!coords.length) coords = pyrkPoints.map(p => ({lat: Number(p.latitude), lng: Number(p.longitude)}));
    pyrkRouteLine.setLatLngs(coords.map(p => [p.lat, p.lng]));
    if (coords.length) pyrkMap.fitBounds(L.latLngBounds(coords.map(p => [p.lat, p.lng])), {padding: [20, 20]});
  } else if (window.google && calc.encoded_polyline && google.maps.geometry) {
    const decoded = google.maps.geometry.encoding.decodePath(calc.encoded_polyline);
    pyrkRouteLine.setPath(decoded);
    const bounds = new google.maps.LatLngBounds();
    decoded.forEach(p => bounds.extend(p));
    pyrkMap.fitBounds(bounds);
  }
}

async function calculateRoute() {
  const msg = document.getElementById('routeMsg');
  updatePointsJsonField();
  let points = pyrkPoints;
  if (!points.length) {
    try { points = JSON.parse(document.getElementById('pointsJson').value || '[]'); } catch { points = []; }
  }
  if (points.length < 2) { msg.textContent = 'Add at least start and finish points.'; msg.className = 'notice bad'; return; }
  msg.textContent = 'Calculating route...'; msg.className = 'notice';
  const res = await Pyrk.post('routes.php', {action:'compute_route', points_json: JSON.stringify(points)});
  Pyrk.toast('routeMsg', res);
  if (!res.ok) return;
  const calc = res.data.route_calc;
  document.getElementById('totalKm').value = calc.distance_km || '';
  document.getElementById('durationMinutes').value = calc.duration_minutes || '';
  updateDurationPreview();
  document.getElementById('googlePolyline').value = calc.geometry_geojson || calc.encoded_polyline || '';
  document.getElementById('mapBoundsJson').value = calc.bounds ? JSON.stringify(calc.bounds) : '';
  drawCalculatedGeometry(calc);
}

function renderRoutes(){
  const searchInput = document.getElementById('routeSearch');
  const difficultySelect = document.getElementById('difficultyFilter');
  const statusSelect = document.getElementById('statusFilter');
  const q = (searchInput?.value || '').toLowerCase().trim();
  const diff = difficultySelect?.value || '';
  const status = statusSelect?.value || '';
  const routes = allRoutes.filter(r => {
    const hay = `${r.route_name || ''} ${r.description || ''} ${r.start_address || ''} ${r.end_address || ''}`.toLowerCase();
    return (!q || hay.includes(q)) && (!diff || r.difficulty === diff) && (!status || r.status === status);
  });

  const countEl = document.getElementById('routesCount');
  if (countEl) countEl.textContent = `${routes.length} of ${allRoutes.length} routes`;

  const list = document.getElementById('routesList');
  if (!routes.length) {
    list.className = 'route-card-grid';
    list.innerHTML = '<p class="muted empty-state">No routes match your filters. Create a new route below or clear your search.</p>';
    return;
  }

  if (routesViewMode === 'table') {
    list.className = 'routes-table-shell';
    list.innerHTML = `
      <div class="routes-table-wrap">
        <table class="pro-table routes-table">
          <thead>
            <tr>
              <th>Route</th>
              <th>Distance</th>
              <th>Duration</th>
              <th>Difficulty</th>
              <th>Points</th>
              <th>Walks</th>
              <th>Status</th>
              <th>Actions</th>
            </tr>
          </thead>
          <tbody>
            ${routes.map(r => `
              <tr>
                <td>
                  <div class="route-table-name">
                    <span class="route-icon route-icon-small">${difficultyIcon(r.difficulty)}</span>
                    <div>
                      <strong>${Pyrk.esc(r.route_name)}</strong>
                      <small>${Pyrk.esc(r.start_address || 'Start not set')} → ${Pyrk.esc(r.end_address || 'Finish not set')}</small>
                    </div>
                  </div>
                </td>
                <td><strong>${Number(r.total_km || 0).toFixed(2)}</strong> km</td>
                <td>${Pyrk.esc(formatDuration(r.expected_duration_minutes))}</td>
                <td>${Pyrk.esc(r.difficulty || 'easy')}</td>
                <td>${Number(r.points_count || 0)}</td>
                <td>${Number(r.walks_count || 0)}</td>
                <td><span class="pill ${routeStatusClass(r.status)}">${Pyrk.esc(r.status || 'active')}</span></td>
                <td><div class="table-actions">${canEditRoutes() ? `<button class="btn btn-light btn-small route-duplicate-btn" type="button" onclick="duplicateRoute(${r.id})">Duplicate</button><button class="btn btn-small" type="button" onclick="editRoute(${r.id})">Edit</button>` : ``}<button class="btn btn-light btn-small" type="button" onclick="viewRoute(${r.id})">Points</button>${canDeleteRoutes() ? `<button class="btn btn-danger btn-small" type="button" onclick="deleteRoute(${r.id})">Delete</button>` : ``}</div></td>
              </tr>
              <tr class="route-points-table-row"><td colspan="8"><div class="route-card-points" id="routePoints-${r.id}" hidden></div></td></tr>
            `).join('')}
          </tbody>
        </table>
      </div>`;
    return;
  }

  list.className = 'route-card-grid';
  list.innerHTML = routes.map(r=>`
    <article class="route-card route-card-pro">
      <div class="route-card-top">
        <span class="route-icon">${difficultyIcon(r.difficulty)}</span>
        <div><h3>${Pyrk.esc(r.route_name)}</h3><p>${Pyrk.esc(r.description || 'No description yet.')}</p></div>
        <span class="pill ${routeStatusClass(r.status)}">${Pyrk.esc(r.status || 'active')}</span>
      </div>
      <div class="route-path-visual"><span></span><i></i><span></span><i></i><span></span></div>
      <div class="route-card-meta">
        <div><strong>${Number(r.total_km || 0).toFixed(2)}</strong><span>KM</span></div>
        <div><strong>${Pyrk.esc(formatDuration(r.expected_duration_minutes))}</strong><span>Duration</span></div>
        <div><strong>${Number(r.points_count || 0)}</strong><span>Points</span></div>
        <div><strong>${Number(r.walks_count || 0)}</strong><span>Active walks</span></div>
      </div>
      <p class="route-from-to"><strong>Start:</strong> ${Pyrk.esc(r.start_address || '')}<br><strong>Finish:</strong> ${Pyrk.esc(r.end_address || '')}</p>
      <div class="inline-controls route-card-actions">${canEditRoutes() ? `<button class="btn btn-light btn-small route-duplicate-btn" type="button" onclick="duplicateRoute(${r.id})">Duplicate</button><button class="btn btn-small" type="button" onclick="editRoute(${r.id})">Edit route</button>` : ``}<button class="btn btn-light btn-small" type="button" onclick="viewRoute(${r.id})">View points</button>${canDeleteRoutes() ? `<button class="btn btn-danger btn-small" type="button" onclick="deleteRoute(${r.id})">Delete</button>` : ``}</div>
      <div class="route-card-points" id="routePoints-${r.id}" hidden></div>
    </article>`).join('');
}

async function loadRoutes(){
 const res=await Pyrk.get('routes.php');
 if (!res.ok) { document.getElementById('routesList').innerHTML = `<p class="bad">${Pyrk.esc(res.message)}</p>`; return; }
 allRoutes = res.data.routes || [];
 renderRoutes();
}

window.viewRoute = async function viewRoute(id) {
  const box = document.getElementById(`routePoints-${id}`);
  if (!box) return;
  if (!box.hidden) { box.hidden = true; return; }
  const res = await Pyrk.get('routes.php', {id});
  if (!res.ok) { alert(res.message); return; }
  const points = res.data.points || [];
  box.innerHTML = points.map((p, i) => `<div><strong>${i+1}. ${Pyrk.esc(pointLabel(p.point_type))}</strong><span>${Pyrk.esc(p.title)} · ${Pyrk.esc(p.latitude)}, ${Pyrk.esc(p.longitude)}${Number(p.is_required_checkpoint || 0) === 1 ? ' · QR check-in required · radius ' + Number(p.checkin_radius_meters || 60) + 'm' : ''}</span></div>`).join('') || '<p>No points saved.</p>';
  box.hidden = false;
};



function suggestedDuplicateRouteName(name) {
  const cleanName = String(name || 'Route').trim() || 'Route';
  return `${cleanName} - Copy`;
}

function setRouteDuplicateMessage(message = '', type = '') {
  const box = document.getElementById('routeDuplicateMsg');
  if (!box) return;
  box.textContent = message;
  box.className = `notice${type ? ` ${type}` : ''}`;
  box.hidden = !message;
}

function closeRouteDuplicateModal() {
  const modal = document.getElementById('routeDuplicateModal');
  if (modal) modal.hidden = true;
  document.body.classList.remove('modal-open');
  setRouteDuplicateMessage();
}

window.duplicateRoute = function duplicateRoute(id) {
  if (!canEditRoutes()) {
    alert('You do not have permission to duplicate routes.');
    return;
  }

  const route = allRoutes.find(item => Number(item.id) === Number(id));
  if (!route) {
    alert('Route not found. Refresh the page and try again.');
    return;
  }

  const modal = document.getElementById('routeDuplicateModal');
  const sourceId = document.getElementById('routeDuplicateSourceId');
  const sourceName = document.getElementById('routeDuplicateSourceName');
  const sourceMeta = document.getElementById('routeDuplicateSourceMeta');
  const nameInput = document.getElementById('routeDuplicateName');
  if (!modal || !sourceId || !sourceName || !sourceMeta || !nameInput) return;

  sourceId.value = String(route.id);
  sourceName.textContent = route.route_name || 'Route';
  sourceMeta.textContent = `${Number(route.total_km || 0).toFixed(2)} km · ${Number(route.points_count || 0)} points · ${String(route.status || 'active')}`;
  nameInput.value = suggestedDuplicateRouteName(route.route_name);
  setRouteDuplicateMessage();

  modal.hidden = false;
  document.body.classList.add('modal-open');
  window.setTimeout(() => {
    nameInput.focus();
    nameInput.select();
  }, 40);
};

const routeDuplicateModal = document.getElementById('routeDuplicateModal');
const routeDuplicateForm = document.getElementById('routeDuplicateForm');
const closeRouteDuplicateBtn = document.getElementById('closeRouteDuplicateBtn');
const cancelRouteDuplicateBtn = document.getElementById('cancelRouteDuplicateBtn');

if (closeRouteDuplicateBtn) closeRouteDuplicateBtn.addEventListener('click', closeRouteDuplicateModal);
if (cancelRouteDuplicateBtn) cancelRouteDuplicateBtn.addEventListener('click', closeRouteDuplicateModal);
if (routeDuplicateModal) {
  routeDuplicateModal.addEventListener('click', event => {
    if (event.target === routeDuplicateModal) closeRouteDuplicateModal();
  });
}

document.addEventListener('keydown', event => {
  if (event.key === 'Escape' && routeDuplicateModal && !routeDuplicateModal.hidden) {
    closeRouteDuplicateModal();
  }
});

if (routeDuplicateForm) {
  routeDuplicateForm.addEventListener('submit', async event => {
    event.preventDefault();
    if (!canEditRoutes()) return;

    const sourceId = Number(document.getElementById('routeDuplicateSourceId')?.value || 0);
    const routeName = String(document.getElementById('routeDuplicateName')?.value || '').trim();
    const submitBtn = document.getElementById('confirmRouteDuplicateBtn');

    if (!sourceId) {
      setRouteDuplicateMessage('The source route is missing. Close this window and try again.', 'bad');
      return;
    }
    if (!routeName) {
      setRouteDuplicateMessage('Enter a name for the duplicated route.', 'bad');
      document.getElementById('routeDuplicateName')?.focus();
      return;
    }

    if (submitBtn) {
      submitBtn.disabled = true;
      submitBtn.dataset.originalText = submitBtn.textContent;
      submitBtn.textContent = 'Duplicating…';
    }
    setRouteDuplicateMessage('Copying the route and all route points…');

    try {
      const res = await Pyrk.post('routes.php', {
        action: 'duplicate',
        id: sourceId,
        route_name: routeName
      });

      if (!res.ok) {
        setRouteDuplicateMessage(res.message || 'Could not duplicate route.', 'bad');
        return;
      }

      const duplicateId = Number(res.data?.route_id || 0);
      closeRouteDuplicateModal();
      await loadRoutes();

      if (duplicateId) {
        await window.editRoute(duplicateId);
        Pyrk.toast('routeMsg', res);
      }
    } catch (error) {
      setRouteDuplicateMessage(error?.message || 'Could not duplicate route.', 'bad');
    } finally {
      if (submitBtn) {
        submitBtn.disabled = false;
        submitBtn.textContent = submitBtn.dataset.originalText || 'Duplicate & edit';
        delete submitBtn.dataset.originalText;
      }
    }
  });
}

window.deleteRoute = async function deleteRoute(id) {
  if (!canDeleteRoutes()) { alert('You do not have permission to delete routes.'); return; }
  const route = allRoutes.find(r => Number(r.id) === Number(id));
  const name = route ? route.route_name : 'this route';
  const confirmed = confirm(`Delete ${name} permanently?\n\nOnly active/non-deleted walks block deletion. Deleted/cancelled walks will not block deleting the route.`);
  if (!confirmed) return;
  const res = await Pyrk.post('routes.php', {action:'delete', id});
  alert(res.message || (res.ok ? 'Deleted' : 'Error'));
  if (res.ok) await loadRoutes();
};

window.editRoute = async function editRoute(id) {
  const res = await Pyrk.get('routes.php', {id});
  if (!res.ok || !res.data.route) { alert(res.message || 'Route not found.'); return; }
  const r = res.data.route;
  document.getElementById('routeFormTitle').textContent = 'Edit route';
  document.getElementById('routeId').value = r.id || '';
  document.getElementById('routeName').value = r.route_name || '';
  document.getElementById('routeDescription').value = r.description || '';
  document.getElementById('startAddress').value = r.start_address || '';
  document.getElementById('endAddress').value = r.end_address || '';
  document.getElementById('totalKm').value = r.total_km || '';
  document.getElementById('durationMinutes').value = r.expected_duration_minutes || 60;
  updateDurationPreview();
  document.getElementById('difficulty').value = r.difficulty || 'easy';
  document.getElementById('routeStatus').value = r.status || 'active';
  document.getElementById('googlePolyline').value = r.google_polyline || '';
  document.getElementById('mapBoundsJson').value = r.map_bounds_json || '';
  pyrkPoints = (res.data.points || []).map((p, i) => ({
    point_type: p.point_type,
    title: p.title,
    description: p.description || '',
    latitude: Number(p.latitude),
    longitude: Number(p.longitude),
    sort_order: Number(p.sort_order || (i + 1)),
    is_required_checkpoint: Number(p.is_required_checkpoint || 0),
    checkin_radius_meters: Number(p.checkin_radius_meters || 60),
    navigation_note: p.navigation_note || '',
    is_navigation_stop: Object.prototype.hasOwnProperty.call(p, 'is_navigation_stop') ? Number(p.is_navigation_stop || 0) : (['start','checkpoint','end'].includes(p.point_type) ? 1 : 0),
    ...(p.point_type === 'checkpoint' ? {checkpoint_number: Number(p.checkpoint_number || 0)} : {})
  }));
  rebuildMarkers();
  syncRouteBuilder();
  document.getElementById('routeEditor').scrollIntoView({behavior:'smooth', block:'start'});
  setTimeout(() => { if (pyrkMap && pyrkMap.invalidateSize) pyrkMap.invalidateSize(); drawLineFromPoints(); }, 300);
};

function resetRouteForm() {
  document.getElementById('routeForm').reset();
  document.getElementById('routeId').value = '';
  document.getElementById('routeStatus').value = 'active';
  document.getElementById('durationMinutes').value = 60;
  updateDurationPreview();
  document.getElementById('routeFormTitle').textContent = 'Create route';
  clearMarkers();
  pyrkPoints = [];
  if (pyrkRouteLine) {
    if (pyrkMapProvider === 'osm' && pyrkRouteLine.setLatLngs) pyrkRouteLine.setLatLngs([]);
    else if (pyrkRouteLine.setPath) pyrkRouteLine.setPath([]);
  }
  document.getElementById('pointsJson').value = '[]';
  document.getElementById('googlePolyline').value = '';
  document.getElementById('mapBoundsJson').value = '';
  syncRouteBuilder();
}

document.querySelectorAll('[data-point-type]').forEach(btn => btn.addEventListener('click', e => {
  pendingPointType = e.currentTarget.dataset.pointType;
  document.querySelectorAll('[data-point-type]').forEach(b => b.classList.remove('active'));
  e.currentTarget.classList.add('active');
}));

document.getElementById('resetRouteBtn').addEventListener('click', () => {
  clearMarkers();
  pyrkPoints = [];
  if (pyrkRouteLine) {
    if (pyrkMapProvider === 'osm' && pyrkRouteLine.setLatLngs) pyrkRouteLine.setLatLngs([]);
    else if (pyrkRouteLine.setPath) pyrkRouteLine.setPath([]);
  }
  document.getElementById('pointsJson').value = '[]';
  document.getElementById('googlePolyline').value = '';
  document.getElementById('mapBoundsJson').value = '';
  syncRouteBuilder();
});

document.getElementById('newRouteBtn').addEventListener('click', resetRouteForm);
document.getElementById('calculateRouteBtn').addEventListener('click', calculateRoute);
document.getElementById('routeSearch').addEventListener('input', renderRoutes);
document.getElementById('difficultyFilter').addEventListener('change', renderRoutes);
const statusFilter = document.getElementById('statusFilter');
if (statusFilter) statusFilter.addEventListener('change', renderRoutes);
const durationInput = document.getElementById('durationMinutes');
if (durationInput) durationInput.addEventListener('input', updateDurationPreview);
document.querySelectorAll('[data-route-view]').forEach(btn => btn.addEventListener('click', e => {
  routesViewMode = e.currentTarget.dataset.routeView || 'grid';
  localStorage.setItem('pyrkRoutesViewMode', routesViewMode);
  document.querySelectorAll('[data-route-view]').forEach(b => b.classList.toggle('active', b.dataset.routeView === routesViewMode));
  renderRoutes();
}));
document.querySelectorAll('[data-route-view]').forEach(b => b.classList.toggle('active', b.dataset.routeView === routesViewMode));
updateDurationPreview();

document.getElementById('routeForm').addEventListener('submit', async e=>{
  e.preventDefault();
  updatePointsJsonField();
  const data=Object.fromEntries(new FormData(e.target));
  data.points_json = JSON.stringify(pyrkPoints);
  if (data.id) data.action = 'update';
  const res=await Pyrk.post('routes.php', data);
  Pyrk.toast('routeMsg',res);
  if(res.ok){ resetRouteForm(); loadRoutes(); }
});

loadRoutes();
syncRouteBuilder();

if (pyrkMapProvider === 'osm') {
  document.addEventListener('DOMContentLoaded', () => {
    if (!pyrkMap) window.initPyrkRouteBuilder();
  });
}
