// ───── Dochádzka — mobile-first screen pre pípanie príchod/odchod ─────
// Veľké tlačidlá, GPS watcher, geofence detekcia, countdown do konca zmeny.
// Ping každú minútu počas zmeny → server udržuje breadcrumb trail.

const ATT_BREAK_KINDS = [
  { id: 'smoke',    label: 'Fajčiarska', icon: '🚬', color: '#94a3b8' },
  { id: 'lunch',    label: 'Obed',       icon: '🍽',  color: '#f59e0b' },
  { id: 'personal', label: 'Osobné',     icon: '📋', color: '#7b68ee' },
  { id: 'meeting',  label: 'Porada',     icon: '👥', color: '#3a6da0' },
  { id: 'other',    label: 'Iné',        icon: '•',  color: '#64748b' },
];
function attBreakMeta(id) { return ATT_BREAK_KINDS.find(b => b.id === id) || ATT_BREAK_KINDS[ATT_BREAK_KINDS.length-1]; }

function attFmtTime(ms) {
  if (ms == null) return '—';
  const d = new Date(ms);
  return `${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}`;
}
function attFmtDuration(ms) {
  if (ms == null || ms < 0) ms = 0;
  const h = Math.floor(ms / 3600000);
  const m = Math.floor((ms % 3600000) / 60000);
  const s = Math.floor((ms % 60000) / 1000);
  if (h > 0) return `${h}:${String(m).padStart(2,'0')}:${String(s).padStart(2,'0')}`;
  return `${m}:${String(s).padStart(2,'0')}`;
}
function attUuid() {
  // Fallback ak crypto.randomUUID nie je dostupné (staršie prehliadače).
  if (window.crypto && crypto.randomUUID) return crypto.randomUUID();
  return 'u' + Date.now().toString(36) + Math.random().toString(36).slice(2, 10);
}
function attHaversine(lat1, lng1, lat2, lng2) {
  const R = 6371000;
  const toRad = d => d * Math.PI / 180;
  const dLat = toRad(lat2 - lat1);
  const dLng = toRad(lng2 - lng1);
  const a = Math.sin(dLat/2)**2 + Math.cos(toRad(lat1))*Math.cos(toRad(lat2))*Math.sin(dLng/2)**2;
  return 2 * R * Math.asin(Math.sqrt(a));
}

