/* Uleez — App router + shell composition */
const { useState: useApp, useEffect: useAppE } = React;

// Titre et sous-titre de l'en-tête, par rôle et par page.
//
// Côté LOUEUR ce sont des CLÉS de traduction, résolues au rendu (voir `meta` plus bas) : cet
// objet est une constante de module, évaluée une seule fois au chargement, alors que la langue
// peut changer sans rechargement de page.
//
// Côté ADMIN les libellés restent en français : seul l'espace loueur est traduit dans cette
// version (cf. le commentaire de shell.jsx).
const PAGE_META = {
  lessor: {
    dashboard:       { title: 'page.dashboard.title', sub: 'page.dashboard.sub' },
    bookings:        { title: 'page.bookings.title', sub: 'page.bookings.sub' },
    'booking-detail':{ title: 'page.bookingDetail.title', sub: 'page.bookingDetail.sub' },
    calendar:        { title: 'page.calendar.title', sub: 'page.calendar.sub' },
    vehicles:        { title: 'page.vehicles.title', sub: 'page.vehicles.sub' },
    'vehicle-new':   { title: 'page.vehicleNew.title', sub: 'page.vehicleNew.sub' },
    'vehicle-edit':  { title: 'page.vehicleEdit.title', sub: 'page.vehicleEdit.sub' },
    payments:        { title: 'page.payments.title', sub: 'page.payments.sub' },
    subscription:    { title: 'page.subscription.title', sub: 'page.subscription.sub' },
    promotions:      { title: 'promo.title', sub: 'promo.sub' },
    profile:         { title: 'page.profile.title', sub: 'page.profile.sub' },
    setup:           { title: 'page.setup.title', sub: 'page.setup.sub' },
  },
  admin: {
    dashboard: { title: 'Tableau de bord', sub: 'Pilotage de la plateforme' },
    lessors:   { title: 'Loueurs', sub: 'Validation et gestion des partenaires' },
    clients:   { title: 'Clients', sub: 'Gestion des utilisateurs de l\'application mobile' },
    vehicles:  { title: 'Véhicules', sub: 'Modération des annonces' },
    bookings:  { title: 'Réservations', sub: 'Suivi global des locations' },
    activity:  { title: 'Activité globale', sub: 'Vue financière par loueur et par période' },
    billing:   { title: 'Facturation loueurs', sub: 'Abonnements, commissions et créances' },
    promos:    { title: 'Codes promo', sub: 'Campagnes de réduction appliquées au paiement' },
    applications: { title: 'Candidatures loueur', sub: 'Demandes reçues depuis la page publique' },
    bugs: { title: 'Signalements', sub: "Problèmes remontés depuis l'application mobile" },
    'vehicle-promos': { title: 'Promotions', sub: 'Prix réduits proposés par les loueurs, à valider' },
    support:   { title: 'Support', sub: 'Tickets et assistance' },
    settings:  { title: 'Paramètres', sub: 'Configuration de la plateforme' },
  },
  // Restricted role: sees ONLY the support desk. Enforced server-side too.
  support_agent: {
    support:   { title: 'Support', sub: 'Tickets et assistance' },
    dashboard: { title: 'Support', sub: 'Tickets et assistance' },
    bugs:      { title: 'Signalements', sub: "Problèmes remontés depuis l'application mobile" },
  },
};

// Whitelist of known pages — the URL hash is validated against this (no arbitrary injection).
const VALID_PAGES = new Set([
  'dashboard', 'bookings', 'booking-detail', 'calendar', 'vehicles', 'vehicle-new',
  'vehicle-edit', 'payments', 'profile', 'setup',
  'lessors', 'clients', 'activity', 'billing', 'support', 'settings', 'promos',
  'subscription', 'applications', 'bugs', 'promotions', 'vehicle-promos',
]);

