// Hard safety for cached api.js on shared hosting.
if (!window.Pyrk) window.Pyrk = {};
if (typeof Pyrk.openModal !== 'function') {
  Pyrk.openModal = id => {
    const modal = document.getElementById(id);
    if (!modal) return;
    modal.hidden = false;
    modal.setAttribute('aria-hidden', 'false');
    document.body.classList.add('modal-open');
  };
}
if (typeof Pyrk.closeModal !== 'function') {
  Pyrk.closeModal = id => {
    const modal = document.getElementById(id);
    if (!modal) return;
    modal.hidden = true;
    modal.setAttribute('aria-hidden', 'true');
    document.body.classList.remove('modal-open');
  };
}
if (typeof Pyrk.formatDate !== 'function') {
  Pyrk.formatDate = value => {
    if (!value) return '—';
    const s = String(value).trim();
    const m = s.match(/^(\d{4})-(\d{2})-(\d{2})/);
    return m ? `${m[3]}/${m[2]}/${m[1]}` : s;
  };
}


function pyrkOpenModalSafe(id) {
  if (typeof Pyrk !== 'undefined' && typeof Pyrk.openModal === 'function') {
    Pyrk.openModal(id);
    return;
  }
  const modal = document.getElementById(id);
  if (!modal) return;
  modal.hidden = false;
  modal.setAttribute('aria-hidden', 'false');
  document.body.classList.add('modal-open');
}

function pyrkCloseModalSafe(id) {
  if (typeof Pyrk !== 'undefined' && typeof Pyrk.closeModal === 'function') {
    Pyrk.closeModal(id);
    return;
  }
  const modal = document.getElementById(id);
  if (!modal) return;
  modal.hidden = true;
  modal.setAttribute('aria-hidden', 'true');
  document.body.classList.remove('modal-open');
}


let allWalks = [];
const requestedWalkListMode = String(window.PYRK_WALK_LIST_MODE || '').toLowerCase();
const normalizedWalkListMode = requestedWalkListMode === 'finished' ? 'closed' : (requestedWalkListMode === 'active' ? 'upcoming' : requestedWalkListMode);
let walkArchiveView = ['upcoming','open','closed','archived'].includes(normalizedWalkListMode) ? normalizedWalkListMode : 'upcoming';
let walkRoutes = [];
let routePickerOpen = false;
let walkPushPresets = [];
let walkPushPresetUseCases = {};
let walkPushPresetsInstalled = false;
let walkPushPresetsLoaded = false;
let walkWhatsAppPresets = [];
let walkWhatsAppPresetUseCases = {};
let walkWhatsAppPresetsInstalled = false;
let walkWhatsAppPresetsLoaded = false;



function isoToDdMmYyyy(value) {
  if (!value) return '';
  const s = String(value).trim();
  const m = s.match(/^(\d{4})-(\d{2})-(\d{2})/);
  if (!m) return s;
  return `${m[3]}/${m[2]}/${m[1]}`;
}

function ddMmYyyyToIso(value) {
  const raw = String(value || '').trim();
  if (!raw) return '';
  const iso = raw.match(/^(\d{4})-(\d{2})-(\d{2})$/);
  if (iso) return raw;
  const m = raw.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/);
  if (!m) return '';
  const d = Number(m[1]);
  const mo = Number(m[2]);
  const y = Number(m[3]);
  const date = new Date(y, mo - 1, d);
  if (date.getFullYear() !== y || date.getMonth() !== mo - 1 || date.getDate() !== d) return '';
  return `${y}-${String(mo).padStart(2, '0')}-${String(d).padStart(2, '0')}`;
}

function isoDateTimeToDdMmYyyyHm(value) {
  if (!value) return '';
  const s = String(value).replace('T', ' ').trim();
  const m = s.match(/^(\d{4})-(\d{2})-(\d{2})(?:\s+(\d{2}):(\d{2}))?/);
  if (!m) return s;
  return `${m[3]}/${m[2]}/${m[1]}${m[4] ? ' ' + m[4] + ':' + (m[5] || '00') : ''}`;
}

function ddMmYyyyHmToIsoDateTime(value) {
  const raw = String(value || '').trim();
  if (!raw) return '';
  const iso = raw.match(/^(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2}))?/);
  if (iso) return iso[4] ? `${iso[1]}-${iso[2]}-${iso[3]} ${iso[4]}:${iso[5] || '00'}:00` : `${iso[1]}-${iso[2]}-${iso[3]}`;
  const m = raw.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})(?:\s+(\d{1,2}):(\d{2}))?$/);
  if (!m) return '';
  const d = Number(m[1]);
  const mo = Number(m[2]);
  const y = Number(m[3]);
  const h = m[4] === undefined ? 0 : Number(m[4]);
  const min = m[5] === undefined ? 0 : Number(m[5]);
  const date = new Date(y, mo - 1, d, h, min);
  if (date.getFullYear() !== y || date.getMonth() !== mo - 1 || date.getDate() !== d || date.getHours() !== h || date.getMinutes() !== min) return '';
  return `${y}-${String(mo).padStart(2, '0')}-${String(d).padStart(2, '0')}${m[4] !== undefined ? ' ' + String(h).padStart(2, '0') + ':' + String(min).padStart(2, '0') + ':00' : ''}`;
}

function prettyRoute(route) {
  if (!route) return '';
  return `${route.route_name || 'Route'} · ${Number(route.total_km || 0).toFixed(1)} KM · ${route.difficulty || ''}`;
}

function selectedRoute() {
  const id = Number(document.getElementById('routeSelect')?.value || 0);
  return walkRoutes.find(r => Number(r.id) === id) || null;
}

function selectRoute(route) {
  const hidden = document.getElementById('routeSelect');
  const input = document.getElementById('routeSearchInput');
  const dropdown = document.getElementById('routeSearchResults');
  if (!route || !hidden || !input) return;
  hidden.value = Number(route.id);
  input.value = prettyRoute(route);
  routePickerOpen = false;
  if (dropdown) dropdown.hidden = true;
}

function renderRoutePicker() {
  const input = document.getElementById('routeSearchInput');
  const dropdown = document.getElementById('routeSearchResults');
  if (!input || !dropdown) return;

  const q = input.value.toLowerCase().trim();
  const selected = selectedRoute();

  const rows = (walkRoutes || []).filter(r => {
    const hay = `${r.route_name || ''} ${r.description || ''} ${r.total_km || ''} ${r.difficulty || ''} ${r.start_address || ''} ${r.end_address || ''}`.toLowerCase();
    return !q || hay.includes(q);
  }).slice(0, 60);

  dropdown.innerHTML = rows.map(r => {
    const active = selected && Number(selected.id) === Number(r.id);
    return `<button type="button" class="pro-search-option ${active ? 'is-selected' : ''}" data-route-option="${Number(r.id)}">
      <strong>${Pyrk.esc(r.route_name || 'Route')}</strong>
      <small>${Number(r.total_km || 0).toFixed(1)} KM · ${Pyrk.esc(r.difficulty || '')}${r.start_address ? ' · ' + Pyrk.esc(r.start_address) : ''}</small>
    </button>`;
  }).join('') || '<p class="muted small pro-search-empty">No routes found.</p>';

  dropdown.hidden = !routePickerOpen;
}

function setRouteById(routeId) {
  const route = walkRoutes.find(r => Number(r.id) === Number(routeId));
  if (route) selectRoute(route);
  else {
    const hidden = document.getElementById('routeSelect');
    const input = document.getElementById('routeSearchInput');
    if (hidden) hidden.value = routeId || '';
    if (input) input.value = routeId ? `Route #${routeId}` : '';
  }
}

function normalizeWalkDateInputsForSubmit(data) {
  const rawWalkDate = data.walk_date;
  const isoWalkDate = ddMmYyyyToIso(rawWalkDate);
  if (!isoWalkDate) return 'Walk date must be dd/mm/yyyy.';
  data.walk_date = isoWalkDate;

  if (data.registration_deadline) {
    const v = ddMmYyyyHmToIsoDateTime(data.registration_deadline);
    if (!v) return 'Registration deadline must be dd/mm/yyyy hh:mm.';
    data.registration_deadline = v;
  }

  if (data.voting_deadline) {
    const v = ddMmYyyyHmToIsoDateTime(data.voting_deadline);
    if (!v) return 'Voting deadline must be dd/mm/yyyy hh:mm.';
    data.voting_deadline = v;
  }

  return '';
}


const walkTeamInlineState = {
  openWalkId: 0,
  loadedWalkId: 0,
  users: [],
  assignments: { manager: [], head_walker: [], walker: [] },
  selectedWalkers: new Set(),
  managerId: 0,
  headId: 0,
  managerQuery: '',
  headQuery: '',
  managerOpen: false,
  headOpen: false,
  canManage: false,
  loading: false
};

function canOpenWalkTeam() {
  return canViewWalkRegistrants() || canEditWalks() || canCreateWalks() || canViewAssignedWalks();
}

function roleLabel(role) {
  return String(role || '').replace(/_/g, ' ') || 'user';
}

function teamUserLabel(user) {
  const role = roleLabel(user.role_name);
  const email = user.email ? ` · ${user.email}` : '';
  return `${user.full_name || 'User'} (${role})${email}`;
}

function teamAssignmentName(role) {
  return (walkTeamInlineState.assignments[role] || []).map(u => u.full_name).filter(Boolean).join(', ') || 'Not assigned';
}


function teamAssignedWalkers() {
  return (walkTeamInlineState.assignments.walker || []).filter(Boolean);
}

function teamAssignedWalkerNames() {
  return teamAssignedWalkers()
    .map(u => u.full_name || u.email || '')
    .filter(Boolean);
}

function teamPersonInitials(value) {
  const parts = String(value || '').trim().split(/\s+/).filter(Boolean);
  if (!parts.length) return 'W';
  return parts.slice(0, 2).map(part => part.charAt(0).toUpperCase()).join('');
}

function teamAssignedWalkerSummaryHtml() {
  const walkers = teamAssignedWalkers();
  const count = walkers.length;
  if (!count) {
    return `<div class="walk-team-current-walkers-value is-empty">
      <strong>Not assigned</strong>
      <small class="walk-team-current-note">No walkers assigned yet.</small>
    </div>`;
  }

  const names = teamAssignedWalkerNames();
  const chips = walkers.map(u => {
    const name = u.full_name || u.email || 'Assigned walker';
    const detail = u.email ? ` title="${Pyrk.esc(u.email)}"` : '';
    return `<span class="walk-team-person-chip"${detail}>
      <span class="walk-team-person-avatar" aria-hidden="true">${Pyrk.esc(teamPersonInitials(name))}</span>
      <span class="walk-team-person-name">${Pyrk.esc(name)}</span>
    </span>`;
  }).join('');

  return `<div class="walk-team-current-walkers-value">
    <div class="walk-team-current-count"><strong>${count} assigned walker${count === 1 ? '' : 's'}</strong></div>
    <div class="walk-team-current-people" aria-label="Assigned walkers">${chips || Pyrk.esc(names.join(', '))}</div>
  </div>`;
}

function teamManagerId() {
  return Number(walkTeamInlineState.managerId || 0);
}

function teamHeadId() {
  return Number(walkTeamInlineState.headId || 0);
}

function teamOptions(selectedId) {
  return '<option value="">Not assigned</option>' + (walkTeamInlineState.users || []).map(u => {
    const selected = Number(selectedId || 0) === Number(u.id) ? ' selected' : '';
    return `<option value="${Number(u.id)}"${selected}>${Pyrk.esc(teamUserLabel(u))}</option>`;
  }).join('');
}

function closeInlineWalkTeam() {
  walkTeamInlineState.openWalkId = 0;
  document.querySelectorAll('.walk-team-inline-panel').forEach(panel => panel.remove());
  document.body.classList.remove('modal-open');
}

function inlineTeamShell(walkId, title = 'Walk team') {
  return `<section class="walk-team-inline-panel" data-inline-team-panel="${Number(walkId)}">
    <div class="walk-team-inline-head">
      <div>
        <p class="eyebrow">Walk team</p>
        <h3>${Pyrk.esc(title)}</h3>
        <p class="muted small">Assign Manager, Head Walker and Walkers. Creators manage their own walk; full-access users manage all walks.</p>
      </div>
      <button class="btn btn-light btn-small" type="button" data-close-team-panel>Close</button>
    </div>
    <div class="walk-team-inline-body" data-team-body>
      <div class="walk-team-loading">
        <span class="spinner"></span>
        <strong>Loading team...</strong>
      </div>
    </div>
  </section>`;
}


function teamUserById(id) {
  return (walkTeamInlineState.users || []).find(u => Number(u.id) === Number(id)) || null;
}

function teamSelectedText(kind) {
  const id = kind === 'manager' ? teamManagerId() : teamHeadId();
  const user = teamUserById(id);
  return user ? teamUserLabel(user) : '';
}


function openTeamPersonDropdown(kind) {
  if (!walkTeamInlineState.canManage) return;

  const panel = document.querySelector(`[data-inline-team-panel="${walkTeamInlineState.openWalkId}"]`);
  if (!panel) return;

  if (kind === 'manager') {
    walkTeamInlineState.managerOpen = true;
    walkTeamInlineState.headOpen = false;
    walkTeamInlineState.managerQuery = '';
    walkTeamInlineState.headQuery = '';
  } else if (kind === 'head') {
    walkTeamInlineState.headOpen = true;
    walkTeamInlineState.managerOpen = false;
    walkTeamInlineState.headQuery = '';
    walkTeamInlineState.managerQuery = '';
  } else {
    return;
  }

  const input = panel.querySelector(kind === 'manager' ? '[data-team-manager-search]' : '[data-team-head-search]');
  if (input) {
    input.value = '';
    input.focus({ preventScroll: true });
  }

  updateTeamPersonDropdown(kind);
}



function teamPersonRows(kind) {
  const query = kind === 'manager' ? walkTeamInlineState.managerQuery : walkTeamInlineState.headQuery;
  const selectedId = kind === 'manager' ? teamManagerId() : teamHeadId();
  const q = String(query || '').toLowerCase().trim();

  return (walkTeamInlineState.users || []).filter(u => {
    const hay = `${u.full_name || ''} ${u.email || ''} ${u.mobile || ''} ${u.role_name || ''}`.toLowerCase();
    return !q || hay.includes(q);
  }).slice(0, 80).map(u => {
    const active = Number(u.id) === Number(selectedId);
    return `<button type="button" class="pro-search-option ${active ? 'is-selected' : ''}" data-team-user-option="${Number(u.id)}" data-team-kind="${kind}">
      <strong>${Pyrk.esc(u.full_name || 'User')}</strong>
      <small>${Pyrk.esc(roleLabel(u.role_name))}${u.email ? ' · ' + Pyrk.esc(u.email) : ''}</small>
    </button>`;
  }).join('') || '<p class="muted small pro-search-empty">No users found.</p>';
}

function updateTeamPersonDropdown(kind) {
  const panel = document.querySelector(`[data-inline-team-panel="${walkTeamInlineState.openWalkId}"]`);
  if (!panel) return;

  const field = panel.querySelector(kind === 'manager' ? '[data-team-manager-search]' : '[data-team-head-search]');
  if (!field) return;

  const wrapper = field.closest('.team-search-field');
  const dropdown = wrapper?.querySelector('.team-search-dropdown');
  if (!dropdown) return;

  const open = kind === 'manager' ? walkTeamInlineState.managerOpen : walkTeamInlineState.headOpen;
  dropdown.innerHTML = teamPersonRows(kind);
  dropdown.hidden = !open || !walkTeamInlineState.canManage;
}