// ───── hlavný screen ─────────────────────────────────────────────────
function AttendanceScreen() {
  const user = (window.currentSession && window.currentSession.user) || {};
  const canEditZones = window.hasPerm ? window.hasPerm('attendance.edit') : false;
  const canViewTeam  = window.hasPerm ? window.hasPerm('attendance.view_team') : false;

  const [state, setState] = React.useState(null);   // server-side state {status, session, metrics, ...}
  const [zones, setZones] = React.useState([]);
  const [todayPunches, setTodayPunches] = React.useState([]);
  // Inicializuj GPS z cached hodnoty v sessionStorage (max 5 min stará) — eliminuje
  // viacnásobné prompty pri reload-och a navigáciách na/od Dochádzky.
  const [gps, setGps] = React.useState(() => {
    try {
      const raw = sessionStorage.getItem('dd_gps_cache');
      if (raw) {
        const c = JSON.parse(raw);
        if (c && Date.now() - c.ts < 5 * 60 * 1000) return { ...c, error: null };
      }
    } catch {}
    return { lat: null, lng: null, accuracy: null, ts: 0, error: null };
  });
  const [tick, setTick] = React.useState(0);        // pre prekreslenie countdownu každú sekundu
  const [busy, setBusy] = React.useState(false);
  const [toast, setToast] = React.useState(null);
  const [breakMenu, setBreakMenu] = React.useState(false);
  const [tripModal, setTripModal] = React.useState(false);   // pri trip_start otvorí dialog
  const [leaveZoneAlert, setLeaveZoneAlert] = React.useState(null); // {zone_name}
  const [askedZoneIds, setAskedZoneIds] = React.useState(new Set()); // už spýtané v tejto session
  const [zonesAdmin, setZonesAdmin] = React.useState(false);  // open zone admin modal
  const [historyOpen, setHistoryOpen] = React.useState(false);
  const [statsOpen, setStatsOpen] = React.useState(false);
  const [planTripOpen, setPlanTripOpen] = React.useState(false);
  const [approveTripsOpen, setApproveTripsOpen] = React.useState(false);
  const [managerAssignOpen, setManagerAssignOpen] = React.useState(false);
  const canApproveTrips = window.hasPerm ? window.hasPerm('business_trip.approve') : false;
  const wakeLockRef = React.useRef(null);
  const watchIdRef = React.useRef(null);
  const lastInZoneRef = React.useRef(null); // posledná známa zóna v ktorej user bol (id alebo null)
  const pingTimerRef = React.useRef(null);

  // ─── auto-open ApproveTripsModal cez hash query ?approve=trips ───
  // Reaguje na mount + na hash zmeny (klik na notifikáciu kým je user už v Dochádzke)
  React.useEffect(() => {
    function checkHash() {
      const hash = window.location.hash || '';
      if (canApproveTrips && /approve=trips/.test(hash)) {
        setApproveTripsOpen(true);
      }
    }
    checkHash();
    window.addEventListener('hashchange', checkHash);
    return () => window.removeEventListener('hashchange', checkHash);
  }, [canApproveTrips]);

  // ─── load state + zones ───
  const reload = React.useCallback(async () => {
    const [s, z, t] = await Promise.all([
      fetch('/api/attendance/state', { credentials: 'same-origin' }).then(r => r.json()),
      fetch('/api/attendance/zones', { credentials: 'same-origin' }).then(r => r.json()),
      fetch('/api/attendance/today', { credentials: 'same-origin' }).then(r => r.json()),
    ]);
    if (s?.ok) setState(s);
    if (z?.ok) setZones(z.zones || []);
    if (t?.ok) setTodayPunches(t.punches || []);
  }, []);

  React.useEffect(() => { reload(); }, [reload]);

  // ─── tick — prekreslenie countdownu každú sekundu kým je clocked_in ───
  React.useEffect(() => {
    if (!state || state.status === 'off') return;
    const id = setInterval(() => setTick(t => t + 1), 1000);
    return () => clearInterval(id);
  }, [state?.status]);

  // ─── live GPS watcher + geofence detection ───
  // Nepýtame oprávnenie automaticky pri otvorení stránky — počkáme kým user klikne
  // PRÍCHOD (user gesture). Browseri tak dokážu zapamätať permission lepšie a nevyzývajú
  // pri každom refreshi. Ak je permission už granted, načítame polohu hneď.
  React.useEffect(() => {
    if (!navigator.geolocation) { setGps(g => ({ ...g, error: 'GPS nie je dostupné' })); return; }

    let cancelled = false;
    async function startTracking(forceRequest = false) {
      // Skontroluj permission status (ak browser podporuje Permissions API)
      let permState = 'prompt';
      try {
        if (navigator.permissions?.query) {
          const ps = await navigator.permissions.query({ name: 'geolocation' });
          permState = ps.state;
        }
      } catch {}

      // Ak nie sme v zmene a permission je 'prompt' (užívateľ ešte nesúhlasil),
      // nepýtame proaktívne — počkáme na user gesture (klik PRÍCHOD).
      // Toto eliminuje "permission prompt na každý refresh" problém.
      if (cancelled) return;
      if (permState === 'denied') {
        setGps(g => ({ ...g, error: 'Poloha bola odmietnutá. Povol ju v nastaveniach prehliadača.' }));
        return;
      }
      if (permState === 'prompt' && !forceRequest && (!state || state.status === 'off')) {
        // Tichý mód — nepýtame, ukážeme len odznak že GPS čaká
        setGps(g => ({ ...g, error: null }));
        return;
      }

      if (state && state.status !== 'off') {
        // počas zmeny: high accuracy watch
        watchIdRef.current = navigator.geolocation.watchPosition(
          (pos) => {
            const next = {
              lat: pos.coords.latitude, lng: pos.coords.longitude, accuracy: pos.coords.accuracy,
              ts: Date.now(), error: null,
            };
            setGps(next);
            try { sessionStorage.setItem('dd_gps_cache', JSON.stringify(next)); } catch {}
            checkGeofence(next);
          },
          (err) => setGps(g => ({ ...g, error: err.message })),
          { enableHighAccuracy: true, maximumAge: 5000, timeout: 15000 },
        );
      } else {
        // mimo zmeny: jednorázové získanie polohy (len ak granted alebo gesture)
        navigator.geolocation.getCurrentPosition(
          (pos) => {
            const next = { lat: pos.coords.latitude, lng: pos.coords.longitude, accuracy: pos.coords.accuracy, ts: Date.now(), error: null };
            setGps(next);
            try { sessionStorage.setItem('dd_gps_cache', JSON.stringify(next)); } catch {}
          },
          (err) => setGps(g => ({ ...g, error: err.message })),
          { enableHighAccuracy: true, maximumAge: 30000, timeout: 10000 },
        );
      }
    }
    startTracking();
    return () => {
      cancelled = true;
      if (watchIdRef.current != null) {
        navigator.geolocation.clearWatch(watchIdRef.current);
        watchIdRef.current = null;
      }
    };
  }, [state?.status, zones.length]);

  // Helper: vynútiť získanie polohy pri user gesture (klik PRÍCHOD aktivuje prompt)
  async function ensureGpsBeforePunch(type) {
    if (gps.lat != null && Date.now() - gps.ts < 60_000) return;  // už máme čerstvú polohu
    if (type !== 'clock_in') return;  // pre prestávky / odchod použijeme posledné GPS
    return new Promise(resolve => {
      navigator.geolocation.getCurrentPosition(
        (pos) => {
          const next = { lat: pos.coords.latitude, lng: pos.coords.longitude, accuracy: pos.coords.accuracy, ts: Date.now(), error: null };
          setGps(next);
          try { sessionStorage.setItem('dd_gps_cache', JSON.stringify(next)); } catch {}
          resolve();
        },
        () => resolve(),  // ignoruj chybu — punch ide aj bez polohy
        { enableHighAccuracy: true, timeout: 10000 },
      );
    });
  }

  // ─── wake lock počas zmeny ───
  React.useEffect(() => {
    async function acquireWakeLock() {
      if (!('wakeLock' in navigator)) return;
      try {
        wakeLockRef.current = await navigator.wakeLock.request('screen');
        wakeLockRef.current.addEventListener('release', () => { wakeLockRef.current = null; });
      } catch (e) { /* user gesture nutný — vyrieši sa pri ďalšom kliku */ }
    }
    function releaseWakeLock() {
      if (wakeLockRef.current) { try { wakeLockRef.current.release(); } catch {} wakeLockRef.current = null; }
    }
    if (state && state.status !== 'off') acquireWakeLock();
    else releaseWakeLock();
    return releaseWakeLock;
  }, [state?.status]);

  // ─── ping každú minútu počas clocked_in ───
  React.useEffect(() => {
    if (!state || state.status === 'off') return;
    function sendPing() {
      if (gps.lat == null) return;
      fetch('/api/attendance/ping', {
        method: 'POST', credentials: 'same-origin',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ lat: gps.lat, lng: gps.lng, accuracy: gps.accuracy }),
      }).catch(() => {});
    }
    sendPing();
    pingTimerRef.current = setInterval(sendPing, 60000);
    return () => { if (pingTimerRef.current) clearInterval(pingTimerRef.current); };
  }, [state?.status, gps.lat]);

  // ─── browser notif permission request (pri prvom načítaní) ───
  React.useEffect(() => {
    if ('Notification' in window && Notification.permission === 'default') {
      // Nepýtaj sa proaktívne — chceme aby to vyvolal user gesture (klik), inak browseri ignorujú.
    }
  }, []);

  // ─── geofence check — pri každom GPS update ───
  function checkGeofence(coords) {
    if (!coords || coords.lat == null) return;
    if (!state || state.status === 'off' || state.status === 'on_trip') {
      lastInZoneRef.current = null;
      return;
    }
    if (!zones.length) return;
    let inside = null;
    for (const z of zones) {
      const d = attHaversine(coords.lat, coords.lng, z.lat, z.lng);
      if (d <= z.radius_m) { inside = z; break; }
    }
    const prev = lastInZoneRef.current;
    if (prev && !inside) {
      // user opustil zónu
      if (!askedZoneIds.has(prev.id)) {
        setLeaveZoneAlert({ zone: prev });
        setAskedZoneIds(s => { const n = new Set(s); n.add(prev.id); return n; });
        try { navigator.vibrate && navigator.vibrate([200, 100, 200]); } catch {}
        try {
          if ('Notification' in window && Notification.permission === 'granted') {
            new Notification(`Opustil si ${prev.name}`, {
              body: 'Pípni si Odchod alebo Služobnú cestu.',
              icon: '/uploads/branding/icon-192.png',
              tag: 'leave-zone',
              requireInteraction: true,
            });
          }
        } catch {}
      }
    }
    if (inside && (!prev || prev.id !== inside.id)) {
      // user vstúpil do (novej) zóny → reset "už spýtané"
      setAskedZoneIds(s => { const n = new Set(s); n.delete(inside.id); return n; });
    }
    lastInZoneRef.current = inside;
  }

  // ─── punch action ───
  async function doPunch(type, extra = {}) {
    if (busy) return;
    setBusy(true);
    // Pri PRÍCHODE sa pokúsime získať polohu (user gesture aktivuje prompt — browser si to zapamätá)
    await ensureGpsBeforePunch(type);
    try {
      const r = await fetch('/api/attendance/punch', {
        method: 'POST', credentials: 'same-origin',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          type,
          lat: gps.lat, lng: gps.lng, accuracy: gps.accuracy,
          client_ts: Date.now(),
          offline_uuid: attUuid(),
          ...extra,
        }),
      });
      const j = await r.json();
      if (j.ok) {
        try { navigator.vibrate && navigator.vibrate(50); } catch {}
        // Špeciálny toast pre trip_start — pripomenie schvaľovanie
        const isTripStart = type === 'trip_start';
        setToast({
          kind: 'success',
          text: isTripStart
            ? '🚗 Služobná cesta začatá. Žiadosť o schválenie odoslaná nadriadenému.'
            : punchLabel(type, extra.break_kind) + ' zaznamenané.',
        });
        if (type === 'clock_in' && 'Notification' in window && Notification.permission === 'default') {
          try { await Notification.requestPermission(); } catch {}
        }
        // Vyvolaj okamžitý fetch notifikácií — manažér/admin uvidí požiadavku do sekundy
        try { window.dispatchEvent(new CustomEvent('dd-notifications-refresh')); } catch {}
        await reload();
        return j;
      } else {
        setToast({ kind: 'error', text: j.error || 'Chyba pri pípnutí.' });
      }
    } catch (e) {
      setToast({ kind: 'error', text: 'Sieťová chyba.' });
    } finally {
      setBusy(false);
    }
  }

  async function undoPunch(punch) {
    if (!confirm(`Vrátiť posledný záznam (${punchLabel(punch.punch_type, punch.break_kind)})?`)) return;
    setBusy(true);
    const r = await fetch(`/api/attendance/punch/${punch.id}`, {
      method: 'DELETE', credentials: 'same-origin',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({}),
    });
    const j = await r.json();
    setBusy(false);
    if (j.ok) { setToast({ kind: 'success', text: 'Záznam vrátený.' }); await reload(); }
    else setToast({ kind: 'error', text: j.error === 'undo_window_expired' ? 'Okno na vrátenie (5 min) vypršalo. Požiadaj nadriadeného o opravu.' : (j.error || 'Chyba.') });
  }

  function punchLabel(type, breakKind) {
    if (type === 'clock_in')    return 'Príchod';
    if (type === 'clock_out')   return 'Odchod';
    if (type === 'break_start') return attBreakMeta(breakKind).label + ' — štart';
    if (type === 'break_end')   return 'Návrat z prestávky';
    if (type === 'trip_start')  return 'Služobná cesta';
    if (type === 'trip_end')    return 'Koniec služobnej cesty';
    return type;
  }

  // ─── computed display ───
  const status = state?.status || 'off';
  const session = state?.session;
  const metrics = state?.metrics;
  const inZone = lastInZoneRef.current;

  // Server vrátil metrics.worked_ms ako snapshot v čase reloadu; tu prirátame
  // čas od reloadu — countdown sa prekresľuje každú sekundu cez tick state.
  let liveWorkedMs = 0, liveRemainingMs = 0;
  if (metrics && session) {
    const elapsedNow = Date.now() - session.clock_in_at;
    liveWorkedMs = metrics.worked_ms + (status === 'working' ? Math.max(0, elapsedNow - metrics.elapsed_ms) : 0);
    liveRemainingMs = Math.max(0, metrics.target_ms - liveWorkedMs);
  }

  const gpsQuality = gps.error ? 'error' : gps.accuracy == null ? 'unknown' : gps.accuracy < 30 ? 'good' : gps.accuracy < 100 ? 'fair' : 'poor';
  const gpsColor = { good: '#10b981', fair: '#f59e0b', poor: '#ef4444', error: '#94a3b8', unknown: '#94a3b8' }[gpsQuality];

  // ─── render ───
  return (
    <AppShell active="attendance" crumbs={['ERP', 'Dochádzka']}>
      {toast && <Toast kind={toast.kind} onClose={() => setToast(null)}>{toast.text}</Toast>}

      {leaveZoneAlert && (
        <LeaveZoneModal
          zone={leaveZoneAlert.zone}
          onClose={() => setLeaveZoneAlert(null)}
          onTrip={() => { setLeaveZoneAlert(null); setTripModal(true); }}
          onClockOut={() => { setLeaveZoneAlert(null); doPunch('clock_out'); }}
        />
      )}

      {tripModal && (
        <TripStartModal
          onClose={() => setTripModal(false)}
          onConfirm={async (data) => {
            const r = await doPunch('trip_start', data);
            if (r?.ok) setTripModal(false);
          }}
        />
      )}

      {breakMenu && (
        <BreakMenuModal
          onClose={() => setBreakMenu(false)}
          onPick={(kind) => { setBreakMenu(false); doPunch('break_start', { break_kind: kind }); }}
        />
      )}

      {zonesAdmin && (
        <ZonesAdminModal
          currentGps={gps}
          onClose={() => setZonesAdmin(false)}
          onChanged={async () => { await reload(); setToast({ kind: 'success', text: 'Uložené.' }); }}
        />
      )}

      {historyOpen && (
        <HistoryModal
          targetUserId={user.id}
          targetUserName={user.name || 'Ja'}
          onClose={() => setHistoryOpen(false)}
        />
      )}

      {statsOpen && (
        <TeamStatsModal
          onClose={() => setStatsOpen(false)}
        />
      )}

      {planTripOpen && (
        <PlanTripModal
          onClose={() => setPlanTripOpen(false)}
          onSubmitted={() => { setPlanTripOpen(false); setToast({ kind: 'success', text: 'Žiadosť odoslaná nadriadenému.' }); reload(); }}
        />
      )}

      {approveTripsOpen && (
        <ApproveTripsModal
          onClose={() => { setApproveTripsOpen(false); /* odstráni ?approve=trips z URL */ if (/approve=trips/.test(window.location.hash)) window.location.hash = '#/attendance'; }}
          onChanged={() => { setToast({ kind: 'success', text: 'Aktualizované.' }); }}
        />
      )}

      {managerAssignOpen && (
        <ManagerAssignModal
          onClose={() => setManagerAssignOpen(false)}
          onChanged={() => setToast({ kind: 'success', text: 'Priradenia uložené.' })}
        />
      )}

      <div className="att-screen">
        {/* status banner */}
        <div className={`att-banner att-status-${status}`}>
          <div className="att-banner-main">
            <div className="att-banner-icon">{statusIcon(status)}</div>
            <div className="att-banner-text">
              <div className="att-banner-title">{statusTitle(status, state?.activeBreakKind)}</div>
              <div className="att-banner-sub">
                {status !== 'off' && session && <>od {attFmtTime(session.clock_in_at)}{session.clock_in_location ? ` · ${session.clock_in_location}` : ''}</>}
                {status === 'off' && 'Ešte si dnes nepípol príchod'}
              </div>
            </div>
          </div>
          <div className="att-gps" title={`GPS: ${gpsQuality}${gps.accuracy ? ' · ±' + Math.round(gps.accuracy) + 'm' : ''}${gps.error ? ' · ' + gps.error : ''}`}>
            <span className="att-gps-dot" style={{background: gpsColor}}/>
            <span className="att-gps-label">{inZone ? `📍 ${inZone.name}` : (gps.lat != null ? '📍 mimo zón' : '📍 hľadám…')}</span>
          </div>
        </div>

        {/* countdown — len keď je v práci */}
        {status !== 'off' && metrics && (
          <div className="att-countdown">
            <div className="att-countdown-row">
              <div className="att-countdown-block">
                <div className="att-countdown-label">Odpracované</div>
                <div className="att-countdown-value">{attFmtDuration(liveWorkedMs)}</div>
              </div>
              <div className="att-countdown-block primary">
                <div className="att-countdown-label">Do konca zmeny</div>
                <div className="att-countdown-value">{liveRemainingMs > 0 ? attFmtDuration(liveRemainingMs) : '✅ Hotovo'}</div>
              </div>
              <div className="att-countdown-block">
                <div className="att-countdown-label">Prestávky</div>
                <div className="att-countdown-value">{attFmtDuration(metrics.break_ms)}</div>
              </div>
            </div>
            <div className="att-progress">
              <div className="att-progress-bar" style={{width: `${Math.min(100, (liveWorkedMs / metrics.target_ms) * 100)}%`}}/>
            </div>
            <div className="att-countdown-meta">
              {metrics.smoke_count > 0 && <span>🚬 {metrics.smoke_count}× ({attFmtDuration(metrics.smoke_ms)})</span>}
              {metrics.lunch_ms > 0 && <span>🍽 {attFmtDuration(metrics.lunch_ms)}</span>}
              {liveRemainingMs <= 0 && <span style={{color:'#10b981', fontWeight:600}}>Môžeš pípnuť odchod</span>}
            </div>
          </div>
        )}

        {/* primary actions */}
        <div className="att-actions">
          {status === 'off' && (
            <button className="att-btn att-btn-primary att-btn-go" disabled={busy} onClick={() => doPunch('clock_in')}>
              <span className="att-btn-ico">▶</span>
              <span className="att-btn-text">PRÍCHOD</span>
              <span className="att-btn-sub">{attFmtTime(Date.now())}</span>
            </button>
          )}
          {status === 'working' && (
            <>
              <button className="att-btn att-btn-danger" disabled={busy} onClick={() => doPunch('clock_out')}>
                <span className="att-btn-ico">⏹</span>
                <span className="att-btn-text">ODCHOD</span>
              </button>
              <div className="att-actions-row">
                <button className="att-btn att-btn-secondary" disabled={busy} onClick={() => setBreakMenu(true)}>
                  <span className="att-btn-ico">⏸</span>
                  <span className="att-btn-text">Prestávka</span>
                </button>
                <button className="att-btn att-btn-secondary" disabled={busy} onClick={() => setTripModal(true)}>
                  <span className="att-btn-ico">🚗</span>
                  <span className="att-btn-text">Služobná cesta</span>
                </button>
              </div>
            </>
          )}
          {status === 'on_break' && (
            <button className="att-btn att-btn-primary" disabled={busy} onClick={() => doPunch('break_end')}>
              <span className="att-btn-ico">▶</span>
              <span className="att-btn-text">Návrat z prestávky</span>
              <span className="att-btn-sub">{attBreakMeta(state.activeBreakKind).label}</span>
            </button>
          )}
          {status === 'on_trip' && (
            <button className="att-btn att-btn-primary" disabled={busy} onClick={() => doPunch('trip_end')}>
              <span className="att-btn-ico">🏁</span>
              <span className="att-btn-text">Koniec služobnej cesty</span>
            </button>
          )}
        </div>

        {/* today log */}
        <div className="att-card">
          <div className="att-card-head">
            <h3>Dnes</h3>
            <button className="btn btn-xs" onClick={reload}>↻</button>
          </div>
          {todayPunches.length === 0 ? (
            <div className="att-empty">Žiadne dnešné záznamy.</div>
          ) : (
            <div className="att-log">
              {todayPunches.map((p, idx) => (
                <PunchRow
                  key={p.id}
                  punch={p}
                  canUndo={idx === todayPunches.length - 1 /* iba posledný */}
                  onUndo={undoPunch}
                />
              ))}
            </div>
          )}
        </div>

        {/* secondary actions */}
        <div className="att-secondary-actions">
          <button className="btn" onClick={() => setPlanTripOpen(true)}>📅 Naplánovať SC</button>
          {canApproveTrips && <button className="btn att-btn-warn" onClick={() => setApproveTripsOpen(true)}>📥 Schvaľovanie SC</button>}
          <button className="btn" onClick={() => setHistoryOpen(true)}>📅 Moja história</button>
          {canViewTeam && <button className="btn" onClick={() => setStatsOpen(true)}>📊 Štatistiky tímu</button>}
          {canEditZones && <button className="btn" onClick={() => setZonesAdmin(true)}>⚙ Pracoviská ({zones.length})</button>}
          {canEditZones && <button className="btn" onClick={() => setManagerAssignOpen(true)}>👥 Manažéri</button>}
        </div>

        {/* tips */}
        <div className="att-tips">
          <p>📱 Pre najlepšiu spoľahlivosť pridaj túto stránku na plochu (Add to Home Screen) a počas zmeny ju nezatváraj.</p>
          {gps.error && <p style={{color:'#ef4444'}}>⚠️ {gps.error} — povoľ polohu v nastaveniach prehliadača.</p>}
        </div>
      </div>
    </AppShell>
  );
}

