/* app.jsx — Veloroute controller: real Sofia map + real route + screen flow + tabs + tweaks. */
const { useState, useEffect, useRef } = React;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "accent": "#df6b46",
  "theme": "Светла",
  "detail": "Стандартна",
  "font": "Sofia Sans",
  "lang": "БГ"
}/*EDITMODE-END*/;

const THEME_MAP = { 'Светла': 'light', 'Тъмна': 'dark', 'Каране': 'riding' };
const DETAIL_MAP = { 'Минимална': 'minimal', 'Стандартна': 'standard', 'Детайlност': 'detailed', 'Детайлна': 'detailed' };

const SCREEN_MAP = {
  home:      { view: 'home',  route: 'none' },
  premium:   { view: 'home',  route: 'none' },
  bike:      { view: 'home',  route: 'none' },
  notifications: { view: 'home', route: 'none' },
  search:    { view: 'home',  route: 'none' },
  route:     { view: 'route', route: 'plan' },
  nav:       { view: 'nav',   route: 'active' },
  routes:    { view: 'home',  route: 'none' },
  community: { view: 'home',  route: 'none' },
  profile:   { view: 'home',  route: 'none' },
  onboarding:{ view: 'home',  route: 'none' },
  auth:      { view: 'home',  route: 'none' },
  saved:     { view: 'home',  route: 'none' },
  language:  { view: 'home',  route: 'none' },
  routeprefs:{ view: 'home',  route: 'none' },
  contribute:{ view: 'home',  route: 'none' },
  reset:     { view: 'home',  route: 'none' },
  changepass:{ view: 'home',  route: 'none' },
};
const TAB_OF = { home: 'map', search: 'map', route: 'map', nav: 'map', routes: 'routes', community: 'community', profile: 'profile' };

// real endpoints in central Sofia: Lozenets → Народен театър „Иван Вазов"
const START = window.VR_ENDPOINTS.start;
const DEST = window.VR_ENDPOINTS.dest;

// Static descriptive labels per role (tag/note). Safety fields — level, score, avoids, pattern —
// are computed from the real safety layer in buildRoute(); ETA + elevation gain come from the
// graded timing model (elevation.jsx), with real denivelation folded in by vrEnrichRouteElevation.
const ROUTE_META = {
  fast:   { id: 'fast',   tag: 'Най-бърз',      note: 'По-кратък, но по-натоварени булеварди' },
  safe:   { id: 'safe',   tag: 'Най-безопасен', note: 'Тихи централни улици и велоалеи' },
  scenic: { id: 'scenic', tag: 'През парка',    note: 'Покрай Борисова градина' },
};
function hav(a, b) {
  const R = 6371000, toR = (x) => x * Math.PI / 180;
  const dLat = toR(b[0] - a[0]), dLng = toR(b[1] - a[1]);
  const s = Math.sin(dLat / 2) ** 2 + Math.cos(toR(a[0])) * Math.cos(toR(b[0])) * Math.sin(dLng / 2) ** 2;
  return 2 * R * Math.asin(Math.sqrt(s));
}
const lenM = (c) => c.reduce((d, p, i) => i ? d + hav(c[i - 1], p) : 0, 0);
const bikeMin = (m) => Math.max(1, Math.round(m / 1000 / 15 * 60));
const fmtKm = (m) => (m / 1000).toFixed(1).replace('.', ',') + ' км';
// pre-ride arrival clock = now + ETA minutes (real wall clock)
const arrivalAt = (min) => { const d = new Date(Date.now() + min * 60000); return `${d.getHours()}:${String(d.getMinutes()).padStart(2, '0')}`; };
window.vrArrivalAt = arrivalAt;   // reused by vrEnrichRouteElevation to refresh arrival after DEM