function closeTeamPersonDropdowns() {
  if (!walkTeamInlineState.managerOpen && !walkTeamInlineState.headOpen) return;
  walkTeamInlineState.managerOpen = false;
  walkTeamInlineState.headOpen = false;
  walkTeamInlineState.managerQuery = '';
  walkTeamInlineState.headQuery = '';

  const panel = document.querySelector(`[data-inline-team-panel="${walkTeamInlineState.openWalkId}"]`);
  if (!panel) return;

  const managerInput = panel.querySelector('[data-team-manager-search]');
  const headInput = panel.querySelector('[data-team-head-search]');
  if (managerInput) managerInput.value = teamSelectedText('manager');
  if (headInput) headInput.value = teamSelectedText('head');

  updateTeamPersonDropdown('manager');
  updateTeamPersonDropdown('head');
}


function teamSearchBox(kind, label, hint) {
  const selectedId = kind === 'manager' ? teamManagerId() : teamHeadId();
  const open = kind === 'manager' ? walkTeamInlineState.managerOpen : walkTeamInlineState.headOpen;
  const disabled = !walkTeamInlineState.canManage;
  const searchValue = open ? (kind === 'manager' ? walkTeamInlineState.managerQuery : walkTeamInlineState.headQuery) : teamSelectedText(kind);

  return `<label class="team-search-field">${Pyrk.esc(label)}
    <input type="hidden" data-team-${kind}-id value="${selectedId || ''}">
    <input type="text" data-team-${kind}-search placeholder="Click or type to choose ${Pyrk.esc(label.toLowerCase())}" value="${Pyrk.esc(searchValue)}" ${disabled ? 'disabled' : ''} autocomplete="off">
    <div class="pro-search-dropdown team-search-dropdown" ${open && !disabled ? '' : 'hidden'}>${teamPersonRows(kind)}</div>
    <small>${Pyrk.esc(hint)}</small>
  </label>`;
}


function teamWalkerRows(query = '') {
  const managerId = teamManagerId();
  const headId = teamHeadId();
  const canManage = !!walkTeamInlineState.canManage;
  const q = String(query || '').toLowerCase().trim();

  const rows = (walkTeamInlineState.users || []).filter(u => {
    const hay = `${u.full_name || ''} ${u.email || ''} ${u.mobile || ''} ${u.role_name || ''}`.toLowerCase();
    return !q || hay.includes(q);
  });

  return rows.map(u => {
    const id = Number(u.id);
    const reserved = id === managerId || id === headId;
    const checked = walkTeamInlineState.selectedWalkers.has(id) && !reserved;
    const disabled = reserved || !canManage;
    const initial = String(u.full_name || '?').slice(0,1).toUpperCase();

    return `<label class="walk-team-user-row ${checked ? 'is-selected' : ''} ${reserved ? 'is-reserved' : ''} ${!canManage ? 'is-readonly' : ''}">
      <input type="checkbox" value="${id}" data-team-walker ${checked ? 'checked' : ''} ${disabled ? 'disabled' : ''}>
      <span class="avatar">${Pyrk.esc(initial)}</span>
      <span><strong>${Pyrk.esc(u.full_name || 'User')}</strong><small>${Pyrk.esc(roleLabel(u.role_name))}${u.email ? ' · ' + Pyrk.esc(u.email) : ''}</small></span>
    </label>`;
  }).join('') || '<p class="muted">No users found.</p>';
}

function updateTeamWalkerSearch(input) {
  const panel = input?.closest('[data-inline-team-panel]');
  const list = panel?.querySelector('.walk-team-list');
  if (!list) return;

  // Update only the matching rows. Rebuilding the whole panel here would
  // replace the search input and make it lose focus after every letter.
  list.innerHTML = teamWalkerRows(input.value);
}

function renderInlineTeamPanel(message = '') {
  const panel = document.querySelector(`[data-inline-team-panel="${walkTeamInlineState.openWalkId}"]`);
  if (!panel) return;

  const body = panel.querySelector('[data-team-body]');
  const canManage = !!walkTeamInlineState.canManage;
  const q = panel.querySelector('[data-team-search]')?.value || '';
  const walkerRows = teamWalkerRows(q);

  body.innerHTML = `
    ${message ? `<p class="notice ${message.includes('saved') ? 'ok' : 'bad'}">${Pyrk.esc(message)}</p>` : ''}
    <div class="walk-team-current">
      <div><span>Current Manager</span><strong>${Pyrk.esc(teamAssignmentName('manager'))}</strong></div>
      <div><span>Current Head</span><strong>${Pyrk.esc(teamAssignmentName('head_walker'))}</strong></div>
      <div class="walk-team-current-walkers"><span>Current Walkers</span>${teamAssignedWalkerSummaryHtml()}</div>
    </div>

    <div class="walk-team-role-grid">
      ${teamSearchBox('manager', 'Manager', 'Manager can update the team for this walk.')}
      ${teamSearchBox('head', 'Head Walker', 'Operational leader for the walk.')}
    </div>

    <section class="walk-team-picker">
      <div class="section-head mini-head">
        <div>
          <p class="eyebrow">Walkers</p>
          <h3>Assign walkers</h3>
          <p class="muted small">${canManage ? 'Select the walkers who should access this walk only.' : 'View only. Only admins or the assigned manager can edit this team.'}</p>
        </div>
        <div class="inline-controls">
          <button class="btn btn-light btn-small" type="button" data-team-select-all ${!canManage ? 'disabled' : ''}>Select all</button>
          <button class="btn btn-light btn-small" type="button" data-team-clear ${!canManage ? 'disabled' : ''}>Clear</button>
        </div>
      </div>
      <input data-team-search placeholder="Search users by name, email, role" value="${Pyrk.esc(q)}" ${!canManage ? 'disabled' : ''}>
      <div class="walk-team-list">${walkerRows}</div>
    </section>

    <div class="walk-team-actions">
      <button class="btn btn-light" type="button" data-close-team-panel>Cancel</button>
      <button class="btn" type="button" data-save-team ${!canManage ? 'hidden' : ''}>Save Team</button>
    </div>
  `;
}

async function openWalkTeamInline(walkId, trigger) {
  walkId = Number(walkId || 0);
  if (!walkId) return;

  // Never lock page scroll for this inline team editor.
  document.body.classList.remove('modal-open');

  if (walkTeamInlineState.openWalkId === walkId) {
    closeInlineWalkTeam();
    return;
  }

  closeInlineWalkTeam();
  walkTeamInlineState.openWalkId = walkId;

  const card = trigger?.closest?.('.walk-admin-card') || trigger?.closest?.('article');
  if (!card) {
    alert('Could not locate walk card.');
    return;
  }

  const title = card.querySelector('h3')?.textContent || 'Walk team';
  card.insertAdjacentHTML('afterend', inlineTeamShell(walkId, title));

  const panel = document.querySelector(`[data-inline-team-panel="${walkId}"]`);
  panel?.scrollIntoView({behavior: 'smooth', block: 'nearest'});

  try {
    const res = await Pyrk.get('walks.php', {action: 'team_options', id: walkId});
    if (!res.ok) {
      const body = panel?.querySelector('[data-team-body]');
      if (body) {
        body.innerHTML = `<p class="notice bad">${Pyrk.esc(res.message || 'Could not load team.')}</p>
          <div class="walk-team-actions"><button class="btn btn-light" type="button" data-close-team-panel>Close</button></div>`;
      }
      return;
    }

    walkTeamInlineState.loadedWalkId = walkId;
    walkTeamInlineState.users = res.data.users || [];
    walkTeamInlineState.assignments = res.data.assignments || { manager: [], head_walker: [], walker: [] };
    walkTeamInlineState.canManage = !!res.data.can_manage_team;
    walkTeamInlineState.managerId = Number((walkTeamInlineState.assignments.manager || [])[0]?.user_id || 0);
    walkTeamInlineState.headId = Number((walkTeamInlineState.assignments.head_walker || [])[0]?.user_id || 0);
    walkTeamInlineState.managerQuery = '';
    walkTeamInlineState.headQuery = '';
    walkTeamInlineState.managerOpen = false;
    walkTeamInlineState.headOpen = false;
    walkTeamInlineState.selectedWalkers = new Set((walkTeamInlineState.assignments.walker || []).map(u => Number(u.user_id)).filter(Boolean));

    renderInlineTeamPanel();
  } catch (error) {
    console.error('Team load failed', error);
    const body = panel?.querySelector('[data-team-body]');
    if (body) {
      body.innerHTML = `<p class="notice bad">Could not load team. Make sure the SQL migration was run, then refresh.</p>
        <div class="walk-team-actions"><button class="btn btn-light" type="button" data-close-team-panel>Close</button></div>`;
    }
  }
}

async function saveInlineWalkTeam() {
  if (!walkTeamInlineState.canManage) return;

  const panel = document.querySelector(`[data-inline-team-panel="${walkTeamInlineState.openWalkId}"]`);
  const managerId = teamManagerId();
  const headId = teamHeadId();
  const walkerIds = Array.from(walkTeamInlineState.selectedWalkers).filter(id => id !== managerId && id !== headId);

  const saveBtn = panel?.querySelector('[data-save-team]');
  if (saveBtn) {
    saveBtn.disabled = true;
    saveBtn.textContent = 'Saving...';
  }

  const res = await Pyrk.post('walks.php', {
    action: 'save_team',
    walk_id: walkTeamInlineState.openWalkId,
    manager_id: managerId ? String(managerId) : '',
    head_walker_id: headId ? String(headId) : '',
    walker_ids: JSON.stringify(walkerIds)
  });

  if (!res.ok) {
    if (saveBtn) {
      saveBtn.disabled = false;
      saveBtn.textContent = 'Save Team';
    }
    renderInlineTeamPanel(res.message || 'Could not save team.');
    return;
  }

  const openId = walkTeamInlineState.openWalkId;
  await loadWalks();
  walkTeamInlineState.openWalkId = openId;

  // Reload fresh assignments and show success, without opening a modal.
  const dummy = document.querySelector(`[data-walk-card-id="${openId}"] [data-open-team]`) || document.querySelector(`[data-open-team="${openId}"]`);
  if (dummy) {
    await openWalkTeamInline(openId, dummy);
    renderInlineTeamPanel('Team saved successfully.');
  }
}


let editingWalkId = null;
function canCreateWalks(){ return !!window.PYRK_CAN_CREATE_WALKS; }
function canEditWalks(){ return !!window.PYRK_CAN_EDIT_WALKS; }
function canDeleteWalks(){ return !!window.PYRK_CAN_DELETE_WALKS; }
function canManageWalkPhotos(){ return !!window.PYRK_CAN_MANAGE_WALK_PHOTOS; }
function canViewWalkRegistrants(){ return !!window.PYRK_CAN_VIEW_WALK_REGISTRANTS || !!window.PYRK_CAN_VIEW_ASSIGNED_WALKS || canCreateWalks() || canEditWalks() || canDeleteWalks() || canUnregisterWalkMembers(); }
function canAssignWalkStaff(){ return !!window.PYRK_CAN_ASSIGN_WALK_STAFF; }
function canViewAllWalks(){ return !!window.PYRK_CAN_VIEW_ALL_WALKS; }
function canViewAssignedWalks(){ return !!window.PYRK_CAN_VIEW_ASSIGNED_WALKS; }
function canUnregisterWalkMembers(){ return !!window.PYRK_CAN_UNREGISTER_WALK_MEMBERS || !!window.PYRK_CAN_EDIT_WALKS; }

let createInviteData = { members: [], groups: [] };
let currentInviteData = { members: [], groups: [], invitations: { members: [], groups: [] } };

const inviteState = {
  createGroups: { selected: new Set(), open: false },
  createMembers: { selected: new Set(), open: false },
  modalGroups: { selected: new Set(), open: false },
  modalMembers: { selected: new Set(), open: false },
  createMemberFilterByGroups: false,
  modalMemberFilterByGroups: false
};