// ───── pomocné komponenty ────────────────────────────────────────────

function statusIcon(s) {
  return ({ off: '⏰', working: '🟢', on_break: '⏸', on_trip: '🚗' }[s] || '⏰');
}
function statusTitle(s, breakKind) {
  if (s === 'off')     return 'Mimo zmeny';
  if (s === 'working') return 'V práci';
  if (s === 'on_break') return `Prestávka — ${attBreakMeta(breakKind).label}`;
  if (s === 'on_trip')  return 'Služobná cesta';
  return s;
}

// 3 minúty okno na vlastné vrátenie posledného punchu.
const ATT_UNDO_WINDOW_MS = 3 * 60 * 1000;

function PunchRow({ punch, canUndo, onUndo }) {
  const time = attFmtTime(punch.punched_at);
  let icon = '•', label = punch.punch_type, color = '#64748b';
  if (punch.punch_type === 'clock_in')    { icon = '▶'; label = 'Príchod';                  color = '#10b981'; }
  if (punch.punch_type === 'clock_out')   { icon = '⏹'; label = 'Odchod';                   color = '#ef4444'; }
  if (punch.punch_type === 'break_start') { const b = attBreakMeta(punch.break_kind); icon = b.icon; label = b.label + ' — štart'; color = b.color; }
  if (punch.punch_type === 'break_end')   { icon = '↺'; label = 'Návrat z prestávky';        color = '#3a6da0'; }
  if (punch.punch_type === 'trip_start')  { icon = '🚗'; label = 'Služobná cesta — štart';   color = '#8a5a2b'; }
  if (punch.punch_type === 'trip_end')    { icon = '🏁'; label = 'Služobná cesta — koniec';  color = '#8a5a2b'; }

  // Real-time odpočet — tick každú sekundu kým je viditeľný undo button.
  const [now, setNow] = React.useState(Date.now());
  const undoLeftMs = canUndo ? Math.max(0, punch.punched_at + ATT_UNDO_WINDOW_MS - now) : 0;
  const showUndo = canUndo && undoLeftMs > 0;
  React.useEffect(() => {
    if (!showUndo) return;
    const id = setInterval(() => setNow(Date.now()), 1000);
    return () => clearInterval(id);
  }, [showUndo]);

  const secs = Math.ceil(undoLeftMs / 1000);
  const mins = Math.floor(secs / 60);
  const rem  = secs % 60;
  const timerText = mins > 0 ? `${mins}:${String(rem).padStart(2,'0')}` : `${rem}s`;
  // Progress (0–1) — pre vizuálnu pulzáciu keď sa blíži koniec
  const progress = undoLeftMs / ATT_UNDO_WINDOW_MS;
  const urgent = progress < 0.25;

  return (
    <div className={`att-log-row${showUndo ? ' has-undo' : ''}`}>
      <div className="att-log-time">{time}</div>
      <div className="att-log-icon" style={{background: color}}>{icon}</div>
      <div className="att-log-body">
        <div className="att-log-label">{label}</div>
        <div className="att-log-meta">
          {punch.location_label || (punch.lat ? `${punch.lat.toFixed(5)}, ${punch.lng.toFixed(5)}` : 'bez polohy')}
          {punch.accuracy && ` · ±${Math.round(punch.accuracy)}m`}
          {punch.is_manual && ' · ✏️ ručná oprava'}
        </div>
      </div>
      {showUndo && (
        <button
          className={`att-undo${urgent ? ' urgent' : ''}`}
          onClick={() => onUndo(punch)}
          title="Vrátiť posledný záznam"
        >
          <span className="att-undo-fill" style={{ width: (progress * 100).toFixed(1) + '%' }}/>
          <span className="att-undo-content">
            <svg className="att-undo-icon" viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
              <path d="M3 7v6h6"/>
              <path d="M3 13a9 9 0 1 0 3-7.7L3 8"/>
            </svg>
            <span className="att-undo-text">Vrátiť späť</span>
            <span className="att-undo-timer">{timerText}</span>
          </span>
        </button>
      )}
    </div>
  );
}

function BreakMenuModal({ onClose, onPick }) {
  return (
    <Modal title="Aký typ prestávky?" onClose={onClose} width={420}>
      <div className="att-break-grid">
        {ATT_BREAK_KINDS.map(b => (
          <button key={b.id} className="att-break-pick" onClick={() => onPick(b.id)} style={{'--c': b.color}}>
            <span className="att-break-ico">{b.icon}</span>
            <span className="att-break-lbl">{b.label}</span>
          </button>
        ))}
      </div>
    </Modal>
  );
}

function TripStartModal({ onClose, onConfirm }) {
  const [destination, setDestination] = React.useState('');
  const [purpose, setPurpose]     = React.useState('');
  const [busy, setBusy] = React.useState(false);
  async function submit() {
    if (!destination.trim()) return;
    setBusy(true);
    await onConfirm({ trip_destination: destination.trim(), trip_purpose: purpose.trim() || null });
    setBusy(false);
  }
  return (
    <Modal title="Začať služobnú cestu" onClose={onClose} width={460}
      footer={<>
        <button className="btn" onClick={onClose}>Zrušiť</button>
        <button className="btn btn-primary" onClick={submit} disabled={busy || !destination.trim()}>Začať cestu</button>
      </>}
    >
      <div className="field">
        <label>Cieľ <span style={{color:'#ef4444'}}>*</span></label>
        <input className="input" value={destination} onChange={e => setDestination(e.target.value)} placeholder="napr. Bratislava, Karlova Ves" autoFocus/>
      </div>
      <div className="field">
        <label>Účel (voliteľne)</label>
        <input className="input" value={purpose} onChange={e => setPurpose(e.target.value)} placeholder="napr. Montáž FVE, reklamácia"/>
      </div>
      <div style={{fontSize:12, color:'#64748b', marginTop:8}}>
        Záznam pôjde nadriadenému na schválenie. GPS sa bude sledovať aj počas cesty.
      </div>
    </Modal>
  );
}

function LeaveZoneModal({ zone, onClose, onTrip, onClockOut }) {
  return (
    <Modal title={`Opustil si ${zone.name}`} onClose={onClose} width={460}
      footer={<>
        <button className="btn" onClick={onClose}>Ignorovať</button>
      </>}
    >
      <p style={{marginTop:0}}>Detegovali sme, že si vyšiel mimo pracoviska <b>{zone.name}</b>. Čo robíš?</p>
      <div className="att-leave-actions">
        <button className="att-btn att-btn-secondary" onClick={onTrip}>
          <span className="att-btn-ico">🚗</span>
          <span className="att-btn-text">Služobná cesta</span>
          <span className="att-btn-sub">Schváli nadriadený</span>
        </button>
        <button className="att-btn att-btn-danger" onClick={onClockOut}>
          <span className="att-btn-ico">⏹</span>
          <span className="att-btn-text">Odchod</span>
          <span className="att-btn-sub">Ukončím zmenu</span>
        </button>
      </div>
    </Modal>
  );
}