function App() {
  // Abonnement aux changements de langue : sans `useT`, l'en-tête garderait l'ancienne langue
  // jusqu'au prochain rendu déclenché par autre chose.
  const tApp = window.I18N ? I18N.useT() : ((k) => k);
  const skipHash = React.useRef(false);
  const [authed, setAuthed] = useApp(false);
  const [authLoading, setAuthLoading] = useApp(true); // true during session restore
  const [role, setRole] = useApp('lessor');
  const [currentUser, setCurrentUser] = useApp(null);
  const [page, setPage] = useApp('dashboard');
  const [params, setParams] = useApp({});
  const [collapsed, setCollapsed] = useApp(false);
  const [mobileOpen, setMobileOpen] = useApp(false);
  // Set when the URL carries a password-reset link (#reset~<token>). Logged-out route.
  const [resetToken, setResetToken] = useApp(null);

  // Register global logout handler for 401 interceptor
  useAppE(() => {
    window.__kd_logout = () => {
      setAuthed(false);
      setCurrentUser(null);
      API.clearSession();
      // Drop the encryption key from memory AND from IndexedDB: on a shared computer the
      // next person to sign in must not inherit the ability to read these conversations.
      if (window.E2E) E2E.forget().catch(() => {});
    };
    return () => { delete window.__kd_logout; };
  }, []);

  // Restore session on mount
  useAppE(() => {
    const token = API.getToken();
    if (!token) { setAuthLoading(false); return; }

    API.auth.me()
      .then((me) => {
        if (['admin', 'lessor', 'support_agent'].includes(me.role)) {
          // La langue du COMPTE fait autorité : elle suit l'utilisateur d'un poste à l'autre,
          // là où la valeur de localStorage n'est qu'un cache pour éviter un affichage en
          // français le temps que /auth/me réponde.
          if (window.I18N) I18N.adoptFromUser(me);
          setCurrentUser(me);
          setRole(me.role);
          setAuthed(true);
          // Remettre le chiffrement en service AVANT d'ouvrir le canal temps réel : sans
          // cela, un rafraîchissement de page laissait la clé privée en mémoire vide, et
          // tout message chiffré — reçu comme envoyé — restait illisible jusqu'à une
          // véritable reconnexion. La clé vient d'IndexedDB, aucun mot de passe n'est requis.
          if (window.E2E) {
            E2E.resume(me.id)
              .then((ok) => { if (!ok) console.warn('[e2e] clé locale absente — reconnexion requise pour déchiffrer'); })
              .catch(() => {})
              .finally(() => API.Realtime.connect());
          } else {
            API.Realtime.connect();
          }
        } else {
          API.clearSession();
        }
      })
      .catch(() => API.clearToken())
      .finally(() => setAuthLoading(false));
  }, []);

  useAppE(() => { document.body.style.overflow = mobileOpen ? 'hidden' : ''; }, [mobileOpen]);

  // Hash-based routing: restore page+params on load, follow back/forward — reload no longer
  // dumps you on the dashboard. The page is whitelisted; params are the route context.
  useAppE(() => {
    const applyHash = () => {
      const raw = (window.location.hash || '').replace(/^#/, '');
      if (!raw) { setResetToken(null); return; }
      const sep = raw.indexOf('~');
      const pg = sep === -1 ? raw : raw.slice(0, sep);
      const rest = sep === -1 ? '' : raw.slice(sep + 1);
      // Password-reset link from the email. Not a regular page: the payload is a raw token,
      // not the JSON params the other routes carry, and it renders before the auth gate.
      // Shape-checked here so nothing but a hex digest ever reaches the API call.
      if (pg === 'reset') {
        const token = decodeURIComponent(rest || '');
        setResetToken(/^[a-f0-9]{32,128}$/i.test(token) ? token : '');
        return;
      }
      setResetToken(null);
      const nextPage = VALID_PAGES.has(pg) ? pg : 'dashboard';
      let prm = {};
      if (rest) { try { const d = JSON.parse(decodeURIComponent(rest)); if (d && typeof d === 'object') prm = d; } catch (_) {} }
      setPage(nextPage); setParams(prm);
    };
    applyHash();
    const onHash = () => { if (skipHash.current) { skipHash.current = false; return; } applyHash(); };
    window.addEventListener('hashchange', onHash);
    return () => window.removeEventListener('hashchange', onHash);
  }, []);

  const navigate = (p, pr = {}) => {
    setPage(p); setParams(pr); setMobileOpen(false); window.scrollTo(0, 0);
    const q = pr && Object.keys(pr).length ? '~' + encodeURIComponent(JSON.stringify(pr)) : '';
    const target = '#' + p + q;
    if (window.location.hash !== target) { skipHash.current = true; window.location.hash = target; }
  };

  // Met à jour les paramètres de l'écran COURANT sans en changer, et les écrit dans le hash.
  //
  // C'est ce qui rend l'ancre temporelle des écrans financiers partageable et résistante au
  // rechargement : `#dashboard~{"anchor":"2026-07"}` rouvre bien juillet.
  //
  // Distinct de `navigate` malgré la parenté : celui-ci ne remonte PAS en haut de page et ne
  // ferme pas le menu mobile. Changer de mois n'est pas changer d'écran — être renvoyé en
  // haut à chaque clic de flèche, alors qu'on regarde un graphique en bas de page, rendrait
  // la navigation temporelle pénible.
  const setPageParams = (pr = {}) => {
    setParams(pr);
    const q = pr && Object.keys(pr).length ? '~' + encodeURIComponent(JSON.stringify(pr)) : '';
    const target = '#' + page + q;
    if (window.location.hash !== target) { skipHash.current = true; window.location.hash = target; }
  };

  const switchRole = (r) => { setRole(r); setPage('dashboard'); setParams({}); };

  const handleLogin = (userRole, user) => {
    if (window.I18N) I18N.adoptFromUser(user);
    setCurrentUser(user);
    setRole(userRole);
    setAuthed(true);
    setPage(userRole === 'support_agent' ? 'support' : 'dashboard');
    API.Realtime.connect(); // open the realtime channel for chat + notifications
  };

  const handleLogout = () => {
    API.auth.logout().catch(() => {}); // best-effort server-side token revocation
    API.clearSession();
    // Same reasoning as the forced-logout path above — see __kd_logout.
    if (window.E2E) E2E.forget().catch(() => {});
    setAuthed(false);
    setCurrentUser(null);
    setPage('dashboard');
  };

  if (authLoading) {
    return React.createElement('div', {
      style: {
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        minHeight: '100vh', background: 'var(--bg)',
      }
    },
      React.createElement('div', { style: { textAlign: 'center' } },
        React.createElement('img', { src: 'icons/Icon-192.png', className: 'brand-mark', style: { width: 48, height: 48, margin: '0 auto 14px', objectFit: 'cover', background: 'transparent', display: 'block' } }),
        React.createElement('div', { style: { fontSize: 13, color: 'var(--muted)' } }, 'Chargement…')
      )
    );
  }

  // Password reset comes before the auth gate: the link is opened from an email, by
  // definition without a session (and it must work even if one is somehow still open).
  if (resetToken !== null) {
    const leaveReset = () => {
      setResetToken(null);
      skipHash.current = true;
      window.location.hash = '';
    };
    return React.createElement(ResetPassword, { token: resetToken, onDone: leaveReset });
  }

  if (!authed) {
    return React.createElement(Login, { onLogin: handleLogin });
  }

  const metaKey = page;
  const rawMeta = (PAGE_META[role]?.[metaKey]) || { title: '', sub: '' };
  // Les entrées LOUEUR portent des clés, les entrées ADMIN du texte français. `t()` retombe
  // sur la clé elle-même quand elle est inconnue du dictionnaire : passer les deux dedans est
  // donc sans effet sur l'admin, et évite un aiguillage sur le rôle.
  const meta = { title: tApp(rawMeta.title), sub: tApp(rawMeta.sub) };

  const render = () => {
    // Un agent support reste restreint par DÉFAUT : tout ce qui n'est pas explicitement
    // listé ici retombe sur le bureau d'assistance, quoi que dise le hash. Les signalements de
    // bug s'y ajoutent en 0.9.10 — c'est souvent l'agent support qui reçoit la plainte, et le
    // backend l'y autorise déjà (`@Roles('admin','support_agent')`). Ajouter une page ici est
    // une décision d'ACCÈS : elle doit rester explicite, page par page.
    if (role === 'support_agent') {
      if (page === 'bugs') return React.createElement(AdminBugReports, null);
      return React.createElement(AdminSupport, null);
    }
    if (role === 'lessor') {
      switch (page) {
        case 'dashboard':    return React.createElement(LessorDashboard, { navigate, currentUser });
        case 'bookings':     return React.createElement(LessorBookings, { navigate });
        case 'booking-detail': return React.createElement(BookingDetail, { params, navigate });
        case 'calendar':     return React.createElement(LessorCalendar, { currentUser });
        case 'vehicles':     return React.createElement(LessorVehicles, { navigate });
        case 'vehicle-new':  return React.createElement(VehicleForm, { navigate });
        case 'vehicle-edit': return React.createElement(VehicleForm, { params, navigate });
        case 'payments':     return React.createElement(LessorPayments, null);
        case 'subscription': return React.createElement(LessorBilling, { navigate });
        case 'profile':      return React.createElement(LessorProfile, { currentUser });
        case 'promotions':   return React.createElement(LessorPromotions, null);
        case 'setup':        return React.createElement(LessorSetup, { navigate });
        default:             return React.createElement(LessorDashboard, { navigate, currentUser });
      }
    } else {
      switch (page) {
        case 'dashboard': return React.createElement(AdminDashboard, { navigate, params, setParams: setPageParams });
        case 'lessors':   return React.createElement(AdminLessors, { navigate });
        case 'clients':   return React.createElement(AdminCustomers, { params });
        case 'vehicles':  return React.createElement(AdminVehicles, null);
        case 'bookings':  return React.createElement(AdminBookings, null);
        case 'activity':  return React.createElement(AdminActivity, { params, setParams: setPageParams });
        case 'billing':   return React.createElement(AdminBilling, { navigate, params, setParams: setPageParams });
        case 'promos':    return React.createElement(AdminPromoCodes, null);
        case 'applications': return React.createElement(AdminApplications, null);
        case 'bugs': return React.createElement(AdminBugReports, null);
        case 'vehicle-promos': return React.createElement(AdminVehiclePromotions, null);
        case 'support':   return React.createElement(AdminSupport, null);
        case 'settings':  return React.createElement(AdminSettings, null);
        default:          return React.createElement(AdminDashboard, { navigate, params, setParams: setPageParams });
      }
    }
  };

  return (
    <div className="app">
      <div className={`backdrop ${mobileOpen ? 'show' : ''}`} onClick={() => setMobileOpen(false)} />
      <Sidebar
        role={role}
        page={page === 'booking-detail' ? 'bookings' : page.startsWith('vehicle-') ? 'vehicles' : page}
        onNavigate={navigate}
        collapsed={collapsed}
        open={mobileOpen}
        onLogout={handleLogout}
        currentUser={currentUser}
      />
      <div className="main">
        <Header
          title={meta.title}
          sub={meta.sub}
          role={role}
          collapsed={collapsed}
          onCollapse={() => setCollapsed(c => !c)}
          onMenu={() => setMobileOpen(true)}
          onRoleSwitch={switchRole}
          onLogout={handleLogout}
          onNavigate={navigate}
          currentUser={currentUser}
        />
        <div className="page">{render()}</div>
      </div>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(
  React.createElement(ToastProvider, null, React.createElement(App))
);