function publicAsset(path) {
  if (!path) return '';
  if (/^https?:\/\//.test(path)) return path;
  return (window.PYRK_PUBLIC_BASE || '../') + String(path).replace(/^\/+/, '');
}

function initials(name) {
  return String(name || 'M').trim().split(/\s+/).slice(0, 2).map(p => p[0] || '').join('').toUpperCase() || 'M';
}

function avatarHtml(person) {
  if (person && person.profile_photo_path) {
    return `<img class="avatar-img" src="${Pyrk.esc(publicAsset(person.profile_photo_path))}" alt="${Pyrk.esc(person.full_name || 'Member')}">`;
  }
  return `<div class="avatar">${Pyrk.esc(initials(person && person.full_name))}</div>`;
}


function formatWalkDateDisplay(value) {
  if (!value) return '';
  const s = String(value).slice(0, 10);
  const m = s.match(/^(\d{4})-(\d{2})-(\d{2})$/);
  if (!m) return Pyrk.formatDate(value);
  return `${m[3]}/${m[2]}/${m[1]}`;
}

function formatTime(value) { return value ? String(value).slice(0, 5) : '—'; }
function formatDateTime(value) { return Pyrk.formatDateTime(value); }

function statusPill(status) {
  const s = String(status || 'unknown');
  const cls = ['active', 'registered', 'approved', 'finished', 'open', 'signed'].includes(s)
    ? 'pill-green'
    : (['blocked', 'rejected', 'cancelled', 'invitation_only'].includes(s) ? 'pill-red' : 'pill-muted');
  return `<span class="pill ${cls}">${Pyrk.esc(s.replace(/_/g, ' '))}</span>`;
}

function openModal(id) { if (window.Pyrk) return Pyrk.openModal(id); }
function closeModal(id) { if (window.Pyrk) return Pyrk.closeModal(id); }
function idsFromSet(set) { return Array.from(set).map(Number).filter(Boolean); }

function inviteDataForContext(context) {
  return context === 'modal' ? currentInviteData : createInviteData;
}

function inviteMemberStateForContext(context) {
  return context === 'modal' ? inviteState.modalMembers : inviteState.createMembers;
}

function selectAllInviteMembers(context) {
  const data = inviteDataForContext(context);
  const memberState = inviteMemberStateForContext(context);
  (data.members || []).forEach(member => {
    const id = Number(member.id || member.user_id || 0);
    if (id) memberState.selected.add(id);
  });
  memberState.open = true;
  if (context === 'modal') {
    renderInvitationChoices();
    focusInviteSearch('modalMembers');
  } else {
    renderCreateInvitationChoices();
    focusInviteSearch('createMembers');
  }
  updateInviteCount();
}

function clearInviteMembers(context) {
  const memberState = inviteMemberStateForContext(context);
  memberState.selected.clear();
  memberState.open = true;
  if (context === 'modal') {
    renderInvitationChoices();
    focusInviteSearch('modalMembers');
  } else {
    renderCreateInvitationChoices();
    focusInviteSearch('createMembers');
  }
  updateInviteCount();
}

function selectedInvitePreview(context) {
  const memberCount = inviteMemberStateForContext(context).selected.size;
  const groupCount = inviteGroupState(context).selected.size;
  const totalMembers = (inviteDataForContext(context).members || []).length;
  if (memberCount === totalMembers && totalMembers > 0) {
    return `${memberCount} members selected · all active members`;
  }
  return `${memberCount} members + ${groupCount} groups selected`;
}


function focusInviteSearch(key) {
  const cfg = getTypeaheadConfig(key);
  if (!cfg) return;
  const input = document.getElementById(cfg.inputId);
  if (!input) return;
  window.setTimeout(() => {
    input.focus({ preventScroll: true });
    try {
      const len = input.value.length;
      input.setSelectionRange(len, len);
    } catch (e) {}
  }, 0);
}

function keepInviteDropdownOpen(key) {
  const cfg = getTypeaheadConfig(key);
  if (!cfg) return;
  cfg.state.open = true;
  renderInviteTypeahead(key);
  focusInviteSearch(key);
}


function inviteContextFromMemberKey(key) {
  return key === 'modalMembers' ? 'modal' : 'create';
}

function inviteContextData(context) {
  return context === 'modal' ? currentInviteData : createInviteData;
}

function inviteGroupState(context) {
  return context === 'modal' ? inviteState.modalGroups : inviteState.createGroups;
}

function inviteMemberState(context) {
  return context === 'modal' ? inviteState.modalMembers : inviteState.createMembers;
}

function inviteGroupFilterEnabled(context) {
  return context === 'modal' ? !!inviteState.modalMemberFilterByGroups : !!inviteState.createMemberFilterByGroups;
}

function syncGroupMemberFilter(context) {
  const hasGroups = inviteGroupState(context).selected.size > 0;
  setInviteGroupFilter(context, hasGroups);
  if (!hasGroups) {
    renderGroupMemberAssistant(context);
    return;
  }

  // Keep any already-selected individual members only if they belong to the selected groups.
  // This prevents accidentally mixing members from another group after changing the group.
  const allowedMemberIds = selectedGroupMemberIds(context);
  const memberState = inviteMemberState(context);
  Array.from(memberState.selected).forEach(memberId => {
    if (!allowedMemberIds.has(Number(memberId))) {
      memberState.selected.delete(Number(memberId));
    }
  });
}

function setInviteGroupFilter(context, enabled) {
  if (context === 'modal') inviteState.modalMemberFilterByGroups = !!enabled;
  else inviteState.createMemberFilterByGroups = !!enabled;
}

function normalizeGroupMembersMap(data) {
  const map = {};
  const raw = data?.group_members || {};
  Object.keys(raw || {}).forEach(key => {
    map[Number(key)] = (raw[key] || []).map(Number).filter(Boolean);
  });
  (data?.groups || []).forEach(group => {
    const gid = Number(group.id);
    if (!map[gid] && Array.isArray(group.member_ids)) {
      map[gid] = group.member_ids.map(Number).filter(Boolean);
    }
  });
  return map;
}

function selectedGroupMemberIds(context) {
  const data = inviteContextData(context);
  const groupMap = normalizeGroupMembersMap(data);
  const ids = new Set();
  inviteGroupState(context).selected.forEach(groupId => {
    (groupMap[Number(groupId)] || []).forEach(memberId => ids.add(Number(memberId)));
  });
  return ids;
}

function selectedGroupsWithCounts(context) {
  const data = inviteContextData(context);
  const groupMap = normalizeGroupMembersMap(data);
  const groups = data?.groups || [];
  return Array.from(inviteGroupState(context).selected)
    .map(groupId => {
      const group = groups.find(g => Number(g.id) === Number(groupId));
      if (!group) return null;
      const memberIds = groupMap[Number(groupId)] || [];
      return { group, memberIds };
    })
    .filter(Boolean);
}

function addSelectedGroupMembersToIndividualInvites(context) {
  const ids = selectedGroupMemberIds(context);
  const memberState = inviteMemberState(context);
  ids.forEach(id => memberState.selected.add(Number(id)));
  const memberKey = context === 'modal' ? 'modalMembers' : 'createMembers';
  inviteMemberState(context).open = true;
  renderInviteTypeahead(memberKey);
  renderGroupMemberAssistant(context);
  updateInviteCount();
  focusInviteSearch(memberKey);
}

function clearIndividualMembersFromSelectedGroups(context) {
  const ids = selectedGroupMemberIds(context);
  const memberState = inviteMemberState(context);
  ids.forEach(id => memberState.selected.delete(Number(id)));
  const memberKey = context === 'modal' ? 'modalMembers' : 'createMembers';
  inviteMemberState(context).open = true;
  renderInviteTypeahead(memberKey);
  renderGroupMemberAssistant(context);
  updateInviteCount();
  focusInviteSearch(memberKey);
}

function renderGroupMemberAssistant(context) {
  const box = document.getElementById(context === 'modal' ? 'modalGroupMemberAssistant' : 'createGroupMemberAssistant');
  if (!box) return;

  const selectedGroups = selectedGroupsWithCounts(context);
  const allGroupMemberIds = selectedGroupMemberIds(context);
  const memberState = inviteMemberState(context);
  const alreadyIndividual = Array.from(allGroupMemberIds).filter(id => memberState.selected.has(Number(id))).length;
  const filterOn = inviteGroupFilterEnabled(context);

  if (!selectedGroups.length) {
    box.innerHTML = `
      <div class="assistant-empty">
        <strong>Smart group helper</strong>
        <span>Select a group and the member list will automatically show only members from that selected group.</span>
      </div>`;
    return;
  }

  const groupCards = selectedGroups.map(({group, memberIds}) => `
    <article class="assistant-group-card">
      <div>
        <strong>${Pyrk.esc(group.group_name || 'Group')}</strong>
        <small>${memberIds.length} active members${group.description ? ' · ' + Pyrk.esc(group.description) : ''}</small>
      </div>
      <button class="mini-link" type="button" data-add-one-group="${Number(group.id)}">Add members</button>
    </article>`).join('');

  box.innerHTML = `
    <section class="assistant-card">
      <div class="assistant-head">
        <div>
          <p class="eyebrow">Smart group helper</p>
          <h3>${allGroupMemberIds.size} members inside selected group${selectedGroups.length > 1 ? 's' : ''}</h3>
          <p class="muted small">${alreadyIndividual} of them are already selected individually. The member list is now filtered to the selected group members only.</p>
        </div>
        <div class="assistant-kpis">
          <span><strong>${selectedGroups.length}</strong> groups</span>
          <span><strong>${allGroupMemberIds.size}</strong> members</span>
          <span><strong>${alreadyIndividual}</strong> individual</span>
        </div>
      </div>
      <div class="assistant-actions">
        <button class="btn btn-small" type="button" data-add-group-members="${context}">Add all group members as individual invitees</button>
        <button class="btn btn-light btn-small" type="button" data-toggle-group-filter="${context}">${filterOn ? 'Temporarily show all members' : 'Filter by selected groups'}</button>
        <button class="btn btn-light btn-small" type="button" data-clear-group-members="${context}">Remove those individual selections</button>
      </div>
      <div class="assistant-groups">${groupCards}</div>
    </section>`;

  box.querySelector('[data-add-group-members]')?.addEventListener('click', () => addSelectedGroupMembersToIndividualInvites(context));
  box.querySelector('[data-toggle-group-filter]')?.addEventListener('click', () => {
    setInviteGroupFilter(context, !filterOn);
    renderInviteTypeahead(context === 'modal' ? 'modalMembers' : 'createMembers');
    renderGroupMemberAssistant(context);
  });
  box.querySelector('[data-clear-group-members]')?.addEventListener('click', () => clearIndividualMembersFromSelectedGroups(context));
  box.querySelectorAll('[data-add-one-group]').forEach(btn => {
    btn.addEventListener('click', () => {
      const data = inviteContextData(context);
      const map = normalizeGroupMembersMap(data);
      const memberState = inviteMemberState(context);
      (map[Number(btn.dataset.addOneGroup)] || []).forEach(id => memberState.selected.add(Number(id)));
      const memberKey = context === 'modal' ? 'modalMembers' : 'createMembers';
      inviteMemberState(context).open = true;
      renderInviteTypeahead(memberKey);
      renderGroupMemberAssistant(context);
      updateInviteCount();
      focusInviteSearch(memberKey);
    });
  });
}

function renderAllGroupMemberAssistants() {
  renderGroupMemberAssistant('create');
  renderGroupMemberAssistant('modal');
}


function closeAllInviteTypeaheads() {
  Object.values(inviteState).forEach(state => { state.open = false; });
  renderCreateInvitationChoices();
  renderInvitationChoices();
}


async function loadRoutesForSelect() {
  const res = await Pyrk.get('routes.php');
  const hidden = document.getElementById('routeSelect');
  const input = document.getElementById('routeSearchInput');
  if (!hidden || !input) return;

  if (!res.ok) {
    input.placeholder = 'Could not load routes';
    return;
  }

  walkRoutes = res.data.routes || [];
  const existing = Number(hidden.value || 0);
  if (existing) setRouteById(existing);
  else input.value = '';

  renderRoutePicker();
}

function getTypeaheadConfig(key) {
  const configs = {
    createGroups: {
      key,
      listId: 'createInviteGroupsList',
      inputId: 'createInviteGroupSearch',
      state: inviteState.createGroups,
      items: createInviteData.groups || [],
      type: 'group',
      placeholder: 'Type to find a group…',
      empty: 'No groups found. Create groups from People → Member groups.',
      maxHeightClass: 'typeahead-create'
    },
    createMembers: {
      key,
      listId: 'createInviteMembersList',
      inputId: 'createInviteMemberSearch',
      state: inviteState.createMembers,
      items: createInviteData.members || [],
      type: 'member',
      placeholder: 'Type member name, email, or mobile…',
      empty: 'No active members found.',
      maxHeightClass: 'typeahead-create'
    },
    modalGroups: {
      key,
      listId: 'inviteGroupsList',
      inputId: 'inviteGroupSearch',
      state: inviteState.modalGroups,
      items: currentInviteData.groups || [],
      type: 'group',
      placeholder: 'Type to find a group…',
      empty: 'No groups found.',
      maxHeightClass: 'typeahead-modal'
    },
    modalMembers: {
      key,
      listId: 'inviteMembersList',
      inputId: 'inviteMemberSearch',
      state: inviteState.modalMembers,
      items: currentInviteData.members || [],
      type: 'member',
      placeholder: 'Type member name, email, or mobile…',
      empty: 'No members found.',
      maxHeightClass: 'typeahead-modal'
    }
  };
  return configs[key];
}

function itemId(item) { return Number(item && item.id); }
function groupSearchText(g) { return `${g.group_name || ''} ${g.description || ''} ${g.member_count || 0}`.toLowerCase(); }
function memberSearchText(m) { return `${m.full_name || ''} ${m.email || ''} ${m.mobile || ''} ${m.total_kilometers || 0}`.toLowerCase(); }
function searchTextForItem(item, type) { return type === 'group' ? groupSearchText(item) : memberSearchText(item); }

function selectedLabel(item, type) {
  return type === 'group'
    ? (item.group_name || `Group #${item.id}`)
    : (item.full_name || `Member #${item.id}`);
}

function resultRowHtml(item, type, selected) {
  if (type === 'group') {
    return `
      <button class="typeahead-result ${selected ? 'is-selected' : ''}" type="button" data-select-id="${Number(item.id)}">
        <span class="typeahead-icon">👥</span>
        <span class="typeahead-copy">
          <strong>${Pyrk.esc(item.group_name || 'Unnamed group')}</strong>
          <small>${Number(item.member_count || 0)} members${item.description ? ' · ' + Pyrk.esc(item.description) : ''}</small>
        </span>
        <span class="typeahead-check" title="${selected ? 'Selected' : 'Add'}">${selected ? '✓' : '+'}</span>
      </button>`;
  }
  return `
    <button class="typeahead-result ${selected ? 'is-selected' : ''}" type="button" data-select-id="${Number(item.id)}">
      <span class="typeahead-avatar">${avatarHtml(item)}</span>
      <span class="typeahead-copy">
        <strong>${Pyrk.esc(item.full_name || 'Unnamed member')}</strong>
        <small>${Pyrk.esc(item.mobile || item.email || 'No contact')} · ${Number(item.total_kilometers || 0).toFixed(1)} KM · ${Number(item.loyalty_points || 0)} pts</small>
      </span>
      <span class="typeahead-check" title="${selected ? 'Selected' : 'Add'}">${selected ? '✓' : '+'}</span>
    </button>`;
}

function updateInviteCount() {
  const modalCount = inviteState.modalMembers.selected.size + inviteState.modalGroups.selected.size;
  const modalInput = document.getElementById('inviteCountPreview');
  if (modalInput) modalInput.value = selectedInvitePreview('modal');

  const createCount = inviteState.createMembers.selected.size + inviteState.createGroups.selected.size;
  const createInput = document.getElementById('createInviteCountPreview');
  if (createInput) createInput.value = selectedInvitePreview('create');
}

function renderInviteTypeahead(key) {
  const cfg = getTypeaheadConfig(key);
  if (!cfg) return;
  const box = document.getElementById(cfg.listId);
  const input = document.getElementById(cfg.inputId);
  if (!box || !input) return;
  input.setAttribute('aria-expanded', cfg.state.open ? 'true' : 'false');

  if (!input.dataset.typeaheadReady) {
    input.dataset.typeaheadReady = '1';
    input.placeholder = cfg.placeholder;
    input.setAttribute('autocomplete', 'off');
    input.setAttribute('role', 'combobox');
    input.setAttribute('aria-expanded', cfg.state.open ? 'true' : 'false');
    input.addEventListener('focus', () => { cfg.state.open = true; renderInviteTypeahead(key); });
    input.addEventListener('input', () => { cfg.state.open = true; renderInviteTypeahead(key); });
    input.addEventListener('keydown', e => {
      if (e.key === 'Escape') { cfg.state.open = false; input.blur(); renderInviteTypeahead(key); }
    });
  }

  const query = String(input.value || '').toLowerCase().trim();
  const allItems = Array.isArray(cfg.items) ? cfg.items : [];
  const context = cfg.key === 'modalMembers' || cfg.key === 'modalGroups' ? 'modal' : 'create';
  const hasSelectedGroups = inviteGroupState(context).selected.size > 0;
  const groupMemberFilter = cfg.type === 'member' && (inviteGroupFilterEnabled(context) || hasSelectedGroups);
  const groupMemberIds = groupMemberFilter ? selectedGroupMemberIds(context) : null;
  const filtered = allItems
    .filter(item => !groupMemberFilter || groupMemberIds.has(itemId(item)))
    .filter(item => !query || searchTextForItem(item, cfg.type).includes(query))
    .slice(0, 60);
  const selectedItems = allItems.filter(item => cfg.state.selected.has(itemId(item)));

  box.className = `pro-typeahead-multi ${cfg.maxHeightClass || ''}`;
  box.innerHTML = `
    <div class="typeahead-selected-zone">
      ${selectedItems.length ? selectedItems.map(item => `
        <button class="typeahead-chip" type="button" data-remove-id="${Number(item.id)}" title="Remove">
          <span>${Pyrk.esc(selectedLabel(item, cfg.type))}</span><b>×</b>
        </button>`).join('') : '<span class="typeahead-placeholder">No one selected yet — search below and click +</span>'}
    </div>
    <div class="typeahead-control-row">
      <button class="mini-link" type="button" data-select-visible>Select visible</button>
      <button class="mini-link danger" type="button" data-clear-selected>Clear</button>
      <span>${selectedItems.length} selected · ${filtered.length} shown${groupMemberFilter ? ' · filtered by selected group' : ''}</span>
    </div>
    <div class="typeahead-dropdown ${cfg.state.open ? 'is-open' : ''}">
      ${filtered.length ? filtered.map(item => resultRowHtml(item, cfg.type, cfg.state.selected.has(itemId(item)))).join('') : `<div class="typeahead-empty"><strong>No results</strong><small>${Pyrk.esc(groupMemberFilter ? 'No members found inside the selected group with this search.' : cfg.empty)}</small></div>`}
    </div>`;

  box.querySelectorAll('[data-select-id]').forEach(btn => {
    btn.addEventListener('mousedown', e => e.preventDefault());
    btn.addEventListener('click', e => {
      e.preventDefault();
      e.stopPropagation();
      const id = Number(btn.dataset.selectId);
      if (cfg.state.selected.has(id)) cfg.state.selected.delete(id);
      else cfg.state.selected.add(id);
      cfg.state.open = true;
      renderInviteTypeahead(key);
      if (cfg.type === 'group') {
        syncGroupMemberFilter(context);
        renderInviteTypeahead(context === 'modal' ? 'modalMembers' : 'createMembers');
        renderGroupMemberAssistant(context);
      }
      updateInviteCount();
      focusInviteSearch(key);
    });
  });

  box.querySelectorAll('[data-remove-id]').forEach(btn => {
    btn.addEventListener('mousedown', e => e.preventDefault());
    btn.addEventListener('click', e => {
      e.preventDefault();
      e.stopPropagation();
      cfg.state.selected.delete(Number(btn.dataset.removeId));
      cfg.state.open = true;
      renderInviteTypeahead(key);
      if (cfg.type === 'group') {
        syncGroupMemberFilter(context);
        renderInviteTypeahead(context === 'modal' ? 'modalMembers' : 'createMembers');
        renderGroupMemberAssistant(context);
      }
      updateInviteCount();
      focusInviteSearch(key);
    });
  });

  box.querySelector('[data-select-visible]')?.addEventListener('mousedown', e => e.preventDefault());
  box.querySelector('[data-select-visible]')?.addEventListener('click', e => {
    e.preventDefault();
    e.stopPropagation();
    filtered.forEach(item => cfg.state.selected.add(itemId(item)));
    cfg.state.open = true;
    renderInviteTypeahead(key);
    if (cfg.type === 'group') {
      syncGroupMemberFilter(context);
      renderInviteTypeahead(context === 'modal' ? 'modalMembers' : 'createMembers');
      renderGroupMemberAssistant(context);
    }
    updateInviteCount();
    focusInviteSearch(key);
  });

  box.querySelector('[data-clear-selected]')?.addEventListener('mousedown', e => e.preventDefault());
  box.querySelector('[data-clear-selected]')?.addEventListener('click', e => {
    e.preventDefault();
    e.stopPropagation();
    cfg.state.selected.clear();
    cfg.state.open = true;
    renderInviteTypeahead(key);
    if (cfg.type === 'group') {
      syncGroupMemberFilter(context);
      renderInviteTypeahead(context === 'modal' ? 'modalMembers' : 'createMembers');
      renderGroupMemberAssistant(context);
    }
    updateInviteCount();
    focusInviteSearch(key);
  });

  updateInviteCount();
}

function renderCreateInvitationChoices() {
  renderInviteTypeahead('createGroups');
  renderInviteTypeahead('createMembers');
  renderGroupMemberAssistant('create');
}

function renderInvitationChoices() {
  renderInviteTypeahead('modalGroups');
  renderInviteTypeahead('modalMembers');
  renderGroupMemberAssistant('modal');
}

async function loadCreateInviteData() {
  const groupsBox = document.getElementById('createInviteGroupsList');
  const membersBox = document.getElementById('createInviteMembersList');
  if (groupsBox) groupsBox.innerHTML = '<p class="muted small">Loading groups...</p>';
  if (membersBox) membersBox.innerHTML = '<p class="muted small">Loading members...</p>';
  const res = await Pyrk.get('groups.php', { action: 'list' });
  if (!res.ok) {
    if (groupsBox) groupsBox.innerHTML = `<div class="notice bad">${Pyrk.esc(res.message)}</div>`;
    if (membersBox) membersBox.innerHTML = `<div class="notice bad">${Pyrk.esc(res.message)}</div>`;
    return;
  }
  createInviteData = res.data || { members: [], groups: [] };
  renderCreateInvitationChoices();
}


function isDeletedOrHiddenWalk(walk) {
  const status = String(walk?.status || '').toLowerCase().trim();
  return ['deleted','archived','removed','remove','trash','trashed','cancelled','canceled','inactive'].includes(status)
    || !!walk?.deleted_at
    || !!walk?.removed_at
    || !!walk?.archived_at
    || !!walk?.cancelled_at
    || !!walk?.canceled_at
    || !!walk?.deleted_by
    || !!walk?.removed_by
    || !!walk?.archived_by
    || Number(walk?.is_deleted || walk?.deleted || walk?.is_archived || walk?.archived || walk?.is_removed || 0) === 1;
}


function filteredWalks() {
  const q = (document.getElementById('walkSearch')?.value || '').toLowerCase().trim();
  return allWalks.filter(w => {
    const hay = `${w.title || ''} ${w.route_name || ''} ${w.start_address || ''} ${w.end_address || ''}`.toLowerCase();
    return !q || hay.includes(q);
  });
}

function isUpcomingWalkView() {
  return walkArchiveView === 'upcoming' || walkArchiveView === 'open';
}


function assignedWalkerNames(w) {
  const raw = String(w?.assigned_walker_names || '').trim();
  if (raw) return raw;
  const count = Number(w?.assigned_walker_count || 0);
  return count > 0 ? `${count} assigned` : 'Not assigned';
}

function assignedWalkerPill(w) {
  const names = assignedWalkerNames(w);
  const count = Number(w?.assigned_walker_count || 0);
  const label = count > 0 && String(w?.assigned_walker_names || '').trim()
    ? `Walkers: ${names}`
    : `Walkers: ${names}`;
  return `<span class="pill pill-team pill-walkers" title="${Pyrk.esc(label)}"><strong>Assigned walkers:</strong> ${Pyrk.esc(names)}</span>`;
}


function paymentText(w) {
  const model = String(w?.payment_model || 'free').toLowerCase();
  const amount = Number(w?.payment_amount || 0);
  const currency = String(w?.payment_currency || 'LBP').toUpperCase();
  if (model !== 'paid' || amount <= 0) return 'Free';
  return `${amount.toLocaleString(undefined, {minimumFractionDigits: amount % 1 ? 2 : 0, maximumFractionDigits: 2})} ${currency}`;
}

function paymentPill(w) {
  const paid = String(w?.payment_model || 'free').toLowerCase() === 'paid' && Number(w?.payment_amount || 0) > 0;
  return `<span class="pill ${paid ? 'payment-pill-paid' : 'payment-pill-free'}">${paid ? 'Paid: ' + Pyrk.esc(paymentText(w)) : 'Free walk'}</span>`;
}

function walkArchiveMeta(w) {
  if (walkArchiveView === 'archived') {
    const at = w.archived_at ? formatDateTime(w.archived_at) : 'Archived';
    const reason = String(w.archive_reason || '').trim();
    return `<p class="muted small archive-meta"><strong>Archived:</strong> ${Pyrk.esc(at)}${reason ? ' · ' + Pyrk.esc(reason) : ''}</p>`;
  }
  if (walkArchiveView === 'open') {
    const now = new Date();
    const todayIso = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
    const isPast = String(w.walk_date || '') < todayIso;
    return `<p class="muted small archive-meta"><strong>Open:</strong> ${isPast ? 'Date passed — still available for attendance, photos and voting until closed.' : 'Upcoming and still open.'}</p>`;
  }
  if (walkArchiveView === 'closed') {
    const finishedCount = Number(w.finished_count || 0);
    const status = String(w.status || '').trim() || 'closed';
    return `<p class="muted small archive-meta"><strong>Closed section:</strong> ${Pyrk.esc(status)} · ${finishedCount} finished attendee${finishedCount === 1 ? '' : 's'}</p>`;
  }
  return '';
}

function updateWalkArchiveToolbar() {
  document.querySelectorAll('[data-walk-view]').forEach(btn => {
    btn.classList.toggle('active', btn.dataset.walkView === walkArchiveView);
  });
  const title = document.querySelector('.walks-list-panel .section-head h2');
  const subtitle = document.querySelector('.walks-list-panel .section-head .muted.small');
  const notice = document.getElementById('walkArchiveNotice');
  const labels = {
    upcoming: 'Upcoming walks',
    open: 'Open walks',
    closed: 'Closed walks',
    archived: 'Archived walks'
  };
  const subtitles = {
    upcoming: 'Future walks appear here. Future walks marked Open also appear in the Open section.',
    open: 'All walks still marked Open remain here even after their date, until an admin closes or completes them.',
    closed: 'Only walks explicitly marked Closed or Completed appear here.',
    archived: 'Archived walks are hidden from members and active admin lists. You can restore them when needed.'
  };
  if (title) title.textContent = labels[walkArchiveView] || labels.upcoming;
  if (subtitle) subtitle.textContent = subtitles[walkArchiveView] || subtitles.upcoming;
  if (notice) {
    notice.hidden = walkArchiveView === 'upcoming';
    notice.innerHTML = walkArchiveView === 'archived'
      ? '<strong>Archived walks:</strong> kept safely for records and hidden from the member portal.'
      : (walkArchiveView === 'closed'
        ? '<strong>Closed walks:</strong> only status Closed or Completed moves a walk here.'
        : (walkArchiveView === 'open'
          ? '<strong>Open walks:</strong> the date does not remove a walk. Keep using Attendance, Photos &amp; voting, then mark it Closed or Completed when operations are finished.'
          : ''));
  }
}

function syncPaymentFields() {
  const model = document.getElementById('walkPaymentModel');
  const amount = document.getElementById('walkPaymentAmount');
  const currency = document.getElementById('walkPaymentCurrency');
  const preview = document.getElementById('walkPaymentPreview');
  if (!model || !amount || !currency) return;

  const paid = model.value === 'paid';
  amount.disabled = !paid;
  currency.disabled = !paid;
  if (!paid) amount.value = '0';

  if (preview) {
    const value = Number(amount.value || 0);
    preview.textContent = paid && value > 0 ? `Paid walk · ${paymentText({payment_model:'paid', payment_amount:value, payment_currency:currency.value})}` : (paid ? 'Paid walk · enter amount' : 'Free walk');
    preview.classList.toggle('is-paid', paid);
  }
}


function renderWalks() {
  const rows = filteredWalks();
  document.getElementById('walksList').innerHTML = rows.map(w => `
    <article class="walk-admin-card enterprise-walk-card" data-walk-card-id="${Number(w.id)}">
      <div class="walk-admin-card-main">
        <div class="route-icon">🚶</div>
        <div>
          <div class="inline-controls tiny-controls"><span class="pill pill-green">${Pyrk.esc(w.status || '')}</span><span class="pill ${w.access_type === 'invitation_only' ? 'pill-red' : 'pill-muted'}">${w.access_type === 'invitation_only' ? 'Invitation only' : 'Open registration'}</span>${paymentPill(w)}<span class="pill ${Number(w.allow_self_checkin || 0) === 1 ? 'pill-green' : 'pill-muted'}">${Number(w.allow_self_checkin || 0) === 1 ? 'Self check-in ON' : 'Admin check-in only'}</span><span class="pill pill-muted">${formatWalkDateDisplay(w.walk_date)} · ${formatTime(w.start_time)}</span><span class="pill pill-team">Manager: ${Pyrk.esc(w.manager_names || 'Not assigned')}</span><span class="pill pill-team">Head: ${Pyrk.esc(w.head_walker_names || 'Not assigned')}</span>${assignedWalkerPill(w)}</div>
          <h3>${Pyrk.esc(w.title)}</h3>
          <p class="muted"><strong>${Pyrk.esc(w.route_name)}</strong> · ${Number(w.total_km || 0).toFixed(1)} KM · Capacity ${Number(w.capacity || 0)}</p>
          <p class="muted small">${Pyrk.esc(w.start_address || 'Start not set')} → ${Pyrk.esc(w.end_address || 'Finish not set')}</p>
          ${walkArchiveMeta(w)}
        </div>
      </div>
      <div class="walk-admin-metrics">
        <div><strong>${Number(w.registered_count || 0)}</strong><span>Registered</span></div>
        <div><strong>${Number(w.checked_in_count || 0)}</strong><span>Checked in</span></div>
        <div><strong>${Number(w.finished_count || 0)}</strong><span>Finished</span></div>
        <div><strong>${Number(w.invited_member_count || 0) + Number(w.invited_group_count || 0)}</strong><span>Invites</span></div>
      </div>
      <div class="walk-admin-actions">
        ${isUpcomingWalkView() && canViewWalkRegistrants() ? `<button class="btn btn-light btn-small" type="button" onclick="openRegistrants(${w.id})">Registrants</button>` : ''}
        ${isUpcomingWalkView() && canOpenWalkTeam() ? `<button class="btn btn-light btn-small walk-team-btn" type="button" data-open-team="${w.id}">Team</button>` : ''}
        ${canCreateWalks() ? `<button class="btn btn-light btn-small walk-duplicate-btn" type="button" onclick="duplicateWalk(${w.id})">Duplicate</button>` : ''}
        ${isUpcomingWalkView() && canEditWalks() ? `<button class="btn btn-light btn-small" type="button" onclick="editWalk(${w.id})">Edit</button>` : ''}
        ${isUpcomingWalkView() && canEditWalks() ? `<button class="btn btn-light btn-small permission-btn-pro" type="button" onclick="openInvitations(${w.id})">Invite members</button>` : ''}
        ${isUpcomingWalkView() ? `<a class="btn btn-small" href="attendance.php?walk_id=${w.id}">Attendance</a>` : ''}
        ${walkArchiveView === 'open' && canManageWalkPhotos() ? `<a class="btn btn-light btn-small" href="photos.php?walk_id=${w.id}">Photos &amp; voting</a>` : ''}
        ${isUpcomingWalkView() && (canEditWalks() || canDeleteWalks()) ? `<button class="btn btn-light btn-small archive-walk-btn" type="button" onclick="archiveWalk(${w.id}, '${Pyrk.esc(w.title || 'walk').replace(/'/g, '&#39;')}')">Archive</button>` : ''}
        ${walkArchiveView === 'archived' && (canEditWalks() || canDeleteWalks()) ? `<button class="btn btn-small restore-walk-btn" type="button" onclick="restoreWalk(${w.id}, '${Pyrk.esc(w.title || 'walk').replace(/'/g, '&#39;')}')">Restore</button>` : ''}
        ${canDeleteWalks() ? `<button class="btn btn-danger btn-small" type="button" onclick="deleteWalk(${w.id}, '${Pyrk.esc(w.title || 'walk').replace(/'/g, '&#39;')}')">Delete</button>` : ''}
      </div>
    </article>`).join('') || `<div class="empty-state walk-empty-state"><strong>No walks visible for this account.</strong><span>${Pyrk.esc((document.getElementById('walkSearch')?.value || '').trim() ? 'No walks match your search.' : walkArchiveView === 'archived' ? 'No archived walks found.' : (walkArchiveView === 'closed' ? 'No Closed or Completed walks found.' : (walkArchiveView === 'open' ? 'No walks are currently marked Open.' : 'No upcoming walks found. Future Open walks will appear here and in Open.')))}</span></div>`;
}



function datetimeLocalValue(value) {
  if (!value) return '';
  return String(value).replace(' ', 'T').slice(0, 16);
}

function timeValue(value) {
  if (!value) return '';
  return String(value).slice(0, 5);
}

function walkNotificationUseCase() {
  const form = document.getElementById('walkForm');
  const access = String(form?.elements['access_type']?.value || 'open');
  if (editingWalkId) return 'walk_update';
  return access === 'invitation_only' ? 'invitation_only' : 'open_walk';
}

function walkNotificationPresetById(id) {
  return walkPushPresets.find(preset => Number(preset.id) === Number(id)) || null;
}

function walkNotificationDefaultPreset() {
  const useCase = walkNotificationUseCase();
  return walkPushPresets.find(preset => preset.use_case === useCase && Number(preset.is_default) === 1 && Number(preset.is_active) === 1)
    || walkPushPresets.find(preset => preset.use_case === useCase && Number(preset.is_active) === 1)
    || null;
}

function walkNotificationPresetLabel(preset) {
  if (!preset) return '';
  const context = walkPushPresetUseCases[preset.use_case] || String(preset.use_case || 'custom').replace(/_/g, ' ');
  return `${preset.preset_name || 'Preset'} · ${context}${Number(preset.is_default) === 1 ? ' · Default' : ''}`;
}

function renderWalkNotificationPresetOptions() {
  const select = document.getElementById('walkNotifyPreset');
  const wrap = select?.closest('.walk-preset-picker-pro');
  const help = document.getElementById('walkNotifyPresetHelp');
  if (!select) return;

  const previous = String(select.value || '');
  if (!walkPushPresetsInstalled) {
    select.innerHTML = '<option value="">Presets not installed</option>';
    select.disabled = true;
    wrap?.classList.add('is-unavailable');
    if (help) help.textContent = 'Import migration 044_push_notification_presets.sql to create and manage Push presets. The existing professional fallback still works.';
    return;
  }

  select.disabled = false;
  wrap?.classList.remove('is-unavailable');
  const useCaseOrder = ['open_walk','invitation_only','walk_update','reminder','cancelled','custom'];
  const groups = [];
  useCaseOrder.forEach(useCase => {
    const rows = walkPushPresets.filter(preset => preset.use_case === useCase && Number(preset.is_active) === 1);
    if (!rows.length) return;
    const label = walkPushPresetUseCases[useCase] || useCase.replace(/_/g, ' ');
    groups.push(`<optgroup label="${Pyrk.esc(label)}">${rows.map(preset => `<option value="${Number(preset.id)}">${Pyrk.esc(walkNotificationPresetLabel(preset))}</option>`).join('')}</optgroup>`);
  });
  const ungrouped = walkPushPresets.filter(preset => !useCaseOrder.includes(String(preset.use_case || '')) && Number(preset.is_active) === 1);
  if (ungrouped.length) groups.push(`<optgroup label="Other">${ungrouped.map(preset => `<option value="${Number(preset.id)}">${Pyrk.esc(walkNotificationPresetLabel(preset))}</option>`).join('')}</optgroup>`);

  select.innerHTML = '<option value="">Choose a preset…</option>' + groups.join('');
  if (previous && walkNotificationPresetById(previous)) select.value = previous;
  if (help) help.textContent = walkPushPresets.length
    ? 'Choose a saved preset and click Apply preset. You can still edit the headline and message for this one walk.'
    : 'No active presets exist yet. Open Manage presets to create the first one.';
}

async function loadWalkPushPresets() {
  const select = document.getElementById('walkNotifyPreset');
  if (!select || typeof Pyrk?.get !== 'function') return;
  try {
    const res = await Pyrk.get('push_notification_presets.php', { action: 'list', active_only: 1 });
    if (!res.ok) throw new Error(res.message || 'Could not load Push presets.');
    walkPushPresetsInstalled = Number(res.data?.installed || 0) === 1;
    walkPushPresets = Array.isArray(res.data?.presets) ? res.data.presets : [];
    walkPushPresetUseCases = res.data?.use_cases || {};
    walkPushPresetsLoaded = true;
    renderWalkNotificationPresetOptions();
    walkNotificationDefaultText(false);
  } catch (error) {
    walkPushPresetsLoaded = true;
    walkPushPresetsInstalled = false;
    renderWalkNotificationPresetOptions();
    const help = document.getElementById('walkNotifyPresetHelp');
    if (help) help.textContent = error?.message || 'Could not load presets. The professional fallback remains available.';
  }
}

function walkWhatsAppPresetById(id) {
  return walkWhatsAppPresets.find(preset => Number(preset.id) === Number(id)) || null;
}

function walkWhatsAppDefaultPreset() {
  const useCase = walkNotificationUseCase();
  return walkWhatsAppPresets.find(preset => preset.use_case === useCase && Number(preset.is_default) === 1 && Number(preset.is_active) === 1)
    || walkWhatsAppPresets.find(preset => preset.use_case === useCase && Number(preset.is_active) === 1)
    || walkWhatsAppPresets.find(preset => Number(preset.is_default) === 1 && Number(preset.is_active) === 1)
    || walkWhatsAppPresets.find(preset => Number(preset.is_active) === 1)
    || null;
}

function walkWhatsAppPresetLabel(preset) {
  if (!preset) return '';
  const context = walkWhatsAppPresetUseCases[preset.use_case] || String(preset.use_case || 'custom').replace(/_/g, ' ');
  return `${preset.preset_name || 'Preset'} · ${context}${Number(preset.is_default) === 1 ? ' · Default' : ''}`;
}

function syncWalkWhatsAppPresetSelection(force = false) {
  const select = document.getElementById('walkNotifyWhatsAppPreset');
  const hidden = document.getElementById('walkNotifyWhatsAppPresetId');
  if (!select || !hidden) return;
  const selected = walkWhatsAppPresetById(select.value);
  const fallback = walkWhatsAppDefaultPreset();
  if ((force || !select.dataset.userChoice || !selected) && fallback) select.value = String(fallback.id || '');
  const resolved = walkWhatsAppPresetById(select.value);
  hidden.value = resolved ? String(resolved.id) : '';
}

function renderWalkWhatsAppPresetOptions() {
  const select = document.getElementById('walkNotifyWhatsAppPreset');
  const picker = document.getElementById('walkWhatsAppPresetPicker');
  const help = document.getElementById('walkNotifyWhatsAppPresetHelp');
  if (!select) return;
  const previous = String(select.value || '');
  if (!walkWhatsAppPresetsInstalled) {
    select.innerHTML = '<option value="">WhatsApp presets not installed</option>';
    select.disabled = true;
    picker?.classList.add('is-unavailable');
    const hidden = document.getElementById('walkNotifyWhatsAppPresetId');
    if (hidden) hidden.value = '';
    if (help) help.textContent = 'The existing WhatsApp preset migration is missing from this source package. WhatsApp delivery stays blocked unless its preset table already exists.';
    return;
  }
  select.disabled = false;
  picker?.classList.remove('is-unavailable');
  const order = ['open_walk','invitation_only','walk_update','reminder','cancelled','custom'];
  const groups = [];
  order.forEach(useCase => {
    const rows = walkWhatsAppPresets.filter(preset => preset.use_case === useCase && Number(preset.is_active) === 1);
    if (!rows.length) return;
    const label = walkWhatsAppPresetUseCases[useCase] || useCase.replace(/_/g, ' ');
    groups.push(`<optgroup label="${Pyrk.esc(label)}">${rows.map(preset => `<option value="${Number(preset.id)}">${Pyrk.esc(walkWhatsAppPresetLabel(preset))}</option>`).join('')}</optgroup>`);
  });
  const ungrouped = walkWhatsAppPresets.filter(preset => !order.includes(String(preset.use_case || '')) && Number(preset.is_active) === 1);
  if (ungrouped.length) groups.push(`<optgroup label="Other">${ungrouped.map(preset => `<option value="${Number(preset.id)}">${Pyrk.esc(walkWhatsAppPresetLabel(preset))}</option>`).join('')}</optgroup>`);
  select.innerHTML = '<option value="">Choose a WhatsApp preset…</option>' + groups.join('');
  if (previous && walkWhatsAppPresetById(previous)) select.value = previous;
  syncWalkWhatsAppPresetSelection(false);
  if (help) help.textContent = walkWhatsAppPresets.length
    ? 'WhatsApp uses this separate preset. Changing the Push preset does not change WhatsApp content.'
    : 'No active WhatsApp presets are available. Manage WhatsApp presets before enabling WhatsApp.';
}

async function loadWalkWhatsAppPresets() {
  const select = document.getElementById('walkNotifyWhatsAppPreset');
  if (!select || typeof Pyrk?.get !== 'function') return;
  try {
    const res = await Pyrk.get('whatsapp_presets.php', { action: 'list', active_only: 1 });
    if (!res.ok) throw new Error(res.message || 'Could not load WhatsApp presets.');
    walkWhatsAppPresetsInstalled = Number(res.data?.installed || 0) === 1;
    walkWhatsAppPresets = Array.isArray(res.data?.presets) ? res.data.presets : [];
    walkWhatsAppPresetUseCases = res.data?.use_cases || {};
    walkWhatsAppPresetsLoaded = true;
    renderWalkWhatsAppPresetOptions();
  } catch (error) {
    walkWhatsAppPresetsLoaded = true;
    walkWhatsAppPresetsInstalled = false;
    renderWalkWhatsAppPresetOptions();
    const help = document.getElementById('walkNotifyWhatsAppPresetHelp');
    if (help) help.textContent = error?.message || 'Could not load WhatsApp presets.';
  }
}

function applyWalkNotificationPreset(preset, options = {}) {
  if (!preset) return false;
  const subject = document.getElementById('walkNotifySubject');
  const message = document.getElementById('walkNotifyMessage');
  const audience = document.getElementById('walkNotifyAudience');
  const select = document.getElementById('walkNotifyPreset');
  const presetIdInput = document.getElementById('walkNotifyPresetId');
  const force = options.force !== false;

  if (subject && (force || !subject.dataset.touched)) subject.value = String(preset.subject_template || '');
  if (message && (force || !message.dataset.touched)) message.value = String(preset.message_template || '');
  if (select) { select.value = String(preset.id || ''); select.dataset.userChoice = '1'; select.dataset.presetApplied = '1'; }
  if (presetIdInput) presetIdInput.value = String(preset.id || '');
  if (subject) subject.dataset.touched = '1';
  if (message) message.dataset.touched = '1';

  const help = document.getElementById('walkNotifyPresetHelp');
  if (help) help.textContent = `Applied “${preset.preset_name || 'preset'}”. You may edit the text below for this walk without changing the saved preset.`;
  return true;
}

function walkNotificationProfessionalTemplate(accessType = 'open', context = 'create') {
  const invited = String(accessType || 'open') === 'invitation_only';
  const isUpdate = context === 'update';

  if (isUpdate) {
    return {
      subject: 'Important walk update: {walk_title}',
      message: `Hello {first_name},

There is an update to your Pyrk walk. Please review the latest details below.

🥾 {walk_title}
📅 {walk_date}
🕒 {walk_time}
📍 {route_name}
📏 {distance_km} KM

Open the walk to review the updated information${invited ? ', confirm your invitation' : ''}, and complete registration or waiver steps if needed:
{walk_link}

Thank you,
Pyrk Walking Club`
    };
  }

  return {
    subject: invited ? 'You are invited: {walk_title}' : 'New Pyrk walk: {walk_title}',
    message: invited
      ? `Hello {first_name},

You are invited to join an upcoming Pyrk walk.

🥾 {walk_title}
📅 {walk_date}
🕒 {walk_time}
📍 {route_name}
📏 {distance_km} KM

Please review the walk details, sign the waiver if required, and register here:
{walk_link}

See you on the trail!
Pyrk Walking Club`
      : `Hello {first_name},

A new Pyrk walk is now available.

🥾 {walk_title}
📅 {walk_date}
🕒 {walk_time}
📍 {route_name}
📏 {distance_km} KM

View the walk details, sign the waiver if required, and register here:
{walk_link}

See you on the trail!
Pyrk Walking Club`
  };
}

function walkNotificationDefaultText(force = false) {
  const form = document.getElementById('walkForm');
  if (!form) return;
  const access = String(form.elements['access_type']?.value || 'open');
  const presetSelect = document.getElementById('walkNotifyPreset');
  const selectedId = String(presetSelect?.value || '');
  const selectedPreset = walkNotificationPresetById(selectedId);
  const preset = force
    ? (selectedPreset || walkNotificationDefaultPreset())
    : (presetSelect?.dataset.presetApplied === '1' && selectedPreset ? selectedPreset : walkNotificationDefaultPreset());
  const template = preset
    ? { subject: String(preset.subject_template || ''), message: String(preset.message_template || '') }
    : (walkPushPresetsLoaded && walkPushPresetsInstalled
        ? { subject: '', message: '' }
        : walkNotificationProfessionalTemplate(access, editingWalkId ? 'update' : 'create'));
  const subject = document.getElementById('walkNotifySubject');
  const message = document.getElementById('walkNotifyMessage');
  const audience = document.getElementById('walkNotifyAudience');
  const hint = document.getElementById('walkNotifyHint');
  const select = document.getElementById('walkNotifyPreset');
  const presetIdInput = document.getElementById('walkNotifyPresetId');
  const pendingPresetSelection = !force && select?.dataset.userChoice === '1' && select?.dataset.presetApplied !== '1';

  if (!pendingPresetSelection && subject && (force || !subject.dataset.touched)) subject.value = template.subject;
  if (!pendingPresetSelection && message && (force || !message.dataset.touched)) message.value = template.message;
  if (preset && select && (force || select.dataset.userChoice !== '1')) select.value = String(preset.id);
  if (!pendingPresetSelection && preset && presetIdInput && (force || !subject?.dataset.touched || !message?.dataset.touched)) presetIdInput.value = String(preset.id);
  if (!pendingPresetSelection && !preset && presetIdInput && walkPushPresetsLoaded && walkPushPresetsInstalled) presetIdInput.value = '';

  if (force) {
    if (subject) delete subject.dataset.touched;
    if (message) delete message.dataset.touched;
  }

  if (audience && hint) {
    if (audience.value === 'all') {
      hint.textContent = 'The notification will go to all active members.';
    } else if (audience.value === 'invited') {
      hint.textContent = 'The notification will go only to members and groups invited to this walk.';
    } else {
      hint.textContent = 'Choose exactly who should receive this notification.';
    }
  }
}

function insertWalkNotificationToken(token) {
  const subject = document.getElementById('walkNotifySubject');
  const message = document.getElementById('walkNotifyMessage');
  const active = document.activeElement;
  const target = active === subject || active === message ? active : message;
  if (!target || !token) return;

  const start = Number.isInteger(target.selectionStart) ? target.selectionStart : target.value.length;
  const end = Number.isInteger(target.selectionEnd) ? target.selectionEnd : start;
  const before = target.value.slice(0, start);
  const after = target.value.slice(end);
  const needsLeadingSpace = before && !/[\s\n]$/.test(before);
  const needsTrailingSpace = after && !/^[\s\n.,!?;:]/.test(after);
  const inserted = `${needsLeadingSpace ? ' ' : ''}${token}${needsTrailingSpace ? ' ' : ''}`;

  target.value = before + inserted + after;
  target.dataset.touched = '1';
  target.focus();
  const caret = before.length + inserted.length;
  target.setSelectionRange(caret, caret);
  target.dispatchEvent(new Event('input', { bubbles: true }));
}

function syncWalkWhatsAppCreateOption(options = {}) {
  const enabled = document.getElementById('walkNotifyEnabled');
  const checkbox = document.getElementById('walkNotifyWhatsApp');
  const card = document.getElementById('walkWhatsAppCreateCard');
  const note = document.getElementById('walkWhatsAppCreateNote');
  const description = document.getElementById('walkWhatsAppCreateDescription');
  const label = document.getElementById('walkNotifyWhatsAppLabel');
  const repeatWrap = document.getElementById('walkWhatsAppRepeatOverride');
  const repeatCheckbox = document.getElementById('walkNotifyWhatsAppRepeat');
  if (!checkbox) return;

  const configured = checkbox.dataset.configured === '1';
  const isCreateMode = !editingWalkId;
  const notificationEnabled = Boolean(enabled?.checked);

  // WhatsApp is deliberately available on both create and edit. It always remains
  // dependent on the main notification switch, so an ordinary walk edit cannot send.
  checkbox.disabled = !configured || !notificationEnabled;

  if (!notificationEnabled || !configured) {
    checkbox.checked = false;
  } else if (isCreateMode && options.autoSelect === true && checkbox.dataset.userChoice !== '1') {
    // New walks default to WhatsApp when the channel is ready. Edits never auto-select
    // WhatsApp, preventing an accidental resend while changing ordinary walk details.
    checkbox.checked = true;
  }

  card?.classList.toggle('is-form-disabled', checkbox.disabled);
  card?.classList.toggle('is-selected', Boolean(checkbox.checked && !checkbox.disabled));
  card?.classList.toggle('is-edit-mode', !isCreateMode);

  const showRepeatOverride = !isCreateMode && configured && notificationEnabled && checkbox.checked;
  if (repeatWrap) repeatWrap.hidden = !showRepeatOverride;
  if (!showRepeatOverride && repeatCheckbox) repeatCheckbox.checked = false;

  if (label) {
    label.textContent = isCreateMode ? 'Also send by WhatsApp' : 'Send this walk update by WhatsApp';
  }
  if (description) {
    description.textContent = isCreateMode
      ? 'Queue the selected WhatsApp preset after creating the walk. It remains separate from the Push preset and the cron sends it gradually.'
      : 'Send a clear update about this existing walk. WhatsApp is never selected automatically while editing, and the queue still sends gradually.';
  }

  if (note) {
    if (!configured) note.textContent = 'Complete the UltraMsg instance/token setup before enabling this option.';
    else if (!notificationEnabled && !isCreateMode) note.textContent = 'Safe edit mode: turn on “Send notification” above, then explicitly select WhatsApp. Nothing is sent during a normal walk update.';
    else if (!notificationEnabled) note.textContent = 'Turn on “Send notification” above to enable WhatsApp for this new walk.';
    else if (!isCreateMode && checkbox.checked) note.textContent = 'This update will be queued for the selected audience after saving and sent gradually by the cron.';
    else if (!isCreateMode) note.textContent = 'WhatsApp is off by default for edits. Select it only when members should receive this walk update.';
    else if (checkbox.checked) note.textContent = 'WhatsApp is selected. The message will be queued safely after creation and sent gradually by the cron.';
    else note.textContent = 'WhatsApp is turned off for this walk. Enable it before creating the walk to queue the message.';
  }
}

function syncWalkNotificationCard(options = {}) {
  const enabled = document.getElementById('walkNotifyEnabled');
  const fields = document.getElementById('walkNotificationFields');
  // Keep the notification controls visible while editing so the administrator can
  // clearly see the optional WhatsApp update action. Sending remains OFF until the
  // main notification switch is explicitly enabled.
  if (fields && enabled) fields.hidden = !enabled.checked && !editingWalkId;
  syncWalkWhatsAppCreateOption(options);
  walkNotificationDefaultText();
  if (walkWhatsAppPresetsLoaded) syncWalkWhatsAppPresetSelection(false);
}

function setWalkFormMode(mode, walk = null) {
  const isEdit = mode === 'edit';
  editingWalkId = isEdit && walk ? Number(walk.id) : null;
  const form = document.getElementById('walkForm');
  if (!form) return;

  document.getElementById('walkId').value = editingWalkId || '';
  document.getElementById('walkFormEyebrow').textContent = isEdit ? 'Edit' : 'Create';
  document.getElementById('walkFormTitle').textContent = isEdit ? 'Edit walk and permissions' : 'Schedule walk';
  document.getElementById('saveWalkBtn').textContent = isEdit ? 'Update walk' : 'Create walk';
  document.getElementById('cancelWalkEditBtn').hidden = !isEdit;

  if (!isEdit) {
    form.reset();
    const routeHidden = document.getElementById('routeSelect');
    const routeInput = document.getElementById('routeSearchInput');
    if (routeHidden) routeHidden.value = '';
    if (routeInput) routeInput.value = '';
    ['walkDateInput','registrationDeadlineInput','votingDeadlineInput'].forEach(id => { const el = document.getElementById(id); if (el) el.value = ''; });
    inviteState.createMembers.selected.clear();
    inviteState.createGroups.selected.clear();
    inviteState.createMembers.open = false;
    inviteState.createGroups.open = false;
    inviteState.createMemberFilterByGroups = false;
    document.getElementById('createInviteMemberSearch').value = '';
    document.getElementById('createInviteGroupSearch').value = '';
    const createSelfCheckin = document.getElementById('createAllowSelfCheckin');
    if (createSelfCheckin) createSelfCheckin.checked = false;
    const createNotifyNewInvitees = document.getElementById('createNotifyNewInvitees');
    if (createNotifyNewInvitees) createNotifyNewInvitees.checked = true;
    const notifyEnabled = document.getElementById('walkNotifyEnabled');
    const notifyWhatsApp = document.getElementById('walkNotifyWhatsApp');
    const notifyAudience = document.getElementById('walkNotifyAudience');
    const notifySubject = document.getElementById('walkNotifySubject');
    const notifyMessage = document.getElementById('walkNotifyMessage');
    const notifyPreset = document.getElementById('walkNotifyPreset');
    const notifyPresetId = document.getElementById('walkNotifyPresetId');
    const notifyWhatsAppRepeat = document.getElementById('walkNotifyWhatsAppRepeat');
    if (notifyEnabled) notifyEnabled.checked = false;
    if (notifyWhatsApp) {
      notifyWhatsApp.checked = false;
      delete notifyWhatsApp.dataset.userChoice;
    }
    if (notifyWhatsAppRepeat) notifyWhatsAppRepeat.checked = false;
    if (notifyAudience) notifyAudience.value = '';
    if (notifyPreset) { notifyPreset.value = ''; delete notifyPreset.dataset.userChoice; delete notifyPreset.dataset.presetApplied; }
    if (notifyPresetId) notifyPresetId.value = '';
    const notifyWhatsAppPreset = document.getElementById('walkNotifyWhatsAppPreset');
    const notifyWhatsAppPresetId = document.getElementById('walkNotifyWhatsAppPresetId');
    if (notifyWhatsAppPreset) { notifyWhatsAppPreset.value = ''; delete notifyWhatsAppPreset.dataset.userChoice; }
    if (notifyWhatsAppPresetId) notifyWhatsAppPresetId.value = '';
    if (notifySubject) { notifySubject.value = ''; delete notifySubject.dataset.touched; }
    if (notifyMessage) { notifyMessage.value = ''; delete notifyMessage.dataset.touched; }
    syncWalkNotificationCard();
    const payModel = document.getElementById('walkPaymentModel');
    const payAmount = document.getElementById('walkPaymentAmount');
    const payCurrency = document.getElementById('walkPaymentCurrency');
    if (payModel) payModel.value = 'free';
    if (payAmount) payAmount.value = '0';
    if (payCurrency) payCurrency.value = 'LBP';
    syncPaymentFields();
    renderCreateInvitationChoices();
  }
}

function setCreateInviteSelectionsFromSummary(invitations = {}) {
  inviteState.createMembers.selected.clear();
  inviteState.createGroups.selected.clear();
  (invitations.members || []).forEach(item => {
    if (item.user_id || item.id) inviteState.createMembers.selected.add(Number(item.user_id || item.id));
  });
  (invitations.groups || []).forEach(item => {
    if (item.group_id || item.id) inviteState.createGroups.selected.add(Number(item.group_id || item.id));
  });
  renderCreateInvitationChoices();
  updateInviteCount();
}

window.editWalk = async function editWalk(id) {
  if (!canEditWalks()) { alert('You do not have permission to edit walks.'); return; }
  const form = document.getElementById('walkForm');
  if (!form) return;

  const res = await Pyrk.get('walks.php', { action: 'invitations', id });
  if (!res.ok) { alert(res.message || 'Could not load walk.'); return; }

  const w = res.data.walk || {};
  setWalkFormMode('edit', w);

  setRouteById(w.route_id || '');
  form.elements['title'].value = w.title || '';
  form.elements['description'].value = w.description || '';
  form.elements['walk_date'].value = isoToDdMmYyyy(String(w.walk_date || '').slice(0, 10));
  form.elements['start_time'].value = timeValue(w.start_time);
  form.elements['expected_finish_time'].value = timeValue(w.expected_finish_time);
  form.elements['capacity'].value = Number(w.capacity || 0);
  form.elements['registration_deadline'].value = isoDateTimeToDdMmYyyyHm(w.registration_deadline);
  form.elements['voting_deadline'].value = isoDateTimeToDdMmYyyyHm(w.voting_deadline);
  form.elements['status'].value = w.status || 'open';
  form.elements['access_type'].value = w.access_type || 'open';
  if (form.elements['payment_model']) form.elements['payment_model'].value = w.payment_model || 'free';
  if (form.elements['payment_amount']) form.elements['payment_amount'].value = Number(w.payment_amount || 0);
  if (form.elements['payment_currency']) form.elements['payment_currency'].value = w.payment_currency || 'LBP';
  syncPaymentFields();
  form.elements['invite_note'].value = w.invite_note || '';
  form.elements['waiver_required'].checked = Number(w.waiver_required || 0) === 1;
  const selfCheck = document.getElementById('createAllowSelfCheckin');
  if (selfCheck) selfCheck.checked = Number(w.allow_self_checkin || 0) === 1;
  const notifyEnabled = document.getElementById('walkNotifyEnabled');
  const notifyWhatsApp = document.getElementById('walkNotifyWhatsApp');
  const notifyAudience = document.getElementById('walkNotifyAudience');
  const notifySubject = document.getElementById('walkNotifySubject');
  const notifyMessage = document.getElementById('walkNotifyMessage');
  const notifyPreset = document.getElementById('walkNotifyPreset');
  const notifyPresetId = document.getElementById('walkNotifyPresetId');
  const notifyWhatsAppRepeat = document.getElementById('walkNotifyWhatsAppRepeat');
  if (notifyEnabled) notifyEnabled.checked = false;
  if (notifyWhatsApp) {
    notifyWhatsApp.checked = false;
    delete notifyWhatsApp.dataset.userChoice;
  }
  if (notifyWhatsAppRepeat) notifyWhatsAppRepeat.checked = false;
  if (notifyAudience) notifyAudience.value = '';
  if (notifyPreset) { notifyPreset.value = ''; delete notifyPreset.dataset.userChoice; delete notifyPreset.dataset.presetApplied; }
  if (notifyPresetId) notifyPresetId.value = '';
  const notifyWhatsAppPreset = document.getElementById('walkNotifyWhatsAppPreset');
  const notifyWhatsAppPresetId = document.getElementById('walkNotifyWhatsAppPresetId');
  if (notifyWhatsAppPreset) { notifyWhatsAppPreset.value = ''; delete notifyWhatsAppPreset.dataset.userChoice; }
  if (notifyWhatsAppPresetId) notifyWhatsAppPresetId.value = '';
  if (notifySubject) { notifySubject.value = ''; delete notifySubject.dataset.touched; }
  if (notifyMessage) { notifyMessage.value = ''; delete notifyMessage.dataset.touched; }
  syncWalkNotificationCard();

  createInviteData.members = res.data.members || createInviteData.members || [];
  createInviteData.groups = res.data.groups || createInviteData.groups || [];
  setCreateInviteSelectionsFromSummary(res.data.invitations || {});
  syncGroupMemberFilter('create');
  renderInviteTypeahead('createMembers');

  form.scrollIntoView({ behavior: 'smooth', block: 'start' });
};

window.archiveWalk = async function archiveWalk(id, name = 'this walk') {
  if (!(canEditWalks() || canDeleteWalks())) { alert('You do not have permission to archive walks.'); return; }
  const reason = prompt(`Archive ${name}?\n\nArchived walks will be hidden from clients and active admin lists, but records/photos/attendance are kept.\n\nOptional archive note:`);
  if (reason === null) return;
  const res = await Pyrk.post('walks.php', {action:'archive', id, reason});
  alert(res.message || (res.ok ? 'Archived' : 'Error'));
  if (res.ok) await loadWalks();
};

window.restoreWalk = async function restoreWalk(id, name = 'this walk') {
  if (!(canEditWalks() || canDeleteWalks())) { alert('You do not have permission to restore walks.'); return; }
  const confirmed = confirm(`Restore ${name}?\n\nIt will appear again in the active admin walk list.`);
  if (!confirmed) return;
  const res = await Pyrk.post('walks.php', {action:'restore', id});
  alert(res.message || (res.ok ? 'Restored' : 'Error'));
  if (res.ok) await loadWalks();
};

window.deleteWalk = async function deleteWalk(id, name = 'this walk') {
  if (!canDeleteWalks()) { alert('You do not have permission to delete walks.'); return; }
  const confirmed = confirm(`Delete ${name} permanently?\n\nThis removes registrations, attendance, invitations and walk photos linked to this walk. This action cannot be undone.`);
  if (!confirmed) return;
  const res = await Pyrk.post('walks.php', {action:'delete', id});
  alert(res.message || (res.ok ? 'Deleted' : 'Error'));
  if (res.ok) await loadWalks();
};

async function loadWalks() {
  updateWalkArchiveToolbar();
  const actionMap = {
    upcoming: 'admin_upcoming',
    open: 'admin_open',
    closed: 'admin_closed',
    archived: 'archived'
  };
  const apiAction = actionMap[walkArchiveView] || 'admin_upcoming';
  let res = await Pyrk.get('walks.php', { action: apiAction });

  // Backward-compatible fallback if an older API file is still cached/uploaded.
  if (!res.ok && /unsupported|not found/i.test(String(res.message || ''))) {
    res = await Pyrk.get('walks.php', { action: walkArchiveView === 'archived' ? 'archived' : (walkArchiveView === 'closed' ? 'admin_finished' : (walkArchiveView === 'open' ? 'admin_all' : 'list')) });
  }

  const list = document.getElementById('walksList');
  if (!res.ok) {
    if (list) list.innerHTML = `<div class="notice bad"><strong>Could not load walks.</strong><br>${Pyrk.esc(res.message || 'Unknown error')}<br><small>Check that api/walks.php was replaced and the walk permissions SQL was run.</small></div>`;
    return;
  }

  const responseWalks = Array.isArray(res.data?.walks) ? res.data.walks : [];
  if (walkArchiveView === 'open') {
    allWalks = responseWalks.filter(w => String(w.status || '').toLowerCase().trim() === 'open' && !isDeletedOrHiddenWalk(w));
  } else if (walkArchiveView === 'closed') {
    allWalks = responseWalks.filter(w => ['closed', 'completed'].includes(String(w.status || '').toLowerCase().trim()));
  } else if (walkArchiveView === 'archived') {
    allWalks = responseWalks;
  } else {
    allWalks = responseWalks.filter(w => !isDeletedOrHiddenWalk(w));
  }
  renderWalks();
}

window.openRegistrants = async function openRegistrants(walkId) {
  if (!canViewWalkRegistrants()) {
    alert('You do not have permission to view registrants. Enable attendance.manage or a walk permission.');
    return;
  }
  openModal('walkRegistrantsModal');
  document.getElementById('registrantsTitle').textContent = 'Registered members';
  document.getElementById('registrantsSubTitle').textContent = 'Loading...';
  const tableBody = document.getElementById('registrantsTableBody');
  const apiUrl = Pyrk.url('walks.php', { action: 'registrants', id: walkId });
  tableBody.innerHTML = '<tr><td colspan="9" class="muted">Loading registrants...</td></tr>';
  const failTimer = setTimeout(() => {
    tableBody.innerHTML = `<tr><td colspan="9"><div class="notice bad modal-error-box"><strong>Registrants are taking too long to load.</strong><br><small>Debug URL: ${Pyrk.esc(apiUrl)}</small><br><br><button class="btn btn-light btn-small" type="button" onclick="Pyrk.closeModal('walkRegistrantsModal')">Close</button></div></td></tr>`;
  }, 6500);
  const res = await Pyrk.get('walks.php', { action: 'registrants', id: walkId });
  clearTimeout(failTimer);
  if (!res.ok) {
    tableBody.innerHTML = `<tr><td colspan="9"><div class="notice bad modal-error-box"><strong>Could not load registrants.</strong><br>${Pyrk.esc(res.message)}<br><small>Debug URL: ${Pyrk.esc(apiUrl)}</small></div></td></tr>`;
    return;
  }
  const walk = res.data.walk || {};
  const regs = res.data.registrants || [];
  document.getElementById('registrantsTitle').textContent = walk.title || 'Registered members';
  document.getElementById('registrantsSubTitle').textContent = `${walk.route_name || ''} · ${Pyrk.formatDate(walk.walk_date)} ${formatTime(walk.start_time)} · ${Number(walk.total_km || 0).toFixed(1)} KM · ${(walk.access_type || 'open').replace('_', ' ')}`;
  document.getElementById('regTotal').textContent = regs.length;
  document.getElementById('regCheckedIn').textContent = regs.filter(r => !!r.check_in_time).length;
  document.getElementById('regFinished').textContent = regs.filter(r => r.attendance_status === 'finished' || !!r.finish_time).length;
  document.getElementById('regKm').textContent = Number(walk.total_km || 0).toFixed(1);
  tableBody.innerHTML = regs.map(r => `
    <tr>
      <td><div class="identity-cell">${avatarHtml(r)}<div><strong>${Pyrk.esc(r.full_name)}</strong><small>${Pyrk.esc(r.email || '')}</small></div></div></td>
      <td>${Pyrk.esc(r.mobile || '—')}</td>
      <td><strong>${Pyrk.esc(r.emergency_contact_name || '—')}</strong><small>${Pyrk.esc(r.emergency_contact_mobile || '')}</small></td>
      <td>${statusPill(r.registration_status)}<small>${formatDateTime(r.registered_at)}</small></td>
      <td>${Number(r.waiver_signed || 0) ? statusPill('signed') : '<span class="pill pill-muted">not signed</span>'}</td>
      <td>${r.attendance_status ? statusPill(r.attendance_status) : '<span class="pill pill-muted">not checked in</span>'}</td>
      <td>${formatDateTime(r.check_in_time)}</td>
      <td>${formatDateTime(r.finish_time)}</td>
      <td>${canUnregisterWalkMembers() ? `<button class="btn btn-danger btn-small" type="button" onclick="unregisterWalkMember(${walk.id}, ${r.user_id}, ${r.registration_id}, '${Pyrk.esc(r.full_name || 'member').replace(/'/g, '&#39;')}')">Unregister</button>` : '<span class="muted">—</span>'}</td>
    </tr>`).join('') || '<tr><td colspan="9" class="muted">No registrations yet.</td></tr>';
};


window.unregisterWalkMember = async function unregisterWalkMember(walkId, userId, registrationId, name = 'this member') {
  if (!canUnregisterWalkMembers()) {
    alert('You do not have permission to unregister members. Enable walks.unregister.');
    return;
  }

  const confirmed = confirm(`Unregister ${name} from this walk?\n\nThey will be removed from the active registrants list.`);
  if (!confirmed) return;

  const res = await Pyrk.post('walks.php', {
    action: 'unregister_member',
    walk_id: walkId,
    user_id: userId,
    registration_id: registrationId
  });

  alert(res.message || (res.ok ? 'Member unregistered.' : 'Could not unregister member.'));
  if (res.ok) {
    await openRegistrants(walkId);
    await loadWalks();
  }
};

window.openInvitations = async function openInvitations(walkId) {
  if (!canEditWalks()) {
    alert('You do not have permission to edit walk permissions. Enable walks.edit or walks.manage.');
    return;
  }
  openModal('walkInvitationsModal');
  document.getElementById('invitationsTitle').textContent = 'Manage invitations';
  document.getElementById('invitationsSubTitle').textContent = 'Loading invite lists...';
  document.getElementById('inviteGroupsList').innerHTML = '<p class="muted">Loading groups...</p>';
  document.getElementById('inviteMembersList').innerHTML = '<p class="muted">Loading members...</p>';
  const res = await Pyrk.get('walks.php', { action: 'invitations', id: walkId });
  if (!res.ok) {
    document.getElementById('inviteGroupsList').innerHTML = '';
    document.getElementById('inviteMembersList').innerHTML = `<div class="notice bad modal-error-box"><strong>Could not open walk permissions.</strong><br>${Pyrk.esc(res.message || 'Permission/API error.')}<br><small>Required: walks.edit or walks.manage.</small></div>`;
    return;
  }
  currentInviteData = res.data || { members: [], groups: [], invitations: { members: [], groups: [] } };
  const walk = currentInviteData.walk || {};
  inviteState.modalMembers.selected = new Set((currentInviteData.invitations?.members || []).map(x => Number(x.user_id)).filter(Boolean));
  inviteState.modalGroups.selected = new Set((currentInviteData.invitations?.groups || []).map(x => Number(x.group_id)).filter(Boolean));
  inviteState.modalMembers.open = false;
  inviteState.modalGroups.open = false;
  syncGroupMemberFilter('modal');
  document.getElementById('inviteMemberSearch').value = '';
  document.getElementById('inviteGroupSearch').value = '';
  document.getElementById('inviteWalkId').value = walk.id || walkId;
  document.getElementById('inviteAccessType').value = walk.access_type || 'open';
  const inviteSelfCheckin = document.getElementById('inviteAllowSelfCheckin');
  if (inviteSelfCheckin) inviteSelfCheckin.checked = Number(walk.allow_self_checkin || 0) === 1;
  const modalNotifyNewInvitees = document.getElementById('modalNotifyNewInvitees');
  if (modalNotifyNewInvitees) modalNotifyNewInvitees.checked = true;
  document.getElementById('inviteNote').value = walk.invite_note || '';
  document.getElementById('invitationsTitle').textContent = walk.title || 'Manage invitations';
  document.getElementById('invitationsSubTitle').textContent = `${walk.route_name || ''} · ${Pyrk.formatDate(walk.walk_date)} ${formatTime(walk.start_time)}`;
  renderInvitationChoices();
};


function parseWalkDateTime(dateValue, timeValue = '00:00') {
  if (!dateValue) return null;
  const t = String(timeValue || '00:00').slice(0, 5);
  const dt = new Date(`${dateValue}T${t}`);
  return Number.isNaN(dt.getTime()) ? null : dt;
}

function parseDeadline(value) {
  if (!value) return null;
  const normalized = String(value).trim().replace(' ', 'T');
  const dt = new Date(normalized);
  return Number.isNaN(dt.getTime()) ? null : dt;
}

function validateWalkScheduleData(data) {
  const walkDate = String(data.walk_date || '').trim();
  const startTime = String(data.start_time || '').trim();
  const finishTime = String(data.expected_finish_time || '').trim();
  if (!walkDate || !startTime || !finishTime) return 'Walk date, start time, and expected finish time are required.';

  const start = parseWalkDateTime(walkDate, startTime);
  const finish = parseWalkDateTime(walkDate, finishTime);
  if (!start || !finish) return 'Invalid walk date/time.';
  if (start.getTime() >= finish.getTime()) return 'Start time must be before expected finish time.';

  const registrationDeadline = parseDeadline(data.registration_deadline);
  if (registrationDeadline) {
    if (registrationDeadline.getTime() > start.getTime()) return 'Registration deadline cannot be after the walk starts.';
    const deadlineDate = String(data.registration_deadline).slice(0, 10);
    if (deadlineDate > walkDate) return 'Registration deadline date cannot be after the walk date.';
  }

  // Voting deadline is allowed after the walk date.
  return '';
}

document.getElementById('walkInvitationsForm')?.addEventListener('submit', async e => {
  e.preventDefault();
  const data = {
    action: 'save_invitations',
    walk_id: Number(document.getElementById('inviteWalkId').value),
    access_type: document.getElementById('inviteAccessType').value,
    invite_note: document.getElementById('inviteNote').value,
    allow_self_checkin: document.getElementById('inviteAllowSelfCheckin')?.checked ? 1 : 0,
    notify_new_invitees: document.getElementById('modalNotifyNewInvitees')?.checked ? 1 : 0,
    invite_member_ids: idsFromSet(inviteState.modalMembers.selected),
    invite_group_ids: idsFromSet(inviteState.modalGroups.selected)
  };
  const res = await Pyrk.post('walks.php', data);
  Pyrk.toast('inviteMsg', res);
  if (res.ok) { await loadWalks(); setTimeout(() => closeModal('walkInvitationsModal'), 500); }
});

document.getElementById('walkForm')?.addEventListener('submit', async e => {
  e.preventDefault();
  const fd = new FormData(e.target);
  const data = Object.fromEntries(fd);
  data.waiver_required = fd.get('waiver_required') ? 1 : 0;
  data.allow_self_checkin = fd.get('allow_self_checkin') ? 1 : 0;
  data.notify_new_invitees = fd.get('notify_new_invitees') ? 1 : 0;
  data.walk_notify_enabled = fd.get('walk_notify_enabled') ? 1 : 0;
  data.walk_notify_whatsapp = fd.get('walk_notify_whatsapp') ? 1 : 0;
  data.walk_notify_whatsapp_repeat = fd.get('walk_notify_whatsapp_repeat') ? 1 : 0;
  data.walk_notify_audience = String(data.walk_notify_audience || '');
  if (data.walk_notify_enabled && !['all', 'invited'].includes(data.walk_notify_audience)) {
    Pyrk.toast('walkMsg', {ok:false, message:'Choose who should receive the notification: all active members or invited members only.'});
    document.getElementById('walkNotifyAudience')?.focus();
    return;
  }
  if (!data.walk_notify_enabled) data.walk_notify_audience = 'all';
  data.walk_notify_subject = data.walk_notify_subject || '';
  data.walk_notify_message = data.walk_notify_message || '';
  data.payment_model = data.payment_model || 'free';
  data.payment_currency = data.payment_currency || 'LBP';
  if (!Number(data.route_id || 0)) {
    Pyrk.toast('walkMsg', {ok:false, message:'Please select a route from the searchable route dropdown.'});
    return;
  }
  const dateMessage = normalizeWalkDateInputsForSubmit(data);
  if (dateMessage) {
    Pyrk.toast('walkMsg', {ok:false, message: dateMessage});
    return;
  }
  data.payment_amount = data.payment_model === 'paid' ? Number(data.payment_amount || 0) : 0;
  if (data.payment_model === 'paid' && (!Number.isFinite(Number(data.payment_amount)) || Number(data.payment_amount) <= 0)) {
    Pyrk.toast('walkMsg', {ok:false, message:'Paid walks must have an amount greater than 0.'});
    return;
  }
  data.invite_member_ids = idsFromSet(inviteState.createMembers.selected);
  data.invite_group_ids = idsFromSet(inviteState.createGroups.selected);
  const validationMessage = validateWalkScheduleData(data);
  if (validationMessage) {
    Pyrk.toast('walkMsg', {ok:false, message: validationMessage});
    return;
  }
  const action = editingWalkId ? 'update' : 'create';
  if (editingWalkId) data.id = editingWalkId;
  const res = await Pyrk.post('walks.php', { action, ...data });
  Pyrk.toast('walkMsg', res);
  if (res.ok) {
    setWalkFormMode('create');
    await loadWalks();
  }
});

document.getElementById('walkSearch')?.addEventListener('input', renderWalks);
document.getElementById('walkPaymentModel')?.addEventListener('change', syncPaymentFields);
document.getElementById('walkPaymentAmount')?.addEventListener('input', syncPaymentFields);
document.getElementById('walkPaymentCurrency')?.addEventListener('change', syncPaymentFields);
document.getElementById('walkNotifyEnabled')?.addEventListener('change', event => {
  syncWalkNotificationCard({ autoSelect: Boolean(event.target.checked) });
});
document.getElementById('walkNotifyAudience')?.addEventListener('change', syncWalkNotificationCard);
document.getElementById('walkNotifyWhatsApp')?.addEventListener('change', event => {
  event.target.dataset.userChoice = '1';
  syncWalkWhatsAppCreateOption();
});
document.getElementById('walkNotifyWhatsAppRepeat')?.addEventListener('change', syncWalkWhatsAppCreateOption);
document.getElementById('walkAccessType')?.addEventListener('change', () => {
  const subject = document.getElementById('walkNotifySubject');
  const message = document.getElementById('walkNotifyMessage');
  if (subject) delete subject.dataset.touched;
  if (message) delete message.dataset.touched;
  const preset = document.getElementById('walkNotifyPreset');
  const presetIdInput = document.getElementById('walkNotifyPresetId');
  if (preset) { preset.value = ''; delete preset.dataset.userChoice; delete preset.dataset.presetApplied; }
  if (presetIdInput) presetIdInput.value = '';
  const whatsappPreset = document.getElementById('walkNotifyWhatsAppPreset');
  const whatsappPresetId = document.getElementById('walkNotifyWhatsAppPresetId');
  if (whatsappPreset) { whatsappPreset.value = ''; delete whatsappPreset.dataset.userChoice; }
  if (whatsappPresetId) whatsappPresetId.value = '';
  syncWalkNotificationCard();
});
document.getElementById('walkNotifyWhatsAppPreset')?.addEventListener('change', event => {
  event.target.dataset.userChoice = '1';
  const hidden = document.getElementById('walkNotifyWhatsAppPresetId');
  const preset = walkWhatsAppPresetById(event.target.value);
  if (hidden) hidden.value = preset ? String(preset.id) : '';
  const help = document.getElementById('walkNotifyWhatsAppPresetHelp');
  if (help) help.textContent = preset
    ? `WhatsApp will use “${preset.preset_name}”. Push content remains unchanged.`
    : 'Choose a WhatsApp preset before enabling WhatsApp delivery.';
});
document.getElementById('walkNotifyPreset')?.addEventListener('change', event => {
  event.target.dataset.userChoice = '1';
  delete event.target.dataset.presetApplied;
  const presetIdInput = document.getElementById('walkNotifyPresetId');
  if (presetIdInput) presetIdInput.value = '';
  const preset = walkNotificationPresetById(event.target.value);
  const help = document.getElementById('walkNotifyPresetHelp');
  if (help) help.textContent = preset
    ? `Selected “${preset.preset_name}”. Click Apply preset to replace the editor text.`
    : 'Choose a saved preset or keep editing the message manually.';
});
document.getElementById('walkNotifyApplyPreset')?.addEventListener('click', () => {
  const preset = walkNotificationPresetById(document.getElementById('walkNotifyPreset')?.value || '');
  if (!preset) { alert('Choose a Push preset first.'); return; }
  applyWalkNotificationPreset(preset, { force: true });
  const message = document.getElementById('walkNotifyMessage');
  message?.focus();
});
document.getElementById('walkNotifySubject')?.addEventListener('input', e => { e.target.dataset.touched = '1'; });
document.getElementById('walkNotifyMessage')?.addEventListener('input', e => { e.target.dataset.touched = '1'; });
document.querySelectorAll('[data-walk-notify-token]').forEach(button => {
  button.addEventListener('click', () => insertWalkNotificationToken(String(button.dataset.walkNotifyToken || '')));
});
document.getElementById('walkNotifyUseProTemplate')?.addEventListener('click', () => {
  const selected = walkNotificationPresetById(document.getElementById('walkNotifyPreset')?.value || '');
  const fallback = selected || walkNotificationDefaultPreset();
  if (fallback) applyWalkNotificationPreset(fallback, { force: true });
  else if (!walkPushPresetsLoaded || !walkPushPresetsInstalled) walkNotificationDefaultText(true);
  else { alert('No active preset exists for this use case. Open Manage presets and create one first.'); return; }
  const message = document.getElementById('walkNotifyMessage');
  if (message) {
    message.focus();
    message.setSelectionRange(message.value.length, message.value.length);
  }
});
['title','walkDateInput'].forEach(id => document.getElementById(id)?.addEventListener('input', walkNotificationDefaultText));
document.querySelector('#walkForm input[name="start_time"]')?.addEventListener('input', walkNotificationDefaultText);
syncWalkNotificationCard();
loadWalkPushPresets();
loadWalkWhatsAppPresets();

['createInviteGroupSearch', 'createInviteMemberSearch', 'inviteGroupSearch', 'inviteMemberSearch'].forEach(id => {
  const map = {
    createInviteGroupSearch: 'createGroups',
    createInviteMemberSearch: 'createMembers',
    inviteGroupSearch: 'modalGroups',
    inviteMemberSearch: 'modalMembers'
  };
  const el = document.getElementById(id);
  if (!el) return;
  el.addEventListener('focus', () => { inviteState[map[id]].open = true; renderInviteTypeahead(map[id]); });
  el.addEventListener('input', () => { inviteState[map[id]].open = true; renderInviteTypeahead(map[id]); });
});

document.addEventListener('click', e => {
  const insideInvite = e.target.closest('.pro-typeahead-multi') || ['createInviteGroupSearch','createInviteMemberSearch','inviteGroupSearch','inviteMemberSearch'].includes(e.target.id);
  if (!insideInvite) {
    Object.values(inviteState).forEach(s => { s.open = false; });
    renderCreateInvitationChoices();
    renderInvitationChoices();
  }
});


document.addEventListener('click', e => {
  const btn = e.target.closest('[data-invite-bulk]');
  if (!btn) return;
  e.preventDefault();
  const context = btn.dataset.inviteBulk === 'modal' ? 'modal' : 'create';
  const action = btn.dataset.bulkAction || '';
  if (action === 'all-members') {
    const total = (inviteDataForContext(context).members || []).length;
    if (!total) {
      alert('No active members are available to invite.');
      return;
    }
    if (total > 25) {
      const ok = confirm(`Invite all ${total} active members to this walk?\n\nOnly newly added members will receive an invitation email.`);
      if (!ok) return;
    }
    selectAllInviteMembers(context);
  }
  if (action === 'clear-members') {
    clearInviteMembers(context);
  }
});

document.querySelectorAll('[data-close-modal]').forEach(btn => btn.addEventListener('click', e => { e.preventDefault(); closeModal(btn.dataset.closeModal); }));
document.querySelectorAll('.modal-backdrop').forEach(modal => modal.addEventListener('click', e => { if (e.target === modal) closeModal(modal.id); }));
document.addEventListener('keydown', e => {
  if (e.key === 'Escape') {
    Object.values(inviteState).forEach(s => { s.open = false; });
    renderCreateInvitationChoices();
    renderInvitationChoices();
    document.querySelectorAll('.modal-backdrop:not([hidden])').forEach(m => closeModal(m.id));
  }
});


document.addEventListener('click', e => {
  if (e.target.closest('.pro-typeahead-input') || e.target.closest('.pro-typeahead-multi')) return;
  let changed = false;
  Object.values(inviteState).forEach(state => {
    if (state.open) { state.open = false; changed = true; }
  });
  if (changed) {
    renderCreateInvitationChoices();
    renderInvitationChoices();
  }
});



document.addEventListener('click', e => {
  const managerFieldClick = e.target.closest('[data-team-manager-search]');
  if (managerFieldClick) {
    if (!walkTeamInlineState.managerOpen) openTeamPersonDropdown('manager');
    return;
  }

  const headFieldClick = e.target.closest('[data-team-head-search]');
  if (headFieldClick) {
    if (!walkTeamInlineState.headOpen) openTeamPersonDropdown('head');
    return;
  }

  const routeOption = e.target.closest('[data-route-option]');
  if (routeOption) {
    e.preventDefault();
    const route = walkRoutes.find(r => Number(r.id) === Number(routeOption.dataset.routeOption));
    if (route) selectRoute(route);
    return;
  }

  const routeField = e.target.closest('.route-search-field');
  if (!routeField && document.getElementById('routeSearchResults')) {
    routePickerOpen = false;
    renderRoutePicker();
  }

  const teamSearchField = e.target.closest('.team-search-field');
  const teamOptionOutside = e.target.closest('[data-team-user-option]');
  if (!teamSearchField && !teamOptionOutside) {
    closeTeamPersonDropdowns();
  }

  const teamBtn = e.target.closest('[data-open-team]');
  if (teamBtn) {
    e.preventDefault();
    e.stopPropagation();
    openWalkTeamInline(Number(teamBtn.dataset.openTeam || 0), teamBtn);
    return;
  }

  if (e.target.closest('[data-close-team-panel]')) {
    e.preventDefault();
    closeInlineWalkTeam();
    return;
  }

  const saveBtn = e.target.closest('[data-save-team]');
  if (saveBtn) {
    e.preventDefault();
    saveInlineWalkTeam();
    return;
  }

  const teamOption = e.target.closest('[data-team-user-option]');
  if (teamOption) {
    e.preventDefault();
    const kind = teamOption.dataset.teamKind;
    const id = Number(teamOption.dataset.teamUserOption || 0);
    if (kind === 'manager') {
      walkTeamInlineState.managerId = id;
      walkTeamInlineState.managerQuery = '';
      walkTeamInlineState.managerOpen = false;
    } else if (kind === 'head') {
      walkTeamInlineState.headId = id;
      walkTeamInlineState.headQuery = '';
      walkTeamInlineState.headOpen = false;
    }
    walkTeamInlineState.selectedWalkers.delete(id);
    renderInlineTeamPanel();
    return;
  }

  if (e.target.closest('[data-team-select-all]')) {
    e.preventDefault();
    if (!walkTeamInlineState.canManage) return;
    const panel = e.target.closest('[data-inline-team-panel]');
    const managerId = Number(panel?.querySelector('[data-team-manager]')?.value || 0);
    const headId = Number(panel?.querySelector('[data-team-head]')?.value || 0);
    walkTeamInlineState.selectedWalkers = new Set((walkTeamInlineState.users || []).map(u => Number(u.id)).filter(id => id && id !== managerId && id !== headId));
    renderInlineTeamPanel();
    return;
  }

  if (e.target.closest('[data-team-clear]')) {
    e.preventDefault();
    if (!walkTeamInlineState.canManage) return;
    walkTeamInlineState.selectedWalkers.clear();
    renderInlineTeamPanel();
  }
});

document.addEventListener('change', e => {
  const walker = e.target.closest('[data-team-walker]');
  if (walker) {
    const id = Number(walker.value);
    if (walker.checked) walkTeamInlineState.selectedWalkers.add(id);
    else walkTeamInlineState.selectedWalkers.delete(id);
    renderInlineTeamPanel();
    return;
  }

  const teamOption = e.target.closest('[data-team-user-option]');
  if (teamOption) {
    const kind = teamOption.dataset.teamKind;
    const id = Number(teamOption.dataset.teamUserOption || 0);
    if (kind === 'manager') {
      walkTeamInlineState.managerId = id;
      walkTeamInlineState.managerQuery = '';
      walkTeamInlineState.managerOpen = false;
    } else if (kind === 'head') {
      walkTeamInlineState.headId = id;
      walkTeamInlineState.headQuery = '';
      walkTeamInlineState.headOpen = false;
    }
    walkTeamInlineState.selectedWalkers.delete(id);
    renderInlineTeamPanel();
  }
});


document.addEventListener('focusin', e => {
  if (e.target.closest('[data-team-manager-search]')) {
    if (!walkTeamInlineState.managerOpen) openTeamPersonDropdown('manager');
    return;
  }

  if (e.target.closest('[data-team-head-search]')) {
    if (!walkTeamInlineState.headOpen) openTeamPersonDropdown('head');
  }
});

document.addEventListener('input', e => {
  const routeSearch = e.target.closest('#routeSearchInput');
  if (routeSearch) {
    const selected = selectedRoute();
    if (!selected || routeSearch.value !== prettyRoute(selected)) {
      document.getElementById('routeSelect').value = '';
    }
    routePickerOpen = true;
    renderRoutePicker();
    return;
  }

  const managerSearch = e.target.closest('[data-team-manager-search]');
  if (managerSearch) {
    walkTeamInlineState.managerQuery = managerSearch.value;
    walkTeamInlineState.managerOpen = true;
    walkTeamInlineState.headOpen = false;
    updateTeamPersonDropdown('manager');
    updateTeamPersonDropdown('head');
    return;
  }

  const headSearch = e.target.closest('[data-team-head-search]');
  if (headSearch) {
    walkTeamInlineState.headQuery = headSearch.value;
    walkTeamInlineState.headOpen = true;
    walkTeamInlineState.managerOpen = false;
    updateTeamPersonDropdown('head');
    updateTeamPersonDropdown('manager');
    return;
  }

  const walkerSearch = e.target.closest('[data-team-search]');
  if (walkerSearch) {
    updateTeamWalkerSearch(walkerSearch);
    return;
  }
});



const walkDatePickerState = {
  targetId: '',
  year: new Date().getFullYear(),
  month: new Date().getMonth()
};

function dateFromDdFieldValue(value) {
  const raw = String(value || '').trim();
  const datePart = raw.split(/\s+/)[0] || '';
  const iso = ddMmYyyyToIso(datePart);
  if (!iso) return null;
  const m = iso.match(/^(\d{4})-(\d{2})-(\d{2})$/);
  if (!m) return null;
  return new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
}

function ensureWalkDatePicker() {
  let picker = document.getElementById('walkDatePickerPopup');
  if (picker) return picker;

  picker = document.createElement('div');
  picker.id = 'walkDatePickerPopup';
  picker.className = 'walk-date-picker-popup';
  picker.hidden = true;
  picker.innerHTML = `
    <div class="walk-date-picker-head">
      <button type="button" class="btn btn-light btn-small" data-walk-cal-prev>‹</button>
      <strong data-walk-cal-title></strong>
      <button type="button" class="btn btn-light btn-small" data-walk-cal-next>›</button>
    </div>
    <div class="walk-date-picker-week"><span>Mon</span><span>Tue</span><span>Wed</span><span>Thu</span><span>Fri</span><span>Sat</span><span>Sun</span></div>
    <div class="walk-date-picker-grid" data-walk-cal-grid></div>
    <div class="walk-date-picker-actions">
      <button type="button" class="btn btn-light btn-small" data-walk-cal-today>Today</button>
      <button type="button" class="btn btn-light btn-small" data-walk-cal-close>Close</button>
    </div>
  `;
  document.body.appendChild(picker);
  return picker;
}

function placeWalkDatePicker(input) {
  const picker = ensureWalkDatePicker();
  const rect = input.getBoundingClientRect();
  const width = Math.min(340, Math.max(290, rect.width));
  picker.style.width = `${width}px`;

  const top = rect.bottom + window.scrollY + 8;
  const left = Math.min(window.scrollX + rect.left, window.scrollX + document.documentElement.clientWidth - width - 12);
  picker.style.top = `${Math.max(12, top)}px`;
  picker.style.left = `${Math.max(12, left)}px`;
}

function renderWalkDatePicker() {
  const picker = ensureWalkDatePicker();
  const title = picker.querySelector('[data-walk-cal-title]');
  const grid = picker.querySelector('[data-walk-cal-grid]');
  if (!title || !grid) return;

  const monthNames = ['January','February','March','April','May','June','July','August','September','October','November','December'];
  title.textContent = `${monthNames[walkDatePickerState.month]} ${walkDatePickerState.year}`;

  const first = new Date(walkDatePickerState.year, walkDatePickerState.month, 1);
  const startOffset = (first.getDay() + 6) % 7; // Monday first
  const start = new Date(walkDatePickerState.year, walkDatePickerState.month, 1 - startOffset);
  const target = document.getElementById(walkDatePickerState.targetId);
  const selected = dateFromDdFieldValue(target?.value || '');
  const today = new Date();

  let html = '';
  for (let i = 0; i < 42; i++) {
    const d = new Date(start.getFullYear(), start.getMonth(), start.getDate() + i);
    const iso = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
    const muted = d.getMonth() !== walkDatePickerState.month;
    const isToday = d.toDateString() === today.toDateString();
    const isSelected = selected && d.toDateString() === selected.toDateString();
    html += `<button type="button" class="${muted ? 'is-muted' : ''} ${isToday ? 'is-today' : ''} ${isSelected ? 'is-selected' : ''}" data-walk-cal-date="${iso}">${d.getDate()}</button>`;
  }
  grid.innerHTML = html;
}

function openWalkDatePicker(inputId) {
  const input = document.getElementById(inputId);
  if (!input) return;

  walkDatePickerState.targetId = inputId;
  const current = dateFromDdFieldValue(input.value) || new Date();
  walkDatePickerState.year = current.getFullYear();
  walkDatePickerState.month = current.getMonth();

  const picker = ensureWalkDatePicker();
  renderWalkDatePicker();
  placeWalkDatePicker(input);
  picker.hidden = false;
}

function closeWalkDatePicker() {
  const picker = document.getElementById('walkDatePickerPopup');
  if (picker) picker.hidden = true;
}

function setWalkDatePickerValue(isoDate) {
  const input = document.getElementById(walkDatePickerState.targetId);
  if (!input) return;

  const pretty = isoToDdMmYyyy(isoDate);
  if (walkDatePickerState.targetId === 'walkDateInput') {
    input.value = pretty;
  } else {
    const current = String(input.value || '').trim();
    const timeMatch = current.match(/(\d{1,2}:\d{2})/);
    input.value = `${pretty} ${timeMatch ? timeMatch[1] : '00:00'}`;
  }

  closeWalkDatePicker();
  input.dispatchEvent(new Event('change', { bubbles: true }));
}


document.getElementById('routeSearchInput')?.addEventListener('focus', () => {
  routePickerOpen = true;
  renderRoutePicker();
});

['walkDateInput','registrationDeadlineInput','votingDeadlineInput'].forEach(id => {
  const el = document.getElementById(id);
  if (!el) return;
  el.addEventListener('blur', () => {
    if (id === 'walkDateInput') {
      const iso = ddMmYyyyToIso(el.value);
      if (iso) el.value = isoToDdMmYyyy(iso);
    } else {
      const iso = ddMmYyyyHmToIsoDateTime(el.value);
      if (iso) el.value = isoDateTimeToDdMmYyyyHm(iso);
    }
  });
});


// Final dd/mm/yyyy calendar binding for Admin > Walks.
// Visible fields stay dd/mm/yyyy; clicking/focusing opens the custom calendar.
const pyrkWalkDateFieldIds = ['walkDateInput','registrationDeadlineInput','votingDeadlineInput'];

document.addEventListener('focusin', e => {
  const field = e.target.closest('#walkDateInput, #registrationDeadlineInput, #votingDeadlineInput');
  if (field) {
    openWalkDatePicker(field.id);
  }
});

document.addEventListener('click', e => {
  const field = e.target.closest('#walkDateInput, #registrationDeadlineInput, #votingDeadlineInput');
  if (field) {
    openWalkDatePicker(field.id);
    return;
  }

  const dateBtn = e.target.closest('[data-walk-cal-date]');
  if (dateBtn) {
    e.preventDefault();
    setWalkDatePickerValue(dateBtn.dataset.walkCalDate);
    return;
  }

  if (e.target.closest('[data-walk-cal-prev]')) {
    e.preventDefault();
    walkDatePickerState.month -= 1;
    if (walkDatePickerState.month < 0) {
      walkDatePickerState.month = 11;
      walkDatePickerState.year -= 1;
    }
    renderWalkDatePicker();
    return;
  }

  if (e.target.closest('[data-walk-cal-next]')) {
    e.preventDefault();
    walkDatePickerState.month += 1;
    if (walkDatePickerState.month > 11) {
      walkDatePickerState.month = 0;
      walkDatePickerState.year += 1;
    }
    renderWalkDatePicker();
    return;
  }

  if (e.target.closest('[data-walk-cal-today]')) {
    e.preventDefault();
    const today = new Date();
    const iso = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`;
    setWalkDatePickerValue(iso);
    return;
  }

  if (e.target.closest('[data-walk-cal-close]')) {
    e.preventDefault();
    closeWalkDatePicker();
    return;
  }

  const picker = document.getElementById('walkDatePickerPopup');
  if (picker && !picker.hidden && !e.target.closest('#walkDatePickerPopup')) {
    closeWalkDatePicker();
  }
});

window.addEventListener('resize', () => {
  const picker = document.getElementById('walkDatePickerPopup');
  const target = document.getElementById(walkDatePickerState.targetId);
  if (picker && target && !picker.hidden) placeWalkDatePicker(target);
});


document.querySelectorAll('[data-walk-view]').forEach(btn => {
  btn.addEventListener('click', async () => {
    const nextView = ['upcoming','open','closed','archived'].includes(btn.dataset.walkView) ? btn.dataset.walkView : 'upcoming';
    if (walkArchiveView === nextView) return;
    walkArchiveView = nextView;
    const search = document.getElementById('walkSearch');
    if (search) search.value = '';
    await loadWalks();
  });
});

loadRoutesForSelect();
loadCreateInviteData();
loadWalks();