// ───── ZonesAdminModal — správa geofence zón ────────────────────────
function ZonesAdminModal({ currentGps, onClose, onChanged }) {
  const [allZones, setAllZones] = React.useState([]);
  const [users, setUsers]       = React.useState([]);
  const [editing, setEditing]   = React.useState(null);
  const [loading, setLoading]   = React.useState(true);

  async function reload() {
    setLoading(true);
    const [zr, ur] = await Promise.all([
      fetch('/api/attendance/zones/all', { credentials: 'same-origin' }).then(r => r.json()),
      fetch('/api/attendance/assignable-users', { credentials: 'same-origin' }).then(r => r.json()),
    ]);
    if (zr.ok) setAllZones(zr.zones || []);
    if (ur.ok) setUsers(ur.users || []);
    setLoading(false);
  }
  React.useEffect(() => { reload(); }, []);

  async function save(zone) {
    const r = await fetch('/api/attendance/zones', {
      method: 'POST', credentials: 'same-origin',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(zone),
    });
    const j = await r.json();
    if (j.ok) { setEditing(null); await reload(); await onChanged(); }
    else alert('Chyba: ' + (j.error || 'neznáma'));
  }
  async function del(id) {
    if (!confirm('Naozaj zmazať túto zónu?')) return;
    const r = await fetch(`/api/attendance/zones/${id}`, { method: 'DELETE', credentials: 'same-origin' });
    if (r.ok) { await reload(); await onChanged(); }
  }

  if (editing) {
    return (
      <ZoneEditModal
        zone={editing}
        users={users}
        currentGps={currentGps}
        onClose={() => setEditing(null)}
        onSave={save}
      />
    );
  }
  return (
    <Modal title="Pracoviská (geofence zóny)" onClose={onClose} width={680}
      footer={<>
        <button className="btn" onClick={onClose}>Zavrieť</button>
        <button className="btn btn-primary" onClick={() => setEditing({
          name: '', address: '',
          lat: currentGps.lat ?? 48.2197, lng: currentGps.lng ?? 17.4019,  // Senec default
          radius_m: 100, is_active: true,
          assigned_users: [], is_global: true,
        })}>
          ➕ Pridať novú zónu
        </button>
      </>}
    >
      {loading ? (
        <div style={{padding:'24px', textAlign:'center', color:'#94a3b8'}}>Načítavam…</div>
      ) : allZones.length === 0 ? (
        <div style={{padding:'16px 0', color:'#94a3b8', textAlign:'center'}}>
          Žiadne zóny. Pridaj prvú — pri pípnutí príchodu v rámci zóny sa zaznamená miesto, pri opustení dostaneš upozornenie.
        </div>
      ) : (
        <div style={{display:'flex', flexDirection:'column', gap:8}}>
          {allZones.map(z => (
            <div key={z.id} style={{display:'flex', alignItems:'center', gap:10, padding:'10px 12px',
              border:'1px solid var(--border)', borderRadius:10, opacity: z.is_active ? 1 : 0.5}}>
              <div style={{flex:1, minWidth:0}}>
                <div style={{display:'flex', alignItems:'center', gap:6, flexWrap:'wrap'}}>
                  <div style={{fontWeight:600, fontSize:14}}>{z.name}</div>
                  {!z.is_active && <span className="chip" style={{background:'#fee2e2', color:'#991b1b', fontSize:10}}>Neaktívna</span>}
                  {z.is_global
                    ? <span className="chip" style={{background:'#dcfce7', color:'#166534', fontSize:10}}>🌍 Pre všetkých</span>
                    : <span className="chip" style={{background:'#dbeafe', color:'#1e40af', fontSize:10}}>👤 {z.assigned_users.length} priradených</span>}
                </div>
                <div style={{fontSize:11, color:'#64748b', marginTop:2}}>
                  {z.lat.toFixed(5)}, {z.lng.toFixed(5)} · polomer {z.radius_m}m
                  {z.address && ` · ${z.address}`}
                </div>
                {!z.is_global && z.assigned_users.length > 0 && (
                  <div style={{fontSize:11, color:'#475569', marginTop:3}}>
                    {z.assigned_users.map(u => u.user_name).slice(0, 4).join(', ')}
                    {z.assigned_users.length > 4 && ` +${z.assigned_users.length - 4} ďalších`}
                  </div>
                )}
              </div>
              <button className="btn btn-xs" onClick={() => setEditing(z)}>✎ Upraviť</button>
              <button className="btn btn-xs btn-ghost" onClick={() => del(z.id)} style={{color:'#ef4444'}}>🗑</button>
            </div>
          ))}
        </div>
      )}
    </Modal>
  );
}

function ZoneEditModal({ zone, users, currentGps, onClose, onSave }) {
  const [name, setName]       = React.useState(zone.name || '');
  const [address, setAddress] = React.useState(zone.address || '');
  const [lat, setLat]         = React.useState(zone.lat ?? 48.2197);
  const [lng, setLng]         = React.useState(zone.lng ?? 17.4019);
  const [radius, setRadius]   = React.useState(zone.radius_m || 100);
  const [active, setActive]   = React.useState(zone.is_active !== false);
  const [isGlobal, setIsGlobal] = React.useState(
    zone.is_global !== undefined ? zone.is_global : (zone.assigned_users || []).length === 0
  );
  const [assignedIds, setAssignedIds] = React.useState(
    new Set((zone.assigned_users || []).map(u => u.user_id))
  );
  const [search, setSearch]   = React.useState('');
  const [searchBusy, setSearchBusy] = React.useState(false);
  const [searchResults, setSearchResults] = React.useState([]);
  const [userFilter, setUserFilter] = React.useState('');
  const isNew = !zone.id;

  const mapRef = React.useRef(null);
  const mapInstRef = React.useRef(null);
  const markerRef = React.useRef(null);
  const circleRef = React.useRef(null);

  // ─── init Leaflet map ───
  React.useEffect(() => {
    if (!mapRef.current || mapInstRef.current) return;
    if (typeof L === 'undefined') {
      console.warn('[zone-edit] Leaflet not loaded');
      return;
    }
    const map = L.map(mapRef.current, { zoomControl: true, scrollWheelZoom: true }).setView([lat, lng], 16);
    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
      maxZoom: 19,
      attribution: '© OpenStreetMap',
    }).addTo(map);

    const marker = L.marker([lat, lng], { draggable: true }).addTo(map);
    const circle = L.circle([lat, lng], { radius: radius, color: '#0e2744', fillColor: '#3b82f6', fillOpacity: 0.18, weight: 2 }).addTo(map);

    marker.on('drag', (e) => {
      const p = e.target.getLatLng();
      circle.setLatLng(p);
      setLat(p.lat); setLng(p.lng);
    });
    map.on('click', (e) => {
      marker.setLatLng(e.latlng);
      circle.setLatLng(e.latlng);
      setLat(e.latlng.lat); setLng(e.latlng.lng);
    });

    mapInstRef.current = map;
    markerRef.current = marker;
    circleRef.current = circle;

    // invalidateSize po krátkom čase — Leaflet potrebuje doupraviť veľkosť keď je v modale.
    setTimeout(() => map.invalidateSize(), 100);
    setTimeout(() => map.invalidateSize(), 400);

    return () => { map.remove(); mapInstRef.current = null; markerRef.current = null; circleRef.current = null; };
    // eslint-disable-next-line
  }, []);

  // ─── update circle radius keď sa zmení slider ───
  React.useEffect(() => {
    if (circleRef.current) circleRef.current.setRadius(Number(radius) || 100);
  }, [radius]);

  // ─── update marker + view keď sa zmenia lat/lng programovo (Use my GPS / search) ───
  React.useEffect(() => {
    const map = mapInstRef.current, marker = markerRef.current, circle = circleRef.current;
    if (!map || !marker || !circle) return;
    const ll = [Number(lat), Number(lng)];
    marker.setLatLng(ll);
    circle.setLatLng(ll);
  }, [lat, lng]);

  function useMyGps() {
    if (currentGps.lat == null) return alert('GPS poloha ešte nie je dostupná. Otvor obrazovku Dochádzka, počkaj kým sa zameria, alebo zadaj adresu nižšie.');
    setLat(currentGps.lat); setLng(currentGps.lng);
    mapInstRef.current?.setView([currentGps.lat, currentGps.lng], 17);
  }

  // ─── Nominatim address search ───
  async function doSearch(e) {
    e?.preventDefault();
    if (!search.trim()) return;
    setSearchBusy(true);
    try {
      const url = `https://nominatim.openstreetmap.org/search?format=jsonv2&limit=5&countrycodes=sk,cz,at,hu,pl&q=${encodeURIComponent(search)}`;
      const r = await fetch(url, { headers: { 'Accept': 'application/json' } });
      const j = await r.json();
      setSearchResults(j || []);
    } catch (err) {
      alert('Chyba pri hľadaní adresy.');
    } finally {
      setSearchBusy(false);
    }
  }
  function pickSearchResult(r) {
    const la = Number(r.lat), lo = Number(r.lon);
    setLat(la); setLng(lo);
    if (!address) setAddress(r.display_name || '');
    if (!name) setName((r.display_name || '').split(',')[0] || '');
    mapInstRef.current?.setView([la, lo], 17);
    setSearchResults([]);
    setSearch('');
  }

  function toggleAssign(uid) {
    setAssignedIds(s => {
      const n = new Set(s);
      if (n.has(uid)) n.delete(uid); else n.add(uid);
      return n;
    });
  }

  function submit() {
    if (!name.trim()) return alert('Názov je povinný.');
    const latN = Number(lat), lngN = Number(lng);
    if (!Number.isFinite(latN) || !Number.isFinite(lngN)) return alert('Neplatné súradnice.');
    onSave({
      id: zone.id,
      name: name.trim(),
      address: address.trim() || null,
      lat: latN, lng: lngN,
      radius_m: parseInt(radius, 10) || 100,
      is_active: active,
      assigned_user_ids: isGlobal ? [] : [...assignedIds],
    });
  }

  const filteredUsers = users.filter(u => {
    if (!userFilter) return true;
    const q = userFilter.toLowerCase();
    return u.name.toLowerCase().includes(q) || (u.email || '').toLowerCase().includes(q);
  });

  return (
    <Modal title={isNew ? 'Nová zóna' : `Upraviť: ${zone.name}`} onClose={onClose} width={860}
      footer={<>
        <button className="btn" onClick={onClose}>Zrušiť</button>
        <button className="btn btn-primary" onClick={submit}>Uložiť zónu</button>
      </>}
    >
      <div className="zone-edit-grid">
        {/* ── ĽAVÁ — mapa + radius ── */}
        <div className="zone-edit-map-col">
          <form onSubmit={doSearch} className="hstack" style={{gap:6}}>
            <input className="input" placeholder="🔍 Hľadať adresu (napr. Lichnerova 12, Senec)" value={search} onChange={e => setSearch(e.target.value)} style={{flex:1}}/>
            <button className="btn" type="submit" disabled={searchBusy}>{searchBusy ? '…' : 'Hľadať'}</button>
          </form>
          {searchResults.length > 0 && (
            <div className="zone-search-results">
              {searchResults.map(r => (
                <button key={r.place_id} className="zone-search-row" onClick={() => pickSearchResult(r)}>
                  <div style={{fontWeight:500, fontSize:13}}>{r.display_name}</div>
                  <div style={{fontSize:11, color:'#94a3b8'}}>{Number(r.lat).toFixed(5)}, {Number(r.lon).toFixed(5)}</div>
                </button>
              ))}
            </div>
          )}

          <div className="zone-edit-map" ref={mapRef}/>

          <div className="hstack" style={{gap:8, marginTop:6, flexWrap:'wrap'}}>
            <button className="btn btn-sm" onClick={useMyGps}>📍 Moja poloha {currentGps.accuracy && `(±${Math.round(currentGps.accuracy)}m)`}</button>
            <div style={{fontSize:11, color:'#64748b', flex:1, textAlign:'right'}}>
              📍 {Number(lat).toFixed(5)}, {Number(lng).toFixed(5)}
            </div>
          </div>

          <div className="field" style={{marginTop:8}}>
            <label>Polomer zóny — <b>{radius} m</b></label>
            <input type="range" min="10" max="500" step="5" value={radius} onChange={e => setRadius(e.target.value)} style={{width:'100%'}}/>
            <div style={{fontSize:11, color:'#94a3b8', display:'flex', justifyContent:'space-between'}}>
              <span>10m budova</span><span>100m areál</span><span>500m park</span>
            </div>
          </div>

          <div style={{fontSize:12, color:'#64748b', background:'#f1f5f9', padding:'8px 10px', borderRadius:8, marginTop:8}}>
            💡 Klikni do mapy alebo potiahni značku na presné miesto. Kruh ukazuje hranicu — opustenie spustí upozornenie.
          </div>
        </div>

        {/* ── PRAVÁ — info + assignment ── */}
        <div className="zone-edit-form-col">
          <div className="field">
            <label>Názov pracoviska <span style={{color:'#ef4444'}}>*</span></label>
            <input className="input" value={name} onChange={e => setName(e.target.value)} placeholder="napr. Sklad Senec" autoFocus/>
          </div>
          <div className="field">
            <label>Adresa (voliteľne)</label>
            <input className="input" value={address} onChange={e => setAddress(e.target.value)} placeholder="napr. Lichnerova 12, Senec"/>
          </div>

          <label style={{display:'flex', alignItems:'center', gap:8, marginTop:4}}>
            <input type="checkbox" checked={active} onChange={e => setActive(e.target.checked)}/>
            <span>Aktívna zóna</span>
          </label>

          <hr style={{margin:'14px 0', border:0, borderTop:'1px solid var(--border)'}}/>

          <div style={{fontWeight:600, fontSize:13, marginBottom:6}}>Priradenie</div>
          <div className="seg" style={{marginBottom:10}}>
            <button className={isGlobal ? 'active' : ''} onClick={() => setIsGlobal(true)} type="button">🌍 Pre všetkých</button>
            <button className={!isGlobal ? 'active' : ''} onClick={() => setIsGlobal(false)} type="button">👤 Iba vybraní</button>
          </div>

          {!isGlobal && (
            <>
              <input className="input" placeholder="🔍 filtrovať používateľov…" value={userFilter} onChange={e => setUserFilter(e.target.value)} style={{marginBottom:6}}/>
              <div className="zone-user-list">
                {filteredUsers.length === 0 && <div style={{padding:8, color:'#94a3b8', fontSize:12}}>Žiadni používatelia.</div>}
                {filteredUsers.map(u => {
                  const checked = assignedIds.has(u.id);
                  return (
                    <label key={u.id} className={`zone-user-row${checked ? ' checked' : ''}`}>
                      <input type="checkbox" checked={checked} onChange={() => toggleAssign(u.id)}/>
                      <div style={{flex:1, minWidth:0}}>
                        <div style={{fontWeight:500, fontSize:13}}>{u.name}</div>
                        <div style={{fontSize:11, color:'#94a3b8'}}>{u.email} · {u.role}</div>
                      </div>
                    </label>
                  );
                })}
              </div>
              <div style={{fontSize:11, color:'#64748b', marginTop:6}}>
                Vybraných: <b>{assignedIds.size}</b> z {users.length}
              </div>
            </>
          )}
          {isGlobal && (
            <div style={{fontSize:12, color:'#475569', background:'#f0fdf4', padding:'10px 12px', borderRadius:8, border:'1px solid #bbf7d0'}}>
              Zóna platí pre <b>všetkých používateľov</b>. Geofence sledovanie sa aktivuje pri každom pípnutí príchodu v tejto oblasti.
            </div>
          )}
        </div>
      </div>
    </Modal>
  );
}

