// Route-mounted entry point for `/settings`. Three responsibilities:
//
//   1. Pick the surface chrome based on screen size — Settings itself
//      provides its own internal PageRoot (settings menu + sub-screen).
//      On Desktop we render it inline inside the right pane of the
//      outer DIRECT_PATH PageRoot (the route element in Router.tsx) so
//      the DM list stays visible on the left and the horseshoe rounded
//      TL/BL on the right pane is inherited. On Mobile the route is a
//      deep-link entry only — we set the `settingsSheetAtom` and
//      redirect to /direct/, where `MobileSettingsHorseshoe` wraps the
//      DM list and renders the bottom-up sheet over it (so the user
//      sees the )|( silhouette: DM list above, void, sheet below).
//
//   2. Translate URL state into Settings props — `?page=devices` deep-
//      links into the Devices sub-screen (used by the UnverifiedTab
//      shield-icon shortcut and future deep links — push notifications,
//      external onboarding emails). Stable string keys live in
//      `SETTINGS_PAGE_PARAM` (Settings.tsx). Unknown values fall through
//      to the default landing (mobile menu / desktop GeneralPage).
//
//   3. Define close behaviour — Esc closes via a plain `keydown`
//      listener (we explicitly do NOT use focus-trap-react here: the
//      desktop Settings surface is non-modal — clicks on the DM list
//      should pass through to navigation, not deactivate a trap — and
//      focus-trap-react's `onDeactivate` fires on Strict-Mode unmount,
//      which would call `navigate(-1)` at the wrong moment).
//      Cold-load entries (no in-app history) fall through to /direct/
//      so the close action is never a dead-end.

import React, { useCallback, useEffect } from 'react';
import { useLocation, useNavigate, useSearchParams } from 'react-router-dom';
import { ScreenSize, useScreenSizeContext } from '../../hooks/useScreenSize';
import { getDirectPath } from '../../pages/pathUtils';
import { useOpenSettingsSheet } from '../../state/hooks/settingsSheet';
import { Settings, SETTINGS_PAGE_PARAM, SettingsPages } from './Settings';

const hasPageParam = (key: string): key is keyof typeof SETTINGS_PAGE_PARAM =>
  Object.prototype.hasOwnProperty.call(SETTINGS_PAGE_PARAM, key);

// `location.state` payload set by every in-Vojo navigation to /settings
// (SettingsTab, DirectSelfRow, UnverifiedTab). When present, we know
// the previous history entry is an in-app page and `navigate(-1)` is
// safe. When missing — cold-start, deep-link from a push, an external
// bookmark — we explicitly route to `/direct/` instead of risking a
// cross-origin back navigation. Keep this constant in sync with the
// entry points.
export const SETTINGS_FROM_IN_APP_STATE = { vojoFrom: 'in-app' as const };

export function SettingsScreen() {
  const screenSize = useScreenSizeContext();
  const navigate = useNavigate();
  const location = useLocation();
  const [searchParams] = useSearchParams();
  const openSheet = useOpenSettingsSheet();

  const pageParam = searchParams.get('page');
  const initialPage: SettingsPages | undefined =
    pageParam && hasPageParam(pageParam) ? SETTINGS_PAGE_PARAM[pageParam] : undefined;

  // `window.history.length` does NOT distinguish in-SPA navigations
  // from cross-origin entries (the user typing `vojo.chat/settings`
  // in the address bar). Relying on it was a load-bearing coincidence;
  // a determined back-fallback could take the user out of Vojo
  // entirely. Instead, every in-app entry to `/settings` passes
  // `state: SETTINGS_FROM_IN_APP_STATE` via the router (see
  // `SettingsTab.tsx`, `DirectSelfRow.tsx`, `UnverifiedTab.tsx`).
  // We read that state here as the safe-`-1` signal; without it we
  // route explicitly to `/direct/`.
  const fromInApp =
    (location.state as { vojoFrom?: string } | null | undefined)?.vojoFrom === 'in-app';
  const handleClose = useCallback(() => {
    if (fromInApp) {
      navigate(-1);
    } else {
      navigate(getDirectPath(), { replace: true });
    }
  }, [navigate, fromInApp]);

  // Mobile is a deep-link redirect — set the atom (so the bottom-up
  // sheet opens with the right initial page) then bounce to /direct/
  // where `MobileSettingsHorseshoe` is mounted. `replace: true` so the
  // /settings entry doesn't sit in the back-stack (otherwise pressing
  // back from /direct/ would bounce back through /settings → loop).
  const isMobile = screenSize === ScreenSize.Mobile;
  useEffect(() => {
    if (!isMobile) return;
    openSheet(initialPage);
    navigate(getDirectPath(), { replace: true });
  }, [isMobile, initialPage, openSheet, navigate]);

  // Esc on desktop — explicit keydown listener instead of FocusTrap's
  // `escapeDeactivates + onDeactivate`. focus-trap-react's
  // `onDeactivate` fires on Strict-Mode mount-cycle unmount and on
  // outside clicks, both of which would erroneously trigger close
  // here. A plain keydown is exact: Esc → handleClose, nothing else.
  useEffect(() => {
    if (isMobile) return undefined;
    const onKeyDown = (e: KeyboardEvent) => {
      if (e.key !== 'Escape') return;
      // Don't intercept Esc when the user is in a text field — they
      // expect Esc to clear input, not close the page.
      const target = e.target as HTMLElement | null;
      if (
        target &&
        (target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable)
      ) {
        return;
      }
      handleClose();
    };
    window.addEventListener('keydown', onKeyDown);
    return () => window.removeEventListener('keydown', onKeyDown);
  }, [isMobile, handleClose]);

  if (isMobile) {
    // Render nothing — the useEffect above redirects on mount. A flash
    // of empty content is acceptable; the redirect runs synchronously
    // in a microtask after mount.
    return null;
  }

  return (
    <div style={{ display: 'flex', flex: 1, minWidth: 0, minHeight: 0 }}>
      <Settings initialPage={initialPage} requestClose={handleClose} />
    </div>
  );
}