// GSD task 4 — safety-aware routing. Score every candidate corridor against the seed safety
// layer, then assign roles from the pool: Fast = shortest, Safe = least hazard exposure,
// Scenic = longest. Safety fields (level/score/avoids/pattern) are all computed from real
// geometry-vs-hazard data so "Safe" genuinely avoids hazards that "Fast" takes.
function buildRoute(sources, destOverride, startOverride) {
  const score = window.scoreRoute || (() => ({ score: 85, level: 'safe', srcIds: new Set(), exposure: 0 }));
  const patternOf = window.routePattern || (() => [{ frac: 1, level: 'safe' }]);

  const cands = sources.map((s) => ({ coords: s.coords, steps: s.steps, m: lenM(s.coords), ...score(s.coords) }));
  const byLen = [...cands].sort((a, b) => a.m - b.m);
  const byScore = [...cands].sort((a, b) => b.score - a.score || a.m - b.m);

  const fast = byLen[0];
  let safe = byScore[0];
  if (safe === fast && byScore.length > 1) safe = byScore[1];   // keep Fast=shortest distinct from Safe
  let scenic = [...byLen].reverse().find((c) => c !== fast && c !== safe) || byLen[byLen.length - 1];
  const assign = { fast, safe, scenic };

  // hazards the Fast corridor passes that another corridor skips
  const avoidedVsFast = (c) => [...fast.srcIds].filter((id) => !c.srcIds.has(id)).length;

  const build = (role) => {
    const src = assign[role]; const m = src.m;
    const pattern = patternOf(src.coords);
    const traffic = src.score >= 78 ? 'нисък' : src.score >= 55 ? 'среден' : 'висок';
    const avoids = role === 'fast' ? src.srcIds.size : avoidedVsFast(src);
    // REAL "% велоалеи" — share of the route actually on the VELOSOFIZE bike-lane network
    // (separated + shared lanes), from the same classification that colors the line. Not a proxy.
    const catP = window.routeCatPattern ? window.routeCatPattern(src.coords) : null;
    const veloPct = catP
      ? Math.round(catP.filter((p) => p.cat === 'bikelane' || p.cat === 'bikelane_shared').reduce((a, p) => a + p.frac, 0) * 100)
      : Math.round(pattern.filter((p) => p.level === 'safe').reduce((a, p) => a + p.frac, 0) * 100);
    // ETA from the graded timing model (flat ground until vrEnrichRouteElevation folds in the DEM).
    const timing = window.vrRouteTiming ? window.vrRouteTiming(src.coords, src.steps, null) : null;
    const etaMin = timing ? Math.max(1, Math.round(timing.totalSec / 60)) : bikeMin(m);
    return { ...ROUTE_META[role], coords: src.coords, steps: src.steps,
      level: src.level, score: src.score, pattern, avoids, lanes: veloPct + '%', traffic,
      catPattern: catP,
      distM: m, dist: fmtKm(m), time: String(etaMin), etaMin,
      arrival: arrivalAt(etaMin), elev: null,
      cumSec: timing ? timing.cumSec : null, totalSec: timing ? timing.totalSec : 0 };
  };

  // display order matches the design: Safe, Fast, Scenic
  const routes = ['safe', 'fast', 'scenic'].map(build);
  return { routes, start: startOverride || START, dest: destOverride || DEST };
}