// ───── HistoryModal — denná história (own or team) ─────────────────
function attTodayIso() {
  const d = new Date();
  return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;
}
function attIsoNDaysAgo(n) {
  const d = new Date(); d.setDate(d.getDate() - n);
  return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;
}
function attFmtHm(ms) {
  if (!ms || ms < 0) return '0:00';
  const m = Math.round(ms / 60000);
  return `${Math.floor(m/60)}:${String(m%60).padStart(2,'0')}`;
}

function HistoryModal({ targetUserId, targetUserName, onClose, canTrack = false }) {
  const [from, setFrom] = React.useState(attIsoNDaysAgo(13));
  const [to, setTo]     = React.useState(attTodayIso());
  const [data, setData] = React.useState(null);
  const [loading, setLoading] = React.useState(false);
  const [trackDay, setTrackDay] = React.useState(null);   // iso

  async function load() {
    setLoading(true);
    const qs = `?from=${from}&to=${to}&user_id=${targetUserId}`;
    const r = await fetch('/api/attendance/history' + qs, { credentials: 'same-origin' });
    const j = await r.json();
    if (j.ok) setData(j);
    setLoading(false);
  }
  React.useEffect(() => { load(); /* eslint-disable-next-line */ }, [from, to, targetUserId]);

  const dowNames = ['Ne','Po','Ut','St','Št','Pi','So'];
  const totalWork = data ? data.days.reduce((a, d) => a + d.worked_ms, 0) : 0;
  const totalBreak = data ? data.days.reduce((a, d) => a + d.break_ms, 0) : 0;
  const totalSmoke = data ? data.days.reduce((a, d) => a + d.smoke_count, 0) : 0;

  return (
    <>
      <Modal title={`📅 História — ${targetUserName}`} onClose={onClose} width={760}
        footer={<button className="btn" onClick={onClose}>Zavrieť</button>}
      >
        <div className="hstack" style={{gap:8, marginBottom:10, flexWrap:'wrap'}}>
          <div className="field" style={{margin:0}}>
            <label style={{fontSize:11}}>Od</label>
            <input type="date" className="input" value={from} onChange={e => setFrom(e.target.value)}/>
          </div>
          <div className="field" style={{margin:0}}>
            <label style={{fontSize:11}}>Do</label>
            <input type="date" className="input" value={to} onChange={e => setTo(e.target.value)}/>
          </div>
          <div style={{flex:1}}/>
          <div className="seg">
            <button onClick={() => { setFrom(attIsoNDaysAgo(6)); setTo(attTodayIso()); }}>7 dní</button>
            <button onClick={() => { setFrom(attIsoNDaysAgo(13)); setTo(attTodayIso()); }}>14 dní</button>
            <button onClick={() => { setFrom(attIsoNDaysAgo(29)); setTo(attTodayIso()); }}>30 dní</button>
          </div>
        </div>

        {loading && <div style={{padding:24, textAlign:'center', color:'#94a3b8'}}>Načítavam…</div>}
        {data && !loading && (
          <>
            <div className="att-history-summary">
              <div><div className="lbl">Spolu odpracované</div><div className="val">{attFmtHm(totalWork)}</div></div>
              <div><div className="lbl">Prestávky</div><div className="val">{attFmtHm(totalBreak)}</div></div>
              <div><div className="lbl">Fajčiarske</div><div className="val">{totalSmoke}×</div></div>
              <div><div className="lbl">Pracovných dní</div><div className="val">{data.days.filter(d => d.worked_ms > 0).length}</div></div>
            </div>
            <div className="att-history-table">
              <div className="att-history-thead">
                <div>Deň</div><div>Príchod</div><div>Odchod</div>
                <div>Práca</div><div>Prest.</div><div>🚬</div><div>🍽</div>
                <div></div>
              </div>
              {data.days.slice().reverse().map(d => (
                <div key={d.date} className={`att-history-row${d.is_open ? ' is-open' : ''}${d.worked_ms === 0 ? ' is-empty' : ''}`}>
                  <div>
                    <div style={{fontWeight:600}}>{d.date.slice(8)}.{d.date.slice(5,7)}.</div>
                    <div style={{fontSize:10, color:'#94a3b8'}}>{dowNames[d.dow]}</div>
                  </div>
                  <div>{d.first_in ? attFmtTime(d.first_in) : '—'}</div>
                  <div>{d.last_out ? attFmtTime(d.last_out) : (d.is_open ? '⏳ otv.' : '—')}</div>
                  <div style={{fontWeight:600}}>{d.worked_ms ? attFmtHm(d.worked_ms) : '—'}</div>
                  <div>{d.break_ms ? attFmtHm(d.break_ms) : '—'}</div>
                  <div>{d.smoke_count || ''}</div>
                  <div>{d.lunch_ms ? attFmtHm(d.lunch_ms) : ''}</div>
                  <div>
                    {d.worked_ms > 0 && (
                      <button className="btn btn-xs" onClick={() => setTrackDay(d.date)} title="Pozri trasu">🗺</button>
                    )}
                  </div>
                </div>
              ))}
            </div>
          </>
        )}
      </Modal>
      {trackDay && (
        <TrackingMapModal userId={targetUserId} userName={targetUserName} date={trackDay} onClose={() => setTrackDay(null)}/>
      )}
    </>
  );
}

// ───── TeamStatsModal — admin: štatistiky, export, tracking ─────────
function TeamStatsModal({ onClose }) {
  const [users, setUsers] = React.useState([]);
  const [selUser, setSelUser] = React.useState(null);
  const [from, setFrom] = React.useState(attIsoNDaysAgo(29));
  const [to, setTo]     = React.useState(attTodayIso());
  const [statsData, setStatsData] = React.useState(null);
  const [historyForUser, setHistoryForUser] = React.useState(null);
  const [trackDate, setTrackDate] = React.useState(attTodayIso());
  const [trackOpen, setTrackOpen] = React.useState(false);

  React.useEffect(() => {
    fetch('/api/attendance/assignable-users', { credentials: 'same-origin' })
      .then(r => r.json())
      .then(j => { if (j.ok) setUsers(j.users || []); });
  }, []);

  React.useEffect(() => {
    if (!selUser) return;
    const qs = `?from=${from}&to=${to}&user_id=${selUser.id}`;
    fetch('/api/attendance/stats' + qs, { credentials: 'same-origin' })
      .then(r => r.json())
      .then(j => { if (j.ok) setStatsData(j); });
  }, [selUser, from, to]);

  function downloadCsv() {
    if (!selUser) return;
    const url = `/api/attendance/export.csv?from=${from}&to=${to}&user_id=${selUser.id}`;
    window.location.href = url;
  }

  return (
    <>
      <Modal title="📊 Štatistiky tímu & export" onClose={onClose} width={780}
        footer={<button className="btn" onClick={onClose}>Zavrieť</button>}
      >
        <div className="hstack" style={{gap:8, flexWrap:'wrap'}}>
          <div className="field" style={{flex:'1 1 240px', margin:0}}>
            <label style={{fontSize:11}}>Zamestnanec</label>
            <select className="input" value={selUser?.id || ''} onChange={e => setSelUser(users.find(u => u.id === Number(e.target.value)) || null)}>
              <option value="">— vyber —</option>
              {users.map(u => <option key={u.id} value={u.id}>{u.name} ({u.email})</option>)}
            </select>
          </div>
          <div className="field" style={{margin:0}}>
            <label style={{fontSize:11}}>Od</label>
            <input type="date" className="input" value={from} onChange={e => setFrom(e.target.value)}/>
          </div>
          <div className="field" style={{margin:0}}>
            <label style={{fontSize:11}}>Do</label>
            <input type="date" className="input" value={to} onChange={e => setTo(e.target.value)}/>
          </div>
        </div>
        <div className="hstack" style={{gap:6, marginTop:8, flexWrap:'wrap'}}>
          <div className="seg">
            <button onClick={() => { setFrom(attIsoNDaysAgo(6)); setTo(attTodayIso()); }}>7 dní</button>
            <button onClick={() => { setFrom(attIsoNDaysAgo(29)); setTo(attTodayIso()); }}>30 dní</button>
            <button onClick={() => {
              const d = new Date(); d.setDate(1);
              setFrom(`${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-01`);
              setTo(attTodayIso());
            }}>Tento mesiac</button>
            <button onClick={() => {
              const d = new Date(); d.setMonth(d.getMonth() - 1); d.setDate(1);
              const last = new Date(d.getFullYear(), d.getMonth() + 1, 0);
              setFrom(`${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-01`);
              setTo(`${last.getFullYear()}-${String(last.getMonth()+1).padStart(2,'0')}-${String(last.getDate()).padStart(2,'0')}`);
            }}>Minulý mesiac</button>
          </div>
          <div style={{flex:1}}/>
          <button className="btn" onClick={() => selUser && setHistoryForUser(selUser)} disabled={!selUser}>📅 Detailná história</button>
          <button className="btn btn-primary" onClick={downloadCsv} disabled={!selUser}>⬇ Export CSV</button>
        </div>

        {selUser && (
          <div className="att-track-shortcut">
            <span style={{fontSize:13, fontWeight:600}}>🗺 Trasa pohybu pre deň:</span>
            <input type="date" className="input" value={trackDate} onChange={e => setTrackDate(e.target.value)} max={attTodayIso()} style={{width:160}}/>
            <button className="btn btn-sm" onClick={() => setTrackDate(attTodayIso())}>Dnes</button>
            <button className="btn btn-sm" onClick={() => { const d = new Date(); d.setDate(d.getDate()-1); setTrackDate(`${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`); }}>Včera</button>
            <button className="btn btn-primary btn-sm" onClick={() => setTrackOpen(true)}>Zobraziť trasu</button>
          </div>
        )}

        {!selUser && (
          <div style={{padding:24, textAlign:'center', color:'#94a3b8', marginTop:14}}>
            Vyber zamestnanca a obdobie. Potom stiahni CSV alebo si pozri detailnú dennú históriu vrátane GPS trasy.
          </div>
        )}
        {selUser && statsData && (
          <div className="att-stats-cards" style={{marginTop:16}}>
            <div className="att-stats-card primary">
              <div className="lbl">Spolu odpracované</div>
              <div className="val">{attFmtHm(statsData.total_worked_ms)}</div>
              <div className="sub">{statsData.work_days} pracovných dní</div>
            </div>
            <div className="att-stats-card">
              <div className="lbl">Priem./deň</div>
              <div className="val">{attFmtHm(statsData.avg_worked_ms_per_workday)}</div>
            </div>
            <div className="att-stats-card">
              <div className="lbl">Prestávky spolu</div>
              <div className="val">{attFmtHm(statsData.total_break_ms)}</div>
            </div>
            <div className="att-stats-card">
              <div className="lbl">🚬 Fajčiarske</div>
              <div className="val">{statsData.total_smoke_count}×</div>
              <div className="sub">{attFmtHm(statsData.total_smoke_ms)} celkom</div>
            </div>
            <div className="att-stats-card">
              <div className="lbl">🍽 Obedy</div>
              <div className="val">{attFmtHm(statsData.total_lunch_ms)}</div>
            </div>
            <div className="att-stats-card">
              <div className="lbl">🚗 Sluz. cesty</div>
              <div className="val">{attFmtHm(statsData.total_trip_ms)}</div>
            </div>
          </div>
        )}
      </Modal>
      {historyForUser && (
        <HistoryModal
          targetUserId={historyForUser.id}
          targetUserName={historyForUser.name}
          canTrack={true}
          onClose={() => setHistoryForUser(null)}
        />
      )}
      {trackOpen && selUser && (
        <TrackingMapModal
          userId={selUser.id}
          userName={selUser.name}
          date={trackDate}
          onClose={() => setTrackOpen(false)}
        />
      )}
    </>
  );
}