const REAL_ROUTE = buildRoute(window.VR_SRC);
window.REAL_ROUTE = REAL_ROUTE;
window.buildRoute = buildRoute;   // reused by the nav engine when it reroutes mid-ride

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const theme = THEME_MAP[t.theme] || 'light';
  const detail = DETAIL_MAP[t.detail] || 'standard';
  const dark = theme !== 'light';
  const lang = (t.lang === 'EN') ? 'en' : 'bg';
  const toggleLang = () => setTweak('lang', lang === 'en' ? 'БГ' : 'EN');
  const setLang = (code) => setTweak('lang', code === 'en' ? 'EN' : 'БГ');
  const setTheme = (v) => setTweak('theme', v);

  const [screen, setScreen] = useState(() => {
    // With a backend configured, land on the Auth screen unless a saved session exists.
    // Supabase persists the session under our storageKey 'vr-auth'.
    try {
      const backend = window.VR_DB && window.VR_DB.enabled;
      const hasSession = !!localStorage.getItem('vr-auth');
      if (backend && !hasSession) return 'auth';
    } catch (e) { /* localStorage blocked → fall through */ }
    // Only restore tab-level screens — never reopen a stuck modal/sub-screen over the map.
    const saved = localStorage.getItem('vr_screen');
    return ['home', 'routes', 'community', 'profile'].includes(saved) ? saved : 'home';
  });
  // GSD task 2/3 — REAL routing for the user's OWN endpoints. Nothing is pre-filled:
  // the user picks a start (current location or a searched place) and a destination,
  // then live street-following OSRM bike geometry (real distance + ETA) is computed.
  // No hardcoded origin — the route stays null until both endpoints are chosen.
  const [route, setRoute] = useState(null);
  const [start, setStart] = useState(null);
  const [startInfo, setStartInfo] = useState(null);
  const [dest, setDest] = useState(null);
  const [destInfo, setDestInfo] = useState(null);
  // VELOSOFIZE imported layer (loaded async). calmReady flips once its bike-lane network
  // has been fed into the safety engine, so routing recomputes against the richer data.
  const [kmz, setKmz] = useState(null);
  const [calmReady, setCalmReady] = useState(false);
  useEffect(() => {
    let alive = true;
    if (window.loadVelosofize) {
      window.loadVelosofize().then((data) => {
        if (!alive) return;
        if (window.vrIngestKMZCalm) window.vrIngestKMZCalm(data.routes);
        setKmz(data);
        setCalmReady(true);
      });
    }
    return () => { alive = false; };
  }, []);
  useEffect(() => {
    if (!start || !dest || !window.fetchLiveRoutes) return;   // need both endpoints first
    let alive = true;
    window.fetchLiveRoutes(start, dest).then((sources) => {
      if (!alive || !sources) return;
      const r = buildRoute(sources, dest, start);
      setRoute(r);
      // fold in real denivelation → updates ETA + elevation gain (no-op on localhost)
      if (window.vrEnrichRouteElevation) window.vrEnrichRouteElevation(r).then((r2) => { if (alive) setRoute({ ...r2 }); });
    });
    return () => { alive = false; };
  }, [start, dest, calmReady]);
  const [selected, setSelected] = useState('safe');
  const community = true; // crowdsourced layer is always on
  const [legendOpen, setLegendOpen] = useState(false);   // legend/filters collapsed by default
  // Default map: only bike lanes + crossings (VR_DEFAULT_FILTER); the rest off but toggleable.
  // User toggles persist across sessions.
  const [commFilter, setCommFilter] = useState(() => {
    try { const saved = localStorage.getItem('vr_commfilter'); if (saved) return JSON.parse(saved); } catch (e) {}
    return window.VR_DEFAULT_FILTER ? { ...window.VR_DEFAULT_FILTER } : {};
  });
  useEffect(() => { try { localStorage.setItem('vr_commfilter', JSON.stringify(commFilter)); } catch (e) {} }, [commFilter]);
  const [pickedComm, setPickedComm] = useState(null);

  // ── live backend (Phase 1.5) ──────────────────────────────────────────────
  // Auth session + crowdsourced reports come from Supabase (window.VR_DB). When no backend is
  // configured, VR_DB.enabled is false and everything below no-ops onto the seed data.
  const [user, setUser] = useState(null);
  const [commVersion, setCommVersion] = useState(0);   // bumps when live reports change → map re-renders
  // keep both local state and the shared UserStore (read by Profile + the search avatar) in sync
  const applyUser = (u) => {
    setUser(u); if (window.UserStore) window.UserStore.set(u);
    if (u && window.vrSyncFavorites) window.vrSyncFavorites(); // pull cross-device saved places/routes
  };
  useEffect(() => {
    if (!window.VR_DB || !window.VR_DB.enabled) return;
    window.VR_DB.currentUser().then((u) => {
      // one-time cleanup: guest mode is gone — sign out any leftover anonymous session and gate.
      if (u && u.is_anonymous) { window.VR_DB.signOut().finally(() => { applyUser(null); setScreen('auth'); }); return; }
      applyUser(u);
    });
    const offAuth = window.VR_DB.onAuth(applyUser);
    // password-recovery deep link → take the user straight to "set a new password"
    const offEvt = window.VR_DB.onAuthEvent && window.VR_DB.onAuthEvent((evt) => { if (evt === 'PASSWORD_RECOVERY') setScreen('reset'); });
    const offComm = window.vrOnCommunityChange(() => setCommVersion((v) => v + 1));
    window.vrHydrateCommunity();
    return () => { offAuth && offAuth(); offEvt && offEvt(); offComm && offComm(); };
  }, []);
  // re-run safety-aware routing whenever the community layer changes (a confirmed report may
  // now steer the route), as long as both endpoints are set.
  useEffect(() => {
    if (!start || !dest || !window.fetchLiveRoutes || commVersion === 0) return;
    let alive = true;
    window.fetchLiveRoutes(start, dest).then((s) => {
      if (!alive || !s) return;
      const r = buildRoute(s, dest, start);
      setRoute(r);
      if (window.vrEnrichRouteElevation) window.vrEnrichRouteElevation(r).then((r2) => { if (alive) setRoute({ ...r2 }); });
    });
    return () => { alive = false; };
  }, [commVersion]);
  const toggleCat = (k) => setCommFilter((f) => ({ ...f, [k]: f[k] === false }));
  const setAllCats = (show) => { const C = window.VR_COMMUNITY; const next = {}; Object.keys(C.cats).forEach((k) => { next[k] = show; }); setCommFilter(next); };
  // close any open pin if its category gets filtered out, or when leaving home
  useEffect(() => { if (pickedComm && commFilter[pickedComm.cat] === false) setPickedComm(null); }, [commFilter, pickedComm]);
  useEffect(() => { if (screen !== 'home') setPickedComm(null); }, [screen]);
  const [contribFrom, setContribFrom] = useState('community');
  const [contribType, setContribType] = useState('safe');
  const [pickMode, setPickMode] = useState(false);     // contribute → "select on map" mode
  const openContribute = (type = 'safe') => { setContribFrom(screen); setContribType(type); setScreen('contribute'); };
  useEffect(() => { localStorage.setItem('vr_screen', screen); }, [screen]);
  useEffect(() => { if (screen !== 'contribute') setPickMode(false); }, [screen]); // never leave pick mode armed

  // GSD task 3 — searched/located endpoints drive live routing.
  const setOrigin = (at, name) => { setStart(at); setStartInfo({ name: name || 'Начална точка' }); };
  const setDestination = (at, name, sub) => {
    setDest(at); setDestInfo({ name: name || 'Дестинация', sub: sub || '' }); setSelected('safe');
    if (window.Recents && at) window.Recents.add({ name: name || 'Дестинация', sub: sub || '', at, ts: Date.now() });
  };
  // locate-me on the home map: show the "you are here" dot and fly to it
  const onLocate = () => window.getMyLocation()
    .then((at) => { if (window.__vrShowUserLocation) window.__vrShowUserLocation(at[0], at[1], true); })
    .catch(() => { alert('Локацията не е достъпна. Разрешете достъп до местоположението в браузъра.'); if (window.__vrMapRecenter) window.__vrMapRecenter(); });
  // quick-card / saved place → set the destination, then send the user to pick a start first.
  const planTo = (at, name, sub) => { setDestination(at, name, sub); go('search'); };
  // "Покажи маршрутите" — NEVER route without a start ("От"). Default it to current location;
  // if that's unavailable, prompt for a start instead of silently hanging on the loader.
  const showRoutes = () => {
    if (start) { go('route'); return; }
    window.getMyLocation()
      .then((at) => { setOrigin(at, 'Моето местоположение'); go('route'); })
      .catch(() => alert('Изберете начална точка („От“). Разрешете достъп до местоположението или въведете адрес.'));
  };
  // Directions to a tapped map marker / feature → set THAT as the destination and show routes from
  // the current location (so it doesn't reuse the previously-searched destination).
  const directionsTo = (at, name, sub) => {
    if (!at) return;
    setDestination(at, name, sub);
    setPickedComm(null);
    showRoutes();
  };

  // language: translate the device subtree to EN on every render when active
  const contentRef = React.useRef(null);
  React.useLayoutEffect(() => {
    if (lang === 'en') window.translateTo(contentRef.current, window.I18N_EN);
  });

  const voicePref = window.useVoicePref ? window.useVoicePref() : true;
  const go = (s) => setScreen(s);
  const back = () => setScreen(screen === 'nav' ? 'route' : 'home');
  // end a navigation session — record the ACTUAL ride as a trip (on arrival or "Край"), then home.
  const endNav = () => {
    const r = route ? (route.routes.find((x) => x.id === selected) || route.routes[0]) : null;
    if (r && window.Trips) window.Trips.add({
      from: (startInfo && startInfo.name) || 'Моето местоположение', to: (destInfo && destInfo.name) || 'Дестинация',
      fromAt: start || null, toAt: dest || null, distM: r.distM, dist: r.dist, dur: r.time + ' мин',
      level: r.level, score: r.score, ts: Date.now(),
    });
    setScreen('home');
  };

  // fill-the-viewport scaling (robust against 0-size mount). The 402×874 design canvas is
  // scaled (uniform) to fill the screen — no margin, may upscale past 1 on larger devices.
  // iPhone aspect ≈ 402:874, so this fills cleanly without distorting the layout.
  const [scale, setScale] = useState(1);
  useEffect(() => {
    const fit = () => {
      const vw = window.innerWidth, vh = window.innerHeight;
      if (vw < 60 || vh < 60) return;
      setScale(Math.max(0.25, Math.min(vh / 874, vw / 402)));
    };
    fit(); requestAnimationFrame(fit);
    const timers = [setTimeout(fit, 120), setTimeout(fit, 500)];
    window.addEventListener('resize', fit);
    let ro; if (window.ResizeObserver) { ro = new ResizeObserver(fit); ro.observe(document.documentElement); }
    return () => { window.removeEventListener('resize', fit); timers.forEach(clearTimeout); if (ro) ro.disconnect(); };
  }, []);

  const sc = SCREEN_MAP[screen] || SCREEN_MAP.home;
  const fontStack = `'${t.font}'`;
  const sel = route ? (route.routes.find((r) => r.id === selected) || route.routes[0]) : null;

  return (
    <React.Fragment>
      <div className="vr" data-theme={theme}
        style={{ '--brand': t.accent, '--font': fontStack, zoom: scale }}>
        <IOSDevice dark={dark} bare>
          <div key={lang} ref={contentRef} style={{ position: 'absolute', inset: 0, overflow: 'hidden', background: 'var(--land)' }}>
            <div style={{ position: 'absolute', inset: 0, zIndex: 0 }}>
              <RealMap theme={theme} detail={detail} view={sc.view} routeMode={sc.route} route={route} selected={selected} community={community} commFilter={commFilter} commSelId={pickedComm ? pickedComm.id : null} onPickComm={setPickedComm} kmzData={kmz} commVersion={commVersion}
                pickPoint={screen === 'contribute' && pickMode}
                onPickPoint={(ll) => { window.__vrPickedPoint = { lat: ll.lat != null ? ll.lat : ll[0], lng: ll.lng != null ? ll.lng : ll[1] }; }} />
            </div>

            {screen === 'home' && <HomeScreen theme={theme} go={go} onPlanTo={planTo} onDirections={directionsTo} onLocate={onLocate} openReport={() => openContribute('point')} community={community} legendOpen={legendOpen} onToggleLegend={() => setLegendOpen((o) => !o)} commFilter={commFilter} onToggleCat={toggleCat} onSetAll={setAllCats} picked={pickedComm} onClosePick={() => setPickedComm(null)} kmz={kmz} />}
            {screen === 'route' && (route
              ? <RouteSelectScreen theme={theme} go={go} back={back} routes={route.routes} selected={selected} onSelect={setSelected} destInfo={destInfo} originName={startInfo && startInfo.name} originAt={start} destAt={dest} />
              : <RouteLoading theme={theme} back={back} destName={destInfo && destInfo.name} />)}
            {screen === 'nav' && sel && <ActiveNavScreen theme={theme} go={go} route={route} selected={selected} onRoute={setRoute} onEnd={endNav} voice={voicePref} openReport={() => openContribute('point')} />}
            {screen === 'search' && <SearchScreen theme={theme} back={back}
              origin={{ at: start, name: startInfo && startInfo.name }} dest={{ at: dest, name: destInfo && destInfo.name }}
              onSetOrigin={setOrigin} onSetDest={setDestination} onDone={showRoutes} />}
            {screen === 'routes' && <RoutesScreen theme={theme} go={go} onPlanRoute={(r) => { if (r.fromAt) setOrigin(r.fromAt, r.from); if (r.toAt) setDestination(r.toAt, r.to); go('route'); }} />}
            {screen === 'community' && <CommunityScreen theme={theme} go={go} openReport={() => openContribute('point')} selId={pickedComm ? pickedComm.id : null} />}
            {screen === 'profile' && <ProfileScreen theme={theme} go={go} lang={lang} onToggleLang={toggleLang} themeName={t.theme} setTheme={setTheme} />}
            {screen === 'onboarding' && <OnboardingScreen theme={theme} go={go} />}
            {screen === 'auth' && <AuthScreen theme={theme} go={go} onAuthed={applyUser} />}
            {screen === 'saved' && <SavedPlacesScreen theme={theme} go={go} />}
            {screen === 'language' && <LanguageScreen theme={theme} go={go} lang={lang} setLang={setLang} />}
            {screen === 'routeprefs' && <RoutePrefsScreen theme={theme} go={go} />}
            {screen === 'premium' && <PremiumScreen theme={theme} go={go} />}
            {screen === 'bike' && <BikeScreen theme={theme} go={go} />}
            {screen === 'notifications' && <NotificationsScreen theme={theme} go={go} />}
            {screen === 'contribute' && <ContributeScreen theme={theme} go={go} back={() => go(contribFrom)} initialType={contribType} user={user} onSubmitted={() => { window.vrHydrateCommunity && window.vrHydrateCommunity(); }} pickMode={pickMode} onEnterPick={() => setPickMode(true)} onExitPick={() => setPickMode(false)} />}
            {screen === 'reset' && <ResetPasswordScreen theme={theme} go={go} onAuthed={applyUser} />}
            {screen === 'changepass' && <ChangePasswordScreen theme={theme} go={go} />}
          </div>
        </IOSDevice>
      </div>

      <TweaksPanel title="Tweaks">
        <TweakSection label="Облик" />
        <TweakColor label="Акцент" value={t.accent}
          options={['#df6b46', '#2f76d8', '#1f9d57', '#7a5ae0']}
          onChange={(v) => setTweak('accent', v)} />
        <TweakRadio label="Тема" value={t.theme}
          options={['Светла', 'Тъмна', 'Каране']}
          onChange={(v) => setTweak('theme', v)} />
        <TweakSelect label="Шрифт" value={t.font}
          options={['Sofia Sans', 'Onest', 'Golos Text', 'Commissioner', 'Geologica']}
          onChange={(v) => setTweak('font', v)} />
        <TweakRadio label="Език / Language" value={t.lang || 'БГ'}
          options={['БГ', 'EN']}
          onChange={(v) => setTweak('lang', v)} />
        <TweakSection label="Карта" />
        <TweakSelect label="Детайлност" value={t.detail}
          options={['Минимална', 'Стандартна', 'Детайлна']}
          onChange={(v) => setTweak('detail', v)} />
        <TweakSection label="Поток" />
        <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
          {[['onboarding', 'Onboarding'], ['auth', 'Вход'], ['home', 'Начало'], ['search', 'Търсене'], ['route', 'Маршрути'], ['nav', 'Навигация'],
            ['community', 'Общност'], ['contribute', 'Принос'], ['saved', 'Запазени'], ['premium', 'Premium'], ['profile', 'Профил']].map(([s, l]) => (
            <TweakButton key={s} label={l} onClick={() => go(s)} />
          ))}
        </div>
      </TweaksPanel>
    </React.Fragment>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