// ───── TrackingMapModal — Leaflet mapa + MiCODUS-style playback ────
// Pingy + punche zoradené podľa času; používateľ stláča ▶ a marker sa pohybuje
// po trase. Speed selector (1×, 10×, 60×, 300×, 600×), scrubber slider,
// "covered" polyline svieti zelene, ešte neprešitá modro.
function TrackingMapModal({ userId, userName, date, onClose }) {
  const mapRef = React.useRef(null);
  const mapInstRef = React.useRef(null);
  const polylinePastRef = React.useRef(null);
  const polylineFutureRef = React.useRef(null);
  const playMarkerRef = React.useRef(null);
  const punchMarkersRef = React.useRef([]);
  const pingMarkersRef = React.useRef([]);
  const animRef = React.useRef(null);
  const lastTickRef = React.useRef(0);

  const [data, setData] = React.useState(null);
  const [loading, setLoading] = React.useState(true);
  const [playhead, setPlayhead] = React.useState(0);   // ms timestamp, kde je marker
  const [playing, setPlaying] = React.useState(false);
  const [speed, setSpeed] = React.useState(60);        // násobok reálneho času

  // ── load data ──
  React.useEffect(() => {
    setLoading(true);
    fetch(`/api/attendance/track?date=${date}&user_id=${userId}`, { credentials: 'same-origin' })
      .then(r => r.json())
      .then(j => {
        if (j.ok) {
          setData(j);
          // playhead = čas prvého eventu dňa
          const allTs = [...j.pings.map(p => p.pinged_at), ...j.punches.map(p => p.punched_at)].sort((a, b) => a - b);
          if (allTs.length) setPlayhead(allTs[0]);
        }
        setLoading(false);
      })
      .catch(() => setLoading(false));
  }, [userId, date]);

  // ── time range ──
  const range = React.useMemo(() => {
    if (!data) return null;
    const ts = [
      ...data.pings.map(p => p.pinged_at),
      ...data.punches.map(p => p.punched_at),
    ].filter(Boolean).sort((a, b) => a - b);
    if (!ts.length) return null;
    return { start: ts[0], end: ts[ts.length - 1] };
  }, [data]);

  // ── init mapa raz keď máme dáta ──
  React.useEffect(() => {
    if (!data || !mapRef.current || mapInstRef.current) return;
    if (typeof L === 'undefined') return;
    const allPoints = [
      ...data.pings.map(p => [p.lat, p.lng]),
      ...data.punches.map(p => [p.lat, p.lng]),
    ].filter(p => p[0] != null && p[1] != null);
    if (allPoints.length === 0) return;

    const map = L.map(mapRef.current, { zoomControl: true }).fitBounds(allPoints, { padding: [30, 30] });
    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19, attribution: '© OSM' }).addTo(map);

    // future polyline (svetlomodrá, celá trasa)
    polylineFutureRef.current = L.polyline(
      data.pings.map(p => [p.lat, p.lng]),
      { color: '#94a3b8', weight: 3, opacity: 0.5, dashArray: '5,8' }
    ).addTo(map);

    // past polyline (zelená, doplní sa pri prehrávaní)
    polylinePastRef.current = L.polyline([], {
      color: '#10b981', weight: 5, opacity: 0.9, smoothFactor: 1.2,
    }).addTo(map);

    // tiché ping bodky
    pingMarkersRef.current = data.pings.map(p => {
      const m = L.circleMarker([p.lat, p.lng], {
        radius: 2.5, color: '#94a3b8', fillColor: '#94a3b8', fillOpacity: 0.5, weight: 1,
      })
        .bindTooltip(`${attFmtTime(p.pinged_at)} · ±${Math.round(p.accuracy || 0)}m${p.battery_pct ? ' · 🔋' + p.battery_pct + '%' : ''}`, { direction: 'top' })
        .addTo(map);
      m._pingTs = p.pinged_at;
      return m;
    });

    // punch markery (defaultne mierne stlmené, pri prehraní sa rozsvietia)
    const punchColors = {
      clock_in: '#10b981', clock_out: '#ef4444',
      break_start: '#f59e0b', break_end: '#3b82f6',
      trip_start: '#8a5a2b', trip_end: '#8a5a2b',
    };
    const punchEmojis = {
      clock_in: '▶', clock_out: '⏹',
      break_start: '⏸', break_end: '↺',
      trip_start: '🚗', trip_end: '🏁',
    };
    punchMarkersRef.current = data.punches.map(p => {
      const icon = L.divIcon({
        html: `<div class="att-punch-marker" data-bg="${punchColors[p.punch_type] || '#64748b'}" style="background:${punchColors[p.punch_type] || '#64748b'};">${punchEmojis[p.punch_type] || '•'}</div>`,
        className: '', iconSize: [30, 30], iconAnchor: [15, 15],
      });
      const m = L.marker([p.lat, p.lng], { icon })
        .bindPopup(`<b>${attFmtTime(p.punched_at)} — ${punchLabelStatic(p.punch_type, p.break_kind)}</b><br/>${p.location_label || 'mimo zóny'}${p.accuracy ? ' · ±' + Math.round(p.accuracy) + 'm' : ''}`)
        .addTo(map);
      m._punchTs = p.punched_at;
      return m;
    });

    // playback marker — väčší modrý kruh ktorý sa pohybuje
    const playIcon = L.divIcon({
      html: `<div class="att-playhead-marker"></div>`,
      className: '', iconSize: [22, 22], iconAnchor: [11, 11],
    });
    const startPoint = allPoints[0];
    playMarkerRef.current = L.marker(startPoint, { icon: playIcon, zIndexOffset: 1000 }).addTo(map);

    mapInstRef.current = map;
    setTimeout(() => map.invalidateSize(), 100);
    return () => {
      if (animRef.current) cancelAnimationFrame(animRef.current);
      map.remove();
      mapInstRef.current = null;
      polylinePastRef.current = null;
      polylineFutureRef.current = null;
      playMarkerRef.current = null;
      punchMarkersRef.current = [];
      pingMarkersRef.current = [];
    };
  }, [data]);

  // ── prekresliť pozíciu markera + polyline keď sa zmení playhead ──
  React.useEffect(() => {
    if (!data || !mapInstRef.current || !playMarkerRef.current) return;
    const pings = data.pings;
    if (!pings.length) return;

    // interpolácia: nájdi dva susedné pingy okolo playhead
    let before = pings[0], after = pings[pings.length - 1];
    for (let i = 0; i < pings.length - 1; i++) {
      if (pings[i].pinged_at <= playhead && pings[i + 1].pinged_at >= playhead) {
        before = pings[i]; after = pings[i + 1];
        break;
      }
    }
    if (playhead <= pings[0].pinged_at) { before = pings[0]; after = pings[0]; }
    if (playhead >= pings[pings.length - 1].pinged_at) { before = pings[pings.length - 1]; after = pings[pings.length - 1]; }
    const span = after.pinged_at - before.pinged_at;
    const t = span > 0 ? (playhead - before.pinged_at) / span : 0;
    const lat = before.lat + (after.lat - before.lat) * t;
    const lng = before.lng + (after.lng - before.lng) * t;
    playMarkerRef.current.setLatLng([lat, lng]);

    // past polyline = všetky pingy s ts <= playhead + interpolovaný bod
    const past = pings.filter(p => p.pinged_at <= playhead).map(p => [p.lat, p.lng]);
    past.push([lat, lng]);
    polylinePastRef.current?.setLatLngs(past);

    // punch markery — viditeľne stlmené ak ešte "nenastali"
    punchMarkersRef.current.forEach(m => {
      const el = m.getElement();
      if (!el) return;
      const inner = el.querySelector('.att-punch-marker');
      if (!inner) return;
      const passed = m._punchTs <= playhead;
      inner.style.opacity = passed ? '1' : '0.35';
      inner.style.transform = passed ? 'scale(1)' : 'scale(0.85)';
    });
  }, [playhead, data]);

  // ── animation loop ──
  React.useEffect(() => {
    if (!playing || !range) return;
    lastTickRef.current = performance.now();
    function tick(now) {
      const delta = now - lastTickRef.current;
      lastTickRef.current = now;
      setPlayhead(p => {
        const next = p + delta * speed;
        if (next >= range.end) {
          setPlaying(false);
          return range.end;
        }
        return next;
      });
      animRef.current = requestAnimationFrame(tick);
    }
    animRef.current = requestAnimationFrame(tick);
    return () => { if (animRef.current) cancelAnimationFrame(animRef.current); };
  }, [playing, speed, range]);

  // ── kontroly ──
  function togglePlay() {
    if (!range) return;
    if (playhead >= range.end) setPlayhead(range.start);
    setPlaying(p => !p);
  }
  function rewind() { if (range) { setPlayhead(range.start); setPlaying(false); } }
  function jumpToPunch(p) {
    setPlayhead(p.punched_at);
    setPlaying(false);
    if (mapInstRef.current) mapInstRef.current.panTo([p.lat, p.lng], { animate: true });
  }

  const speeds = [
    { v: 10, label: '10×' },
    { v: 60, label: '1 min/s' },
    { v: 300, label: '5 min/s' },
    { v: 1800, label: '30 min/s' },
    { v: 3600, label: '1 h/s' },
  ];

  const playPercent = range ? ((playhead - range.start) / Math.max(1, range.end - range.start)) * 100 : 0;

  return (
    <Modal title={`🗺 Trasa — ${userName} · ${date}`} onClose={onClose} width={920}
      footer={<button className="btn" onClick={onClose}>Zavrieť</button>}
    >
      {loading && <div style={{padding:24, textAlign:'center', color:'#94a3b8'}}>Načítavam…</div>}
      {data && !loading && (
        <>
          <div style={{display:'flex', gap:12, marginBottom:8, fontSize:12, color:'#475569', flexWrap:'wrap'}}>
            <span>📍 {data.pings.length} GPS bodov</span>
            <span>⏱ {data.punches.length} eventov</span>
            {range && <span>🕐 {attFmtTime(range.start)} → {attFmtTime(range.end)}</span>}
          </div>
          {data.pings.length === 0 && data.punches.length === 0 ? (
            <div style={{padding:24, textAlign:'center', color:'#94a3b8'}}>
              Žiadne GPS dáta pre tento deň. (Buď nebol prihlásený, alebo nemal povolené sledovanie polohy.)
            </div>
          ) : (
            <>
              <div className="zone-edit-map" ref={mapRef} style={{height: 440}}/>

              {/* Playback toolbar */}
              {range && (
                <div className="att-playback">
                  <div className="att-playback-time">
                    <div className="att-playback-now">{attFmtTimeWithSec(playhead)}</div>
                    <div className="att-playback-range">{attFmtTime(range.start)} – {attFmtTime(range.end)}</div>
                  </div>
                  <div className="att-playback-controls">
                    <button className="att-pb-btn" onClick={rewind} title="Reset na začiatok">⏮</button>
                    <button className="att-pb-btn att-pb-play" onClick={togglePlay}>{playing ? '⏸' : '▶'}</button>
                    <select className="att-pb-speed" value={speed} onChange={e => setSpeed(Number(e.target.value))}>
                      {speeds.map(s => <option key={s.v} value={s.v}>{s.label}</option>)}
                    </select>
                  </div>
                  <input
                    type="range"
                    className="att-playback-slider"
                    min={range.start}
                    max={range.end}
                    step={1000}
                    value={playhead}
                    onChange={e => { setPlayhead(Number(e.target.value)); setPlaying(false); }}
                    style={{ '--pct': playPercent + '%' }}
                  />
                </div>
              )}

              {/* Timeline punchov — klik skočí na ten čas */}
              <div className="att-track-timeline">
                {data.punches.map(p => {
                  const passed = p.punched_at <= playhead;
                  return (
                    <button
                      key={p.id}
                      className={`att-track-time-item${passed ? '' : ' upcoming'}`}
                      onClick={() => jumpToPunch(p)}
                    >
                      <span style={{fontWeight:600, width:48}}>{attFmtTime(p.punched_at)}</span>
                      <span style={{flex:1}}>{punchLabelStatic(p.punch_type, p.break_kind)}</span>
                      {p.location_label && <span style={{color:'#94a3b8'}}>{p.location_label}</span>}
                    </button>
                  );
                })}
              </div>
            </>
          )}
        </>
      )}
    </Modal>
  );
}

function attFmtTimeWithSec(ms) {
  if (ms == null) return '—';
  const d = new Date(ms);
  return `${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}:${String(d.getSeconds()).padStart(2,'0')}`;
}

// Pomocný label pre punch type (samostatne kvôli použitiu v tracking map popupoch).
function punchLabelStatic(type, breakKind) {
  if (type === 'clock_in')    return 'Príchod';
  if (type === 'clock_out')   return 'Odchod';
  if (type === 'break_start') return attBreakMeta(breakKind).label + ' — štart';
  if (type === 'break_end')   return 'Návrat z prestávky';
  if (type === 'trip_start')  return 'Služobná cesta — štart';
  if (type === 'trip_end')    return 'Služobná cesta — koniec';
  return type;
}

// ───── PlanTripModal — žiadosť o SC vopred ──────────────────────────
function PlanTripModal({ onClose, onSubmitted }) {
  const [destination, setDestination] = React.useState('');
  const [purpose, setPurpose]     = React.useState('');
  const [from, setFrom]           = React.useState(attTodayIso());
  const [to, setTo]               = React.useState(attTodayIso());
  const [myManager, setMyManager] = React.useState(null);
  const [busy, setBusy] = React.useState(false);
  const [err, setErr]   = React.useState('');

  React.useEffect(() => {
    fetch('/api/attendance/my-manager', { credentials: 'same-origin' })
      .then(r => r.json())
      .then(j => { if (j.ok) setMyManager(j); });
  }, []);

  async function submit() {
    setErr('');
    if (!destination.trim()) return setErr('Cieľ je povinný.');
    if (to < from) return setErr('Dátum „do" nemôže byť skôr ako „od".');
    setBusy(true);
    const r = await fetch('/api/attendance/trips', {
      method: 'POST', credentials: 'same-origin',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        destination: destination.trim(),
        purpose: purpose.trim() || null,
        planned_from: from, planned_to: to,
      }),
    });
    const j = await r.json();
    setBusy(false);
    if (j.ok) {
      try { window.dispatchEvent(new CustomEvent('dd-notifications-refresh')); } catch {}
      onSubmitted?.(j);
    } else setErr(j.error || 'Chyba.');
  }

  return (
    <Modal title="📅 Naplánovať služobnú cestu" onClose={onClose} width={520}
      footer={<>
        <button className="btn" onClick={onClose}>Zrušiť</button>
        <button className="btn btn-primary" onClick={submit} disabled={busy || !destination.trim()}>Odoslať na schválenie</button>
      </>}
    >
      {err && <div className="auth-alert" role="alert" style={{marginBottom:10}}><span>{err}</span></div>}
      <div className="field">
        <label>Cieľ <span style={{color:'#ef4444'}}>*</span></label>
        <input className="input" value={destination} onChange={e => setDestination(e.target.value)} placeholder="napr. Bratislava, Karlova Ves" autoFocus/>
      </div>
      <div className="field">
        <label>Účel cesty (voliteľne)</label>
        <input className="input" value={purpose} onChange={e => setPurpose(e.target.value)} placeholder="napr. Montáž FVE, reklamácia, školenie"/>
      </div>
      <div className="hstack" style={{gap:8}}>
        <div className="field" style={{flex:1}}>
          <label>Od</label>
          <input type="date" className="input" value={from} onChange={e => setFrom(e.target.value)}/>
        </div>
        <div className="field" style={{flex:1}}>
          <label>Do</label>
          <input type="date" className="input" value={to} onChange={e => setTo(e.target.value)} min={from}/>
        </div>
      </div>
      <div style={{fontSize:12, color:'#475569', background:'#f0f9ff', padding:'10px 12px', borderRadius:8, marginTop:8, border:'1px solid #bae6fd'}}>
        {myManager?.manager
          ? <>Žiadosť pôjde na schválenie: <b>{myManager.manager.name}</b> ({myManager.manager.email})</>
          : myManager?.fallback_admins?.length
            ? <>Nemáš priradeného manažéra — žiadosť pôjde adminom: <b>{myManager.fallback_admins.map(a => a.name).join(', ')}</b></>
            : 'Žiadosť pôjde na schválenie nadriadenému.'}
      </div>
    </Modal>
  );
}

// ───── ApproveTripsModal — manažér schvaľuje / zamieta SC ─────────
function ApproveTripsModal({ onClose, onChanged }) {
  const [trips, setTrips] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [acting, setActing] = React.useState({});  // { tripId: 'approving' | 'rejecting' }

  async function load() {
    setLoading(true);
    const r = await fetch('/api/attendance/trips/pending', { credentials: 'same-origin' });
    const j = await r.json();
    if (j.ok) setTrips(j.trips || []);
    setLoading(false);
  }
  React.useEffect(() => { load(); }, []);

  async function act(trip, kind) {
    const note = kind === 'reject' ? (prompt('Dôvod zamietnutia (voliteľne):') || '') : '';
    setActing(s => ({ ...s, [trip.id]: kind === 'approve' ? 'approving' : 'rejecting' }));
    const r = await fetch(`/api/attendance/trips/${trip.id}/${kind}`, {
      method: 'POST', credentials: 'same-origin',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ note }),
    });
    setActing(s => { const n = { ...s }; delete n[trip.id]; return n; });
    if (r.ok) {
      try { window.dispatchEvent(new CustomEvent('dd-notifications-refresh')); } catch {}
      onChanged?.();
      load();
    }
  }

  function fmtSk(ts) {
    if (!ts) return '';
    const d = new Date(ts * 1000);
    return `${String(d.getDate()).padStart(2,'0')}.${String(d.getMonth()+1).padStart(2,'0')}. ${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}`;
  }

  return (
    <Modal title="📥 Žiadosti o služobnú cestu" onClose={onClose} width={640}
      footer={<button className="btn" onClick={onClose}>Zavrieť</button>}
    >
      {loading && <div style={{padding:24, textAlign:'center', color:'#94a3b8'}}>Načítavam…</div>}
      {!loading && trips.length === 0 && (
        <div style={{padding:32, textAlign:'center', color:'#94a3b8'}}>
          🎉 Žiadne nevybavené žiadosti.
        </div>
      )}
      {!loading && trips.length > 0 && (
        <div style={{display:'flex', flexDirection:'column', gap:10}}>
          {trips.map(t => {
            const isPlan = !!t.planned_from;
            const isInProgress = !!t.started_at && !t.ended_at;
            return (
              <div key={t.id} className="att-trip-card">
                <div className="att-trip-head">
                  <div>
                    <div className="att-trip-user">{t.user_name}</div>
                    <div className="att-trip-meta">{t.user_email}</div>
                  </div>
                  <span className={`chip ${isInProgress ? 'amber' : isPlan ? 'blue' : 'navy'}`} style={{fontSize:10}}>
                    {isInProgress ? '🔴 PRÁVE PRECHÁDZA' : isPlan ? '📅 Plán' : '⏱ Okamžitá'}
                  </span>
                </div>
                <div className="att-trip-body">
                  <div style={{fontWeight:600, fontSize:14}}>📍 {t.destination || '(bez cieľa)'}</div>
                  {t.purpose && <div style={{fontSize:13, color:'#475569', marginTop:2}}>{t.purpose}</div>}
                  <div style={{fontSize:11, color:'#64748b', marginTop:6, display:'flex', flexWrap:'wrap', gap:10}}>
                    {isPlan && <span>📅 {t.planned_from}{t.planned_to !== t.planned_from ? ` → ${t.planned_to}` : ''}</span>}
                    {t.started_at && <span>▶ Začaté: {fmtSk(Math.floor(t.started_at/1000))}</span>}
                    <span>Žiadosť: {fmtSk(t.created_at)}</span>
                  </div>
                </div>
                <div className="att-trip-actions">
                  <button className="btn btn-sm" disabled={!!acting[t.id]} onClick={() => act(t, 'reject')}>
                    {acting[t.id] === 'rejecting' ? '…' : '✕ Zamietnuť'}
                  </button>
                  <button className="btn btn-sm btn-primary" disabled={!!acting[t.id]} onClick={() => act(t, 'approve')}>
                    {acting[t.id] === 'approving' ? '…' : '✓ Schváliť'}
                  </button>
                </div>
              </div>
            );
          })}
        </div>
      )}
    </Modal>
  );
}

// ───── ManagerAssignModal — admin priradenie viacerých nadriadených k userovi ──
function ManagerAssignModal({ onClose, onChanged }) {
  const [users, setUsers] = React.useState([]);
  const [loading, setLoading] = React.useState(true);
  const [filter, setFilter] = React.useState('');
  const [editingUser, setEditingUser] = React.useState(null);

  async function load() {
    setLoading(true);
    const r = await fetch('/api/attendance/assignable-users', { credentials: 'same-origin' });
    const j = await r.json();
    if (j.ok) setUsers(j.users || []);
    setLoading(false);
  }
  React.useEffect(() => { load(); }, []);

  const filtered = users.filter(u => {
    if (!filter) return true;
    const q = filter.toLowerCase();
    return u.name.toLowerCase().includes(q) || (u.email || '').toLowerCase().includes(q);
  });

  return (
    <>
      <Modal title="👥 Priradenie manažérov" onClose={onClose} width={680}
        footer={<button className="btn" onClick={onClose}>Zavrieť</button>}
      >
        <input className="input" placeholder="🔍 filtrovať podľa mena / emailu…" value={filter} onChange={e => setFilter(e.target.value)} style={{marginBottom:10}}/>
        {loading && <div style={{padding:24, textAlign:'center', color:'#94a3b8'}}>Načítavam…</div>}
        {!loading && (
          <div style={{display:'flex', flexDirection:'column', gap:6}}>
            {filtered.map(u => (
              <div key={u.id} className="att-mgr-row">
                <div style={{flex:1, minWidth:0}}>
                  <div style={{fontWeight:600, fontSize:13}}>{u.name}</div>
                  <div style={{fontSize:11, color:'#94a3b8'}}>{u.email} · {u.role}</div>
                  <div className="att-mgr-chips">
                    {u.managers && u.managers.length > 0
                      ? u.managers.map(m => <span key={m.id} className="chip" style={{background:'#dbeafe', color:'#1e40af', fontSize:10, padding:'1px 6px'}}>{m.name}</span>)
                      : <span style={{fontSize:11, color:'#94a3b8', fontStyle:'italic'}}>bez manažéra (fallback na adminov)</span>}
                  </div>
                </div>
                <button className="btn btn-sm" onClick={() => setEditingUser(u)}>✎ Upraviť</button>
              </div>
            ))}
          </div>
        )}
        <div style={{fontSize:12, color:'#64748b', marginTop:12, padding:'10px 12px', background:'#f1f5f9', borderRadius:8}}>
          💡 Worker môže mať <b>viac manažérov</b> — notifikáciu o SC/dovolenke dostanú všetci priradení.
          Stačí, ak schváli ktorýkoľvek z nich.
        </div>
      </Modal>
      {editingUser && (
        <UserManagersEditModal
          user={editingUser}
          allUsers={users}
          onClose={() => setEditingUser(null)}
          onSaved={() => { setEditingUser(null); load(); onChanged?.(); }}
        />
      )}
    </>
  );
}

// Sub-modal: vybrať viacerých manažérov pre jedného workera (multi-checkbox).
function UserManagersEditModal({ user, allUsers, onClose, onSaved }) {
  const [selected, setSelected] = React.useState(
    new Set((user.managers || []).map(m => m.id))
  );
  const [filter, setFilter] = React.useState('');
  const [saving, setSaving] = React.useState(false);

  const candidates = allUsers
    .filter(u => u.id !== user.id)
    .filter(u => {
      if (!filter) return true;
      const q = filter.toLowerCase();
      return u.name.toLowerCase().includes(q) || (u.email || '').toLowerCase().includes(q);
    });

  function toggle(id) {
    setSelected(s => { const n = new Set(s); if (n.has(id)) n.delete(id); else n.add(id); return n; });
  }

  async function save() {
    setSaving(true);
    const r = await fetch(`/api/attendance/users/${user.id}/managers`, {
      method: 'PUT', credentials: 'same-origin',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ manager_ids: [...selected] }),
    });
    const j = await r.json();
    setSaving(false);
    if (j.ok) onSaved?.();
    else alert('Chyba: ' + (j.error || 'neznáma'));
  }

  return (
    <Modal title={`Manažéri pre: ${user.name}`} onClose={onClose} width={520}
      footer={<>
        <button className="btn" onClick={onClose}>Zrušiť</button>
        <button className="btn btn-primary" onClick={save} disabled={saving}>
          {saving ? 'Ukladám…' : `Uložiť (${selected.size})`}
        </button>
      </>}
    >
      <div style={{fontSize:12, color:'#475569', marginBottom:8}}>
        Zaškrtni ktorí používatelia môžu schvaľovať žiadosti od <b>{user.name}</b>. Môžeš ich vybrať aj viac.
      </div>
      <input className="input" placeholder="🔍 filtrovať…" value={filter} onChange={e => setFilter(e.target.value)} style={{marginBottom:8}}/>
      <div className="zone-user-list" style={{maxHeight:340}}>
        {candidates.length === 0 && <div style={{padding:10, color:'#94a3b8', fontSize:12}}>Žiadni používatelia.</div>}
        {candidates.map(c => {
          const checked = selected.has(c.id);
          return (
            <label key={c.id} className={`zone-user-row${checked ? ' checked' : ''}`}>
              <input type="checkbox" checked={checked} onChange={() => toggle(c.id)}/>
              <div style={{flex:1, minWidth:0}}>
                <div style={{fontWeight:500, fontSize:13}}>{c.name}</div>
                <div style={{fontSize:11, color:'#94a3b8'}}>{c.email} · {c.role}</div>
              </div>
            </label>
          );
        })}
      </div>
      <div style={{fontSize:11, color:'#64748b', marginTop:6}}>
        Vybraných: <b>{selected.size}</b> {selected.size === 0 && '— fallback na adminov'}
      </div>
    </Modal>
  );
}

// expose
Object.assign(window, { AttendanceScreen });
