import React, {
  ChangeEventHandler,
  FormEventHandler,
  KeyboardEventHandler,
  MouseEventHandler,
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from 'react';
import dayjs from 'dayjs';
import {
  as,
  Box,
  Button,
  Chip,
  color,
  config,
  Header,
  Icon,
  IconButton,
  Icons,
  Input,
  Menu,
  MenuItem,
  PopOut,
  RectCords,
  Scroll,
  Spinner,
  Switch,
  Text,
  toRem,
} from 'folds';
import { isKeyHotkey } from 'is-hotkey';
import { HexColorPicker } from 'react-colorful';
import FocusTrap from 'focus-trap-react';
import {
  draggable,
  dropTargetForElements,
  monitorForElements,
} from '@atlaskit/pragmatic-drag-and-drop/element/adapter';
import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
import { reorder } from '@atlaskit/pragmatic-drag-and-drop/reorder';
import {
  attachClosestEdge,
  extractClosestEdge,
  Edge,
} from '@atlaskit/pragmatic-drag-and-drop-hitbox/closest-edge';
import { useAtom } from 'jotai';
import { HexColorPickerPopOut } from '../../../components/HexColorPickerPopOut';
import { BgSwatch as BgSwatchStyle } from './BgSwatch.css';
import { Page, PageContent, PageHeader } from '../../../components/page';
import { SequenceCard } from '../../../components/sequence-card';
import {
  DENOISE_MODELS,
  isMLDenoiseSupported,
  ML_DENOISE_REQUIREMENTS,
} from '../../../utils/lotusDenoiseUtils';
import { useSetting } from '../../../state/hooks/settings';
import {
  CallAudioBitrate,
  ChatBackground,
  ComposerToolbarButtonKey,
  ComposerToolbarSettings,
  DateFormat,
  DenoiseModelId,
  MessageLayout,
  MessageSpacing,
  NoiseSuppressionMode,
  normalizeComposerToolbarOrder,
  RingtoneId,
  ScreenshareBitrate,
  ScreenshareFramerate,
  Settings,
  settingsAtom,
} from '../../../state/settings';
import {
  AUDIO_BITRATE_OPTIONS,
  SCREENSHARE_BITRATE_OPTIONS,
  SCREENSHARE_FRAMERATE_OPTIONS,
} from '../../../utils/callQuality';
import { SeasonalPreview, SeasonTheme } from '../../../components/seasonal/SeasonalEffect';
import { SEASON_DATE_RANGES } from '../../../components/seasonal/seasonSchedule';
import { SettingTile } from '../../../components/setting-tile';
import { KeySymbol } from '../../../utils/key-symbol';
import { isMacOS } from '../../../utils/user-agent';
import {
  DarkTheme,
  LightTheme,
  Theme,
  ThemeKind,
  useSystemThemeKind,
  useTheme,
  useThemeNames,
  useThemes,
} from '../../../hooks/useTheme';
import { stopPropagation } from '../../../utils/keyboard';
import { pickerInputStyle } from '../../../utils/datetimeInput';
import { BG_OPTIONS, getChatBg } from '../../lotus/chatBackground';
import { resetBootSequence, runLotusBootSequence } from '../../../../lotus-boot';
import { useMessageLayoutItems } from '../../../hooks/useMessageLayout';
import { useMessageSpacingItems } from '../../../hooks/useMessageSpacing';
import {
  TRANSLATE_TARGET_LANGUAGES,
  normalizeLang,
  isSupportedTargetLang,
} from '../../../utils/translation/langUtils';
import { chromeTranslationEngine } from '../../../utils/translation/chromeEngine';
import { SequenceCardStyle } from '../styles.css';
import { useTauriUpdater } from '../../../hooks/useTauriUpdater';
import { isTauri as isTauriEnv, invokeTauri, tauriInvoke } from '../../../hooks/useTauri';
import { customWindowChromeAtom } from '../../../state/customWindowChrome';
import { useDateFormatItems } from '../../../hooks/useDateFormat';
import { useReducedMotion } from '../../../hooks/useReducedMotion';
import { playCallJoinSound } from '../../../utils/callSounds';
import { previewRingtone, RINGTONE_OPTIONS } from '../../../utils/ringtones';
import { DenoiseTester } from './DenoiseTester';
import { SettingsSelect } from '../../../components/settings-select/SettingsSelect';

/**
 * P5-47 — opt-in TDS window chrome toggle (desktop only). Renders nothing in the
 * browser. Backed by the standalone `customWindowChromeAtom`; `useTauriWindowChrome`
 * (mounted in App.tsx) applies `set_decorations` when this flips.
 */
function DesktopChromeSetting() {
  const [customChrome, setCustomChrome] = useAtom(customWindowChromeAtom);
  if (!isTauriEnv()) return null;
  // Persist the flag, then reload so the window layout is rebuilt from scratch.
  // Toggling live reflows the whole app while the room timeline is mounted, which
  // resizes its virtualized scroll container and triggers runaway back-pagination
  // (the "screen expands + auto-scrolls into the past" bug). A reload applies the
  // chrome cleanly against a fresh, correct layout.
  const handleToggle = (value: boolean) => {
    setCustomChrome(value);
    window.location.reload();
  };
  return (
    <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
      <SettingTile
        title="Custom Window Chrome (Beta)"
        description="Replace the system title bar with a Lotus-styled one. Desktop only — reloads to apply."
        after={<Switch variant="Primary" value={customChrome} onChange={handleToggle} />}
      />
    </SequenceCard>
  );
}

/**
 * P6-1 — "Launch on login" toggle (desktop only). Renders nothing in the
 * browser. Reads the current state from the `autostart` plugin on mount and
 * enables/disables it via the plugin commands when flipped. Not backed by an
 * atom — the OS registration is the source of truth, mirrored into local state.
 */
function AutostartSetting() {
  const [enabled, setEnabled] = useState(false);

  useEffect(() => {
    tauriInvoke()?.('plugin:autostart|is_enabled')
      .then((value) => setEnabled(value === true))
      .catch(() => undefined);
  }, []);

  const handleChange = (value: boolean) => {
    invokeTauri(value ? 'plugin:autostart|enable' : 'plugin:autostart|disable');
    setEnabled(value);
  };

  if (!isTauriEnv()) return null;
  return (
    <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
      <SettingTile
        title="Launch on login"
        description="Start Lotus Chat automatically when you sign in to your computer."
        after={<Switch variant="Primary" value={enabled} onChange={handleChange} />}
      />
    </SequenceCard>
  );
}

type ThemeSelectorProps = {
  themeNames: Record<string, string>;
  themes: Theme[];
  selected: Theme;
  onSelect: (theme: Theme) => void;
};
const ThemeSelector = as<'div', ThemeSelectorProps>(
  ({ themeNames, themes, selected, onSelect, ...props }, ref) => (
    <Menu {...props} ref={ref}>
      <Box direction="Column" gap="100" style={{ padding: config.space.S100 }}>
        {themes.map((theme) => (
          <MenuItem
            key={theme.id}
            size="300"
            variant={theme.id === selected.id ? 'Primary' : 'Surface'}
            radii="300"
            onClick={() => onSelect(theme)}
          >
            <Text size="T300">{themeNames[theme.id] ?? theme.id}</Text>
          </MenuItem>
        ))}
      </Box>
    </Menu>
  ),
);

function SelectTheme({ disabled }: { disabled?: boolean }) {
  const themes = useThemes();
  const themeNames = useThemeNames();
  const [themeId, setThemeId] = useSetting(settingsAtom, 'themeId');
  const [menuCords, setMenuCords] = useState<RectCords>();
  const selectedTheme = themes.find((theme) => theme.id === themeId) ?? LightTheme;

  const handleThemeMenu: MouseEventHandler<HTMLButtonElement> = (evt) => {
    setMenuCords(evt.currentTarget.getBoundingClientRect());
  };

  const handleThemeSelect = (theme: Theme) => {
    setThemeId(theme.id);
    setMenuCords(undefined);
  };

  return (
    <>
      <Button
        size="300"
        variant="Secondary"
        outlined
        fill="Soft"
        radii="300"
        after={<Icon size="300" src={Icons.ChevronBottom} />}
        onClick={disabled ? undefined : handleThemeMenu}
        aria-disabled={disabled}
      >
        <Text size="T300">{themeNames[selectedTheme.id] ?? selectedTheme.id}</Text>
      </Button>
      <PopOut
        anchor={menuCords}
        offset={5}
        position="Bottom"
        align="End"
        content={
          <FocusTrap
            focusTrapOptions={{
              initialFocus: false,
              onDeactivate: () => setMenuCords(undefined),
              clickOutsideDeactivates: true,
              isKeyForward: (evt: KeyboardEvent) =>
                evt.key === 'ArrowDown' || evt.key === 'ArrowRight',
              isKeyBackward: (evt: KeyboardEvent) =>
                evt.key === 'ArrowUp' || evt.key === 'ArrowLeft',
              escapeDeactivates: stopPropagation,
            }}
          >
            <ThemeSelector
              themeNames={themeNames}
              themes={themes}
              selected={selectedTheme}
              onSelect={handleThemeSelect}
            />
          </FocusTrap>
        }
      />
    </>
  );
}

function SystemThemePreferences() {
  const themeKind = useSystemThemeKind();
  const themeNames = useThemeNames();
  const themes = useThemes();
  const [lightThemeId, setLightThemeId] = useSetting(settingsAtom, 'lightThemeId');
  const [darkThemeId, setDarkThemeId] = useSetting(settingsAtom, 'darkThemeId');

  const lightThemes = themes.filter((theme) => theme.kind === ThemeKind.Light);
  const darkThemes = themes.filter((theme) => theme.kind === ThemeKind.Dark);

  const selectedLightTheme = lightThemes.find((theme) => theme.id === lightThemeId) ?? LightTheme;
  const selectedDarkTheme = darkThemes.find((theme) => theme.id === darkThemeId) ?? DarkTheme;

  const [ltCords, setLTCords] = useState<RectCords>();
  const [dtCords, setDTCords] = useState<RectCords>();

  const handleLightThemeMenu: MouseEventHandler<HTMLButtonElement> = (evt) => {
    setLTCords(evt.currentTarget.getBoundingClientRect());
  };
  const handleDarkThemeMenu: MouseEventHandler<HTMLButtonElement> = (evt) => {
    setDTCords(evt.currentTarget.getBoundingClientRect());
  };

  const handleLightThemeSelect = (theme: Theme) => {
    setLightThemeId(theme.id);
    setLTCords(undefined);
  };

  const handleDarkThemeSelect = (theme: Theme) => {
    setDarkThemeId(theme.id);
    setDTCords(undefined);
  };

  return (
    <Box wrap="Wrap" gap="400">
      <SettingTile
        title="Light Theme:"
        after={
          <Chip
            variant={themeKind === ThemeKind.Light ? 'Primary' : 'Secondary'}
            outlined={themeKind === ThemeKind.Light}
            radii="Pill"
            after={<Icon size="200" src={Icons.ChevronBottom} />}
            onClick={handleLightThemeMenu}
          >
            <Text size="B300">{themeNames[selectedLightTheme.id] ?? selectedLightTheme.id}</Text>
          </Chip>
        }
      />
      <PopOut
        anchor={ltCords}
        offset={5}
        position="Bottom"
        align="End"
        content={
          <FocusTrap
            focusTrapOptions={{
              initialFocus: false,
              onDeactivate: () => setLTCords(undefined),
              clickOutsideDeactivates: true,
              isKeyForward: (evt: KeyboardEvent) =>
                evt.key === 'ArrowDown' || evt.key === 'ArrowRight',
              isKeyBackward: (evt: KeyboardEvent) =>
                evt.key === 'ArrowUp' || evt.key === 'ArrowLeft',
              escapeDeactivates: stopPropagation,
            }}
          >
            <ThemeSelector
              themeNames={themeNames}
              themes={lightThemes}
              selected={selectedLightTheme}
              onSelect={handleLightThemeSelect}
            />
          </FocusTrap>
        }
      />
      <SettingTile
        title="Dark Theme:"
        after={
          <Chip
            variant={themeKind === ThemeKind.Dark ? 'Primary' : 'Secondary'}
            outlined={themeKind === ThemeKind.Dark}
            radii="Pill"
            after={<Icon size="200" src={Icons.ChevronBottom} />}
            onClick={handleDarkThemeMenu}
          >
            <Text size="B300">{themeNames[selectedDarkTheme.id] ?? selectedDarkTheme.id}</Text>
          </Chip>
        }
      />
      <PopOut
        anchor={dtCords}
        offset={5}
        position="Bottom"
        align="End"
        content={
          <FocusTrap
            focusTrapOptions={{
              initialFocus: false,
              onDeactivate: () => setDTCords(undefined),
              clickOutsideDeactivates: true,
              isKeyForward: (evt: KeyboardEvent) =>
                evt.key === 'ArrowDown' || evt.key === 'ArrowRight',
              isKeyBackward: (evt: KeyboardEvent) =>
                evt.key === 'ArrowUp' || evt.key === 'ArrowLeft',
              escapeDeactivates: stopPropagation,
            }}
          >
            <ThemeSelector
              themeNames={themeNames}
              themes={darkThemes}
              selected={selectedDarkTheme}
              onSelect={handleDarkThemeSelect}
            />
          </FocusTrap>
        }
      />
    </Box>
  );
}

function PageZoomInput() {
  const [pageZoom, setPageZoom] = useSetting(settingsAtom, 'pageZoom');
  const [currentZoom, setCurrentZoom] = useState(`${pageZoom}`);

  const handleZoomChange: ChangeEventHandler<HTMLInputElement> = (evt) => {
    setCurrentZoom(evt.target.value);
  };

  const handleZoomEnter: KeyboardEventHandler<HTMLInputElement> = (evt) => {
    if (isKeyHotkey('escape', evt)) {
      evt.stopPropagation();
      setCurrentZoom(pageZoom.toString());
    }
    if (
      isKeyHotkey('enter', evt) &&
      'value' in evt.target &&
      typeof evt.target.value === 'string'
    ) {
      const newZoom = parseInt(evt.target.value, 10);
      if (Number.isNaN(newZoom)) return;
      const safeZoom = Math.max(Math.min(newZoom, 150), 75);
      setPageZoom(safeZoom);
      setCurrentZoom(safeZoom.toString());
    }
  };

  return (
    <Input
      style={{ width: toRem(100) }}
      aria-label="Page zoom percentage"
      variant={pageZoom === parseInt(currentZoom, 10) ? 'Secondary' : 'Success'}
      size="300"
      radii="300"
      type="number"
      min="75"
      max="150"
      value={currentZoom}
      onChange={handleZoomChange}
      onKeyDown={handleZoomEnter}
      after={<Text size="T300">%</Text>}
      outlined
    />
  );
}

function Appearance() {
  const [systemTheme, setSystemTheme] = useSetting(settingsAtom, 'useSystemTheme');
  const [monochromeMode, setMonochromeMode] = useSetting(settingsAtom, 'monochromeMode');
  const [twitterEmoji, setTwitterEmoji] = useSetting(settingsAtom, 'twitterEmoji');
  const [perMessageProfiles, setPerMessageProfiles] = useSetting(
    settingsAtom,
    'perMessageProfiles',
  );
  const [lotusTerminal, setLotusTerminal] = useSetting(settingsAtom, 'lotusTerminal');
  const [nightLightEnabled, setNightLightEnabled] = useSetting(settingsAtom, 'nightLightEnabled');
  const [nightLightOpacity, setNightLightOpacity] = useSetting(settingsAtom, 'nightLightOpacity');
  const [nightLightSchedule, setNightLightSchedule] = useSetting(
    settingsAtom,
    'nightLightSchedule',
  );
  const [nightLightStart, setNightLightStart] = useSetting(settingsAtom, 'nightLightStart');
  const [nightLightEnd, setNightLightEnd] = useSetting(settingsAtom, 'nightLightEnd');
  const [glassmorphismSidebar, setGlassmorphismSidebar] = useSetting(
    settingsAtom,
    'glassmorphismSidebar',
  );
  const [pauseAnimations, setPauseAnimations] = useSetting(settingsAtom, 'pauseAnimations');
  const [mentionHighlightColor, setMentionHighlightColor] = useSetting(
    settingsAtom,
    'mentionHighlightColor',
  );
  const [customAccentColor, setCustomAccentColor] = useSetting(settingsAtom, 'customAccentColor');
  const [fontFamily, setFontFamily] = useSetting(settingsAtom, 'fontFamily');
  const [seasonalThemeOverride, setSeasonalThemeOverride] = useSetting(
    settingsAtom,
    'seasonalThemeOverride',
  );
  const [, setChatBackground] = useSetting(settingsAtom, 'chatBackground');

  return (
    <Box direction="Column" gap="100">
      <Text size="L400">Appearance</Text>
      <SequenceCard
        className={SequenceCardStyle}
        variant="SurfaceVariant"
        direction="Column"
        gap="400"
      >
        <SettingTile
          title="System Theme"
          description="Choose between light and dark theme based on system preference."
          after={<Switch variant="Primary" value={systemTheme} onChange={setSystemTheme} />}
        />
        {systemTheme && <SystemThemePreferences />}
      </SequenceCard>

      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="Theme"
          description="Theme to use when system theme is not enabled."
          after={<SelectTheme disabled={systemTheme} />}
        />
      </SequenceCard>

      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="Monochrome Mode"
          after={<Switch variant="Primary" value={monochromeMode} onChange={setMonochromeMode} />}
        />
      </SequenceCard>

      <DesktopChromeSetting />
      <AutostartSetting />

      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="Twitter Emoji"
          after={<Switch variant="Primary" value={twitterEmoji} onChange={setTwitterEmoji} />}
        />
      </SequenceCard>

      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile title="Page Zoom" after={<PageZoomInput />} />
      </SequenceCard>

      <SequenceCard
        className={SequenceCardStyle}
        variant="SurfaceVariant"
        direction="Column"
        gap="200"
      >
        <SettingTile
          title="Chat Background"
          description="Pattern applied behind the message timeline."
        />
        <Box style={{ padding: `0 ${config.space.S400} ${config.space.S300}` }}>
          <ChatBgGrid />
        </Box>
      </SequenceCard>

      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="Pause Background Animations"
          description="Stop background animation to reduce motion or improve performance."
          after={<Switch variant="Primary" value={pauseAnimations} onChange={setPauseAnimations} />}
        />
      </SequenceCard>

      <SequenceCard
        className={SequenceCardStyle}
        variant="SurfaceVariant"
        direction="Column"
        gap="200"
      >
        <SettingTile
          title="Seasonal Theme"
          description="Decorative overlays for holidays and events. “Auto” follows the calendar — each theme below shows the dates it turns on. Click to select."
        />
        <Box style={{ padding: `0 ${config.space.S400} ${config.space.S300}` }}>
          <SeasonalBgGrid
            value={seasonalThemeOverride ?? 'auto'}
            onChange={(v) => {
              setSeasonalThemeOverride(v);
              // Any active seasonal mode (incl. "auto") is mutually exclusive
              // with a chat background — else picking it is a silent no-op, since
              // SeasonalEffect suppresses the overlay while a background is set.
              // Only "off" leaves the background alone.
              if (v !== 'off') setChatBackground('none');
            }}
          />
        </Box>
      </SequenceCard>

      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="Show Profile on Every Message"
          description="Display avatar and name on each message instead of grouping consecutive messages."
          after={
            <Switch variant="Primary" value={perMessageProfiles} onChange={setPerMessageProfiles} />
          }
        />
      </SequenceCard>
      <SequenceCard
        className={SequenceCardStyle}
        variant="SurfaceVariant"
        direction="Column"
        gap="200"
      >
        <SettingTile
          title="Night Light"
          description="Reduce blue light with a warm orange tint overlay."
          after={
            <Switch variant="Primary" value={nightLightEnabled} onChange={setNightLightEnabled} />
          }
        />
        {nightLightEnabled && (
          <Box
            direction="Column"
            gap="300"
            style={{ padding: `0 ${config.space.S400} ${config.space.S300}` }}
          >
            <Box direction="Column" gap="100">
              <Text size="T200" priority="300">
                Intensity: {nightLightOpacity}%
              </Text>
              <input
                aria-label="Night light intensity"
                type="range"
                min={5}
                max={80}
                value={nightLightOpacity}
                onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
                  setNightLightOpacity(parseInt(e.target.value, 10))
                }
                style={{ width: '100%', accentColor: color.Primary.Main }}
              />
            </Box>
            <Box alignItems="Center" justifyContent="SpaceBetween" gap="200">
              <Box direction="Column">
                <Text size="T300">Schedule</Text>
                <Text size="T200" priority="300">
                  Only tint during the hours below
                </Text>
              </Box>
              <Switch
                variant="Primary"
                value={nightLightSchedule}
                onChange={setNightLightSchedule}
              />
            </Box>
            {nightLightSchedule && (
              <Box gap="200">
                <Box direction="Column" gap="100" style={{ flex: 1 }}>
                  <Text as="label" htmlFor="night-light-start" size="T200" priority="400">
                    From
                  </Text>
                  <input
                    id="night-light-start"
                    type="time"
                    value={nightLightStart}
                    onChange={(e) => setNightLightStart(e.target.value)}
                    style={pickerInputStyle(color, config)}
                  />
                </Box>
                <Box direction="Column" gap="100" style={{ flex: 1 }}>
                  <Text as="label" htmlFor="night-light-end" size="T200" priority="400">
                    To
                  </Text>
                  <input
                    id="night-light-end"
                    type="time"
                    value={nightLightEnd}
                    onChange={(e) => setNightLightEnd(e.target.value)}
                    style={pickerInputStyle(color, config)}
                  />
                </Box>
              </Box>
            )}
            {nightLightSchedule && nightLightStart === nightLightEnd && (
              <Text size="T200" style={{ color: color.Critical.Main }}>
                Start and end times are the same, so the tint never turns on. Set different times.
              </Text>
            )}
          </Box>
        )}
      </SequenceCard>

      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="Glassmorphism Sidebar"
          description="Semi-transparent sidebar with frosted glass effect. Works best with custom chat backgrounds."
          after={
            <Switch
              variant="Primary"
              value={glassmorphismSidebar}
              onChange={setGlassmorphismSidebar}
            />
          }
        />
      </SequenceCard>

      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="Lotus Terminal Mode"
          description="LotusGuild Terminal Design System: Anduril Orange + Ice Cyan + Matrix Green, dot-grid background, CRT scanlines, and boot sequence animation (press Escape to skip)."
          after={
            <Box direction="Row" gap="200" alignItems="Center">
              {lotusTerminal && (
                <button
                  type="button"
                  onClick={() => {
                    resetBootSequence();
                    runLotusBootSequence(true);
                  }}
                  title="Replay boot sequence"
                  style={{
                    background: 'transparent',
                    border: '1px solid var(--accent-orange-border)',
                    color: 'var(--accent-orange)',
                    fontSize: '0.65rem',
                    padding: '0.2rem 0.6rem',
                    cursor: 'pointer',
                    fontFamily: 'inherit',
                    letterSpacing: '0.1em',
                    textTransform: 'uppercase',
                    whiteSpace: 'nowrap',
                  }}
                >
                  ▶ Boot
                </button>
              )}
              <Switch variant="Primary" value={lotusTerminal} onChange={setLotusTerminal} />
            </Box>
          }
        />
      </SequenceCard>
      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="UI Font"
          description="Font used throughout the interface."
          after={
            <SettingsSelect
              value={fontFamily ?? 'inter'}
              onChange={(v) =>
                setFontFamily(v as 'system' | 'inter' | 'jetbrains-mono' | 'fira-code')
              }
              options={[
                { value: 'system', label: 'System Default' },
                { value: 'inter', label: 'Inter (default)' },
                { value: 'jetbrains-mono', label: 'JetBrains Mono' },
                { value: 'fira-code', label: 'Fira Code' },
              ]}
            />
          }
        />
      </SequenceCard>
      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="@Mention Highlight Color"
          description="Color used to highlight messages that mention you. Leave empty to use the theme default."
          after={
            <HexColorPickerPopOut
              picker={
                <HexColorPicker
                  color={mentionHighlightColor || '#4caf50'}
                  onChange={setMentionHighlightColor}
                />
              }
              onRemove={mentionHighlightColor ? () => setMentionHighlightColor('') : undefined}
            >
              {(openPicker, opened) => (
                <Button
                  type="button"
                  aria-pressed={opened}
                  onClick={openPicker}
                  size="300"
                  variant="Secondary"
                  fill="Soft"
                  radii="300"
                  before={
                    <span
                      style={{
                        width: toRem(16),
                        height: toRem(16),
                        borderRadius: config.radii.R300,
                        background: mentionHighlightColor || color.Primary.Main,
                        border: `${config.borderWidth.B300} solid ${color.SurfaceVariant.ContainerLine}`,
                        display: 'inline-block',
                        flexShrink: 0,
                      }}
                    />
                  }
                >
                  <Text size="B300">{mentionHighlightColor ? 'Change' : 'Pick'}</Text>
                </Button>
              )}
            </HexColorPickerPopOut>
          }
        />
      </SequenceCard>
      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="Custom Accent Color"
          description={
            lotusTerminal
              ? 'Only applies to non-TDS themes. Disable Lotus Terminal Mode to use a custom accent.'
              : 'Recolors the app accent (buttons, active states, links, selected states). Leave empty to use the theme default.'
          }
          after={
            <HexColorPickerPopOut
              picker={
                <HexColorPicker
                  color={customAccentColor || color.Primary.Main}
                  onChange={setCustomAccentColor}
                />
              }
              onRemove={customAccentColor ? () => setCustomAccentColor('') : undefined}
            >
              {(openPicker, opened) => (
                <Button
                  type="button"
                  aria-pressed={opened}
                  onClick={openPicker}
                  disabled={lotusTerminal}
                  size="300"
                  variant="Secondary"
                  fill="Soft"
                  radii="300"
                  before={
                    <span
                      style={{
                        width: toRem(16),
                        height: toRem(16),
                        borderRadius: config.radii.R300,
                        background: customAccentColor || color.Primary.Main,
                        border: `${config.borderWidth.B300} solid ${color.SurfaceVariant.ContainerLine}`,
                        display: 'inline-block',
                        flexShrink: 0,
                      }}
                    />
                  }
                >
                  <Text size="B300">{customAccentColor ? 'Change' : 'Pick'}</Text>
                </Button>
              )}
            </HexColorPickerPopOut>
          }
        />
      </SequenceCard>
    </Box>
  );
}

type DateHintProps = {
  hasChanges: boolean;
  handleReset: () => void;
};
function DateHint({ hasChanges, handleReset }: DateHintProps) {
  const [anchor, setAnchor] = useState<RectCords>();
  const categoryPadding = { padding: config.space.S200, paddingTop: 0 };

  const handleOpenMenu: MouseEventHandler<HTMLElement> = (evt) => {
    setAnchor(evt.currentTarget.getBoundingClientRect());
  };
  return (
    <PopOut
      anchor={anchor}
      position="Top"
      align="End"
      content={
        <FocusTrap
          focusTrapOptions={{
            initialFocus: false,
            onDeactivate: () => setAnchor(undefined),
            clickOutsideDeactivates: true,
            escapeDeactivates: stopPropagation,
          }}
        >
          <Menu style={{ maxHeight: '85vh', overflowY: 'auto' }}>
            <Header size="300" style={{ padding: `0 ${config.space.S200}` }}>
              <Text size="L400">Formatting</Text>
            </Header>

            <Box direction="Column">
              <Box style={categoryPadding} direction="Column">
                <Header size="300">
                  <Text size="L400">Year</Text>
                </Header>
                <Box direction="Column" tabIndex={0} gap="100">
                  <Text size="T300">
                    YY
                    <Text as="span" size="Inherit" priority="300">
                      {': '}
                      Two-digit year
                    </Text>{' '}
                  </Text>
                  <Text size="T300">
                    YYYY
                    <Text as="span" size="Inherit" priority="300">
                      {': '}Four-digit year
                    </Text>
                  </Text>
                </Box>
              </Box>

              <Box style={categoryPadding} direction="Column">
                <Header size="300">
                  <Text size="L400">Month</Text>
                </Header>
                <Box direction="Column" tabIndex={0} gap="100">
                  <Text size="T300">
                    M
                    <Text as="span" size="Inherit" priority="300">
                      {': '}The month
                    </Text>
                  </Text>
                  <Text size="T300">
                    MM
                    <Text as="span" size="Inherit" priority="300">
                      {': '}Two-digit month
                    </Text>{' '}
                  </Text>
                  <Text size="T300">
                    MMM
                    <Text as="span" size="Inherit" priority="300">
                      {': '}Short month name
                    </Text>
                  </Text>
                  <Text size="T300">
                    MMMM
                    <Text as="span" size="Inherit" priority="300">
                      {': '}Full month name
                    </Text>
                  </Text>
                </Box>
              </Box>

              <Box style={categoryPadding} direction="Column">
                <Header size="300">
                  <Text size="L400">Day of the Month</Text>
                </Header>
                <Box direction="Column" tabIndex={0} gap="100">
                  <Text size="T300">
                    D
                    <Text as="span" size="Inherit" priority="300">
                      {': '}Day of the month
                    </Text>
                  </Text>
                  <Text size="T300">
                    DD
                    <Text as="span" size="Inherit" priority="300">
                      {': '}Two-digit day of the month
                    </Text>
                  </Text>
                </Box>
              </Box>
              <Box style={categoryPadding} direction="Column">
                <Header size="300">
                  <Text size="L400">Day of the Week</Text>
                </Header>
                <Box direction="Column" tabIndex={0} gap="100">
                  <Text size="T300">
                    d
                    <Text as="span" size="Inherit" priority="300">
                      {': '}Day of the week (Sunday = 0)
                    </Text>
                  </Text>
                  <Text size="T300">
                    dd
                    <Text as="span" size="Inherit" priority="300">
                      {': '}Two-letter day name
                    </Text>
                  </Text>
                  <Text size="T300">
                    ddd
                    <Text as="span" size="Inherit" priority="300">
                      {': '}Short day name
                    </Text>
                  </Text>
                  <Text size="T300">
                    dddd
                    <Text as="span" size="Inherit" priority="300">
                      {': '}Full day name
                    </Text>
                  </Text>
                </Box>
              </Box>
            </Box>
          </Menu>
        </FocusTrap>
      }
    >
      {hasChanges ? (
        <IconButton
          onClick={handleReset}
          type="reset"
          variant="Secondary"
          size="300"
          radii="300"
          aria-label="Reset to default"
        >
          <Icon src={Icons.Cross} size="100" />
        </IconButton>
      ) : (
        <IconButton
          onClick={handleOpenMenu}
          type="button"
          variant="Secondary"
          size="300"
          radii="300"
          aria-label="Setting info"
          aria-pressed={!!anchor}
        >
          <Icon style={{ opacity: config.opacity.P300 }} size="100" src={Icons.Info} />
        </IconButton>
      )}
    </PopOut>
  );
}

type CustomDateFormatProps = {
  value: string;
  onChange: (format: string) => void;
};
function CustomDateFormat({ value, onChange }: CustomDateFormatProps) {
  const [dateFormatCustom, setDateFormatCustom] = useState(value);

  useEffect(() => {
    setDateFormatCustom(value);
  }, [value]);

  const handleChange: ChangeEventHandler<HTMLInputElement> = (evt) => {
    const format = evt.currentTarget.value;
    setDateFormatCustom(format);
  };

  const handleReset = () => {
    setDateFormatCustom(value);
  };

  const handleSubmit: FormEventHandler<HTMLFormElement> = (evt) => {
    evt.preventDefault();

    const target = evt.target as HTMLFormElement | undefined;
    const customDateFormatInput = target?.customDateFormatInput as HTMLInputElement | undefined;
    const format = customDateFormatInput?.value;
    if (!format) return;

    onChange(format);
  };

  const hasChanges = dateFormatCustom !== value;
  return (
    <SettingTile>
      <Box as="form" onSubmit={handleSubmit} gap="200">
        <Box grow="Yes" direction="Column">
          <Input
            required
            name="customDateFormatInput"
            aria-label="Custom date format"
            value={dateFormatCustom}
            onChange={handleChange}
            maxLength={16}
            autoComplete="off"
            variant="Secondary"
            radii="300"
            style={{ paddingRight: config.space.S200 }}
            after={<DateHint hasChanges={hasChanges} handleReset={handleReset} />}
          />
        </Box>
        <Button
          size="400"
          variant={hasChanges ? 'Success' : 'Secondary'}
          fill={hasChanges ? 'Solid' : 'Soft'}
          outlined
          radii="300"
          disabled={!hasChanges}
          type="submit"
        >
          <Text size="B400">Save</Text>
        </Button>
      </Box>
    </SettingTile>
  );
}

type PresetDateFormatProps = {
  value: string;
  onChange: (format: string) => void;
};
function PresetDateFormat({ value, onChange }: PresetDateFormatProps) {
  const [menuCords, setMenuCords] = useState<RectCords>();
  const dateFormatItems = useDateFormatItems();

  const getDisplayDate = (format: string): string =>
    format !== '' ? dayjs().format(format) : 'Custom';

  const handleMenu: MouseEventHandler<HTMLButtonElement> = (evt) => {
    setMenuCords(evt.currentTarget.getBoundingClientRect());
  };

  const handleSelect = (format: DateFormat) => {
    onChange(format);
    setMenuCords(undefined);
  };

  return (
    <>
      <Button
        size="300"
        variant="Secondary"
        outlined
        fill="Soft"
        radii="300"
        after={<Icon size="300" src={Icons.ChevronBottom} />}
        onClick={handleMenu}
      >
        <Text size="T300">
          {getDisplayDate(dateFormatItems.find((i) => i.format === value)?.format ?? value)}
        </Text>
      </Button>
      <PopOut
        anchor={menuCords}
        offset={5}
        position="Bottom"
        align="End"
        content={
          <FocusTrap
            focusTrapOptions={{
              initialFocus: false,
              onDeactivate: () => setMenuCords(undefined),
              clickOutsideDeactivates: true,
              isKeyForward: (evt: KeyboardEvent) =>
                evt.key === 'ArrowDown' || evt.key === 'ArrowRight',
              isKeyBackward: (evt: KeyboardEvent) =>
                evt.key === 'ArrowUp' || evt.key === 'ArrowLeft',
              escapeDeactivates: stopPropagation,
            }}
          >
            <Menu>
              <Box direction="Column" gap="100" style={{ padding: config.space.S100 }}>
                {dateFormatItems.map((item) => (
                  <MenuItem
                    key={item.format}
                    size="300"
                    variant={value === item.format ? 'Primary' : 'Surface'}
                    radii="300"
                    onClick={() => handleSelect(item.format)}
                  >
                    <Text size="T300">{getDisplayDate(item.format)}</Text>
                  </MenuItem>
                ))}
              </Box>
            </Menu>
          </FocusTrap>
        }
      />
    </>
  );
}

function SelectDateFormat() {
  const [dateFormatString, setDateFormatString] = useSetting(settingsAtom, 'dateFormatString');
  const [selectedDateFormat, setSelectedDateFormat] = useState(dateFormatString);
  const customDateFormat = selectedDateFormat === '';

  const handlePresetChange = (format: string) => {
    setSelectedDateFormat(format);
    if (format !== '') {
      setDateFormatString(format);
    }
  };

  return (
    <>
      <SettingTile
        title="Date Format"
        description={customDateFormat ? dayjs().format(dateFormatString) : ''}
        after={<PresetDateFormat value={selectedDateFormat} onChange={handlePresetChange} />}
      />
      {customDateFormat && (
        <CustomDateFormat value={dateFormatString} onChange={setDateFormatString} />
      )}
    </>
  );
}

function DateAndTime() {
  const [hour24Clock, setHour24Clock] = useSetting(settingsAtom, 'hour24Clock');

  return (
    <Box direction="Column" gap="100">
      <Text size="L400">Date & Time</Text>
      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="24-Hour Time Format"
          after={<Switch variant="Primary" value={hour24Clock} onChange={setHour24Clock} />}
        />
      </SequenceCard>

      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SelectDateFormat />
      </SequenceCard>
    </Box>
  );
}

const COMPOSER_TOOLBAR_LABELS: Record<ComposerToolbarButtonKey, string> = {
  showFormat: 'Format',
  showEmoji: 'Emoji',
  showSticker: 'Sticker',
  showGif: 'GIF',
  showLocation: 'Location',
  showPoll: 'Poll',
  showVoice: 'Voice',
  showSchedule: 'Schedule',
};

const COMPOSER_TOOLBAR_DRAG_TYPE = 'composer-toolbar-button';

type ComposerToolbarButtonRowProps = {
  buttonKey: ComposerToolbarButtonKey;
  index: number;
  active: boolean;
  onToggle: (key: ComposerToolbarButtonKey) => void;
};

function ComposerToolbarButtonRow({
  buttonKey,
  index,
  active,
  onToggle,
}: ComposerToolbarButtonRowProps) {
  const rowRef = useRef<HTMLDivElement>(null);
  const handleRef = useRef<HTMLButtonElement>(null);
  const [dragging, setDragging] = useState(false);
  const [closestEdge, setClosestEdge] = useState<Edge | null>(null);

  useEffect(() => {
    const element = rowRef.current;
    const dragHandle = handleRef.current;
    if (!element || !dragHandle) return undefined;

    return combine(
      draggable({
        element,
        dragHandle,
        getInitialData: () => ({ type: COMPOSER_TOOLBAR_DRAG_TYPE, buttonKey, index }),
        onDragStart: () => setDragging(true),
        onDrop: () => setDragging(false),
      }),
      dropTargetForElements({
        element,
        canDrop: ({ source }) => source.data.type === COMPOSER_TOOLBAR_DRAG_TYPE,
        getData: ({ input }) =>
          attachClosestEdge(
            { type: COMPOSER_TOOLBAR_DRAG_TYPE, buttonKey, index },
            { element, input, allowedEdges: ['top', 'bottom'] },
          ),
        getIsSticky: () => true,
        onDrag: ({ self, source }) => {
          if (source.data.buttonKey === buttonKey) {
            setClosestEdge(null);
            return;
          }
          setClosestEdge(extractClosestEdge(self.data));
        },
        onDragLeave: () => setClosestEdge(null),
        onDrop: () => setClosestEdge(null),
      }),
    );
  }, [buttonKey, index]);

  let boxShadow: string | undefined;
  if (closestEdge === 'top') boxShadow = `inset 0 2px 0 0 ${color.Primary.Main}`;
  else if (closestEdge === 'bottom') boxShadow = `inset 0 -2px 0 0 ${color.Primary.Main}`;

  return (
    <Box
      ref={rowRef}
      alignItems="Center"
      gap="200"
      style={{
        padding: `${config.space.S200} ${config.space.S400}`,
        opacity: dragging ? 0.5 : undefined,
        boxShadow,
      }}
    >
      <IconButton
        ref={handleRef}
        size="300"
        radii="300"
        variant="SurfaceVariant"
        style={{ cursor: 'grab' }}
        aria-label={`Reorder ${COMPOSER_TOOLBAR_LABELS[buttonKey]}`}
      >
        <Icon size="100" src={Icons.VerticalDots} />
      </IconButton>
      <Text style={{ flexGrow: 1 }} size="T300">
        {COMPOSER_TOOLBAR_LABELS[buttonKey]}
      </Text>
      <Chip
        variant={active ? 'Primary' : 'Secondary'}
        outlined={active}
        radii="Pill"
        onClick={() => onToggle(buttonKey)}
        aria-pressed={active}
      >
        <Text size="T200">{active ? 'Shown' : 'Hidden'}</Text>
      </Chip>
    </Box>
  );
}

type ComposerToolbarReorderProps = {
  order: ComposerToolbarButtonKey[];
  buttons: ComposerToolbarSettings;
  onReorder: (startIndex: number, finishIndex: number) => void;
  onToggle: (key: ComposerToolbarButtonKey) => void;
};

function ComposerToolbarReorder({
  order,
  buttons,
  onReorder,
  onToggle,
}: ComposerToolbarReorderProps) {
  useEffect(
    () =>
      monitorForElements({
        canMonitor: ({ source }) => source.data.type === COMPOSER_TOOLBAR_DRAG_TYPE,
        onDrop: ({ location, source }) => {
          const target = location.current.dropTargets[0];
          if (!target) return;
          const startIndex = source.data.index;
          const indexOfTarget = target.data.index;
          if (typeof startIndex !== 'number' || typeof indexOfTarget !== 'number') return;
          const closestEdgeOfTarget = extractClosestEdge(target.data);

          // Insert relative to the target row, then compensate for the source
          // row being removed from its original position.
          let finishIndex = closestEdgeOfTarget === 'bottom' ? indexOfTarget + 1 : indexOfTarget;
          if (startIndex < finishIndex) finishIndex -= 1;

          if (finishIndex === startIndex) return;
          onReorder(startIndex, finishIndex);
        },
      }),
    [onReorder],
  );

  return (
    <Box direction="Column">
      {order.map((key, index) => (
        <ComposerToolbarButtonRow
          key={key}
          buttonKey={key}
          index={index}
          active={buttons[key]}
          onToggle={onToggle}
        />
      ))}
    </Box>
  );
}

function Editor() {
  const [enterForNewline, setEnterForNewline] = useSetting(settingsAtom, 'enterForNewline');
  const [isMarkdown, setIsMarkdown] = useSetting(settingsAtom, 'isMarkdown');
  const [editorToolbar, setEditorToolbar] = useSetting(settingsAtom, 'editorToolbar');
  const [composerToolbarButtons, setComposerToolbarButtons] = useSetting(
    settingsAtom,
    'composerToolbarButtons',
  );

  const composerToolbarOrder = useMemo(
    () => normalizeComposerToolbarOrder(composerToolbarButtons?.order),
    [composerToolbarButtons?.order],
  );

  const toggleToolbarButton = useCallback(
    (key: ComposerToolbarButtonKey) => {
      setComposerToolbarButtons((current) => ({ ...current, [key]: !current[key] }));
    },
    [setComposerToolbarButtons],
  );

  const reorderToolbarButtons = useCallback(
    (startIndex: number, finishIndex: number) => {
      setComposerToolbarButtons((current) => ({
        ...current,
        order: reorder({
          list: normalizeComposerToolbarOrder(current.order),
          startIndex,
          finishIndex,
        }),
      }));
    },
    [setComposerToolbarButtons],
  );

  return (
    <Box direction="Column" gap="100">
      <Text size="L400">Editor</Text>
      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="ENTER for Newline"
          description={`Use ${
            isMacOS() ? KeySymbol.Command : 'Ctrl'
          } + ENTER to send message and ENTER for newline.`}
          after={<Switch variant="Primary" value={enterForNewline} onChange={setEnterForNewline} />}
        />
      </SequenceCard>
      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="Markdown Formatting"
          after={<Switch variant="Primary" value={isMarkdown} onChange={setIsMarkdown} />}
        />
      </SequenceCard>
      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="Formatting Toolbar"
          description="Show bold, italic, code and other formatting buttons above the message input."
          after={<Switch variant="Primary" value={editorToolbar} onChange={setEditorToolbar} />}
        />
      </SequenceCard>
      <SequenceCard
        className={SequenceCardStyle}
        variant="SurfaceVariant"
        direction="Column"
        gap="300"
      >
        <SettingTile
          title="Composer Toolbar"
          description="Drag to reorder buttons, and tap a button to show or hide it in the message composer."
        />
        <Box direction="Column" style={{ paddingBottom: config.space.S200 }}>
          <ComposerToolbarReorder
            order={composerToolbarOrder}
            buttons={composerToolbarButtons}
            onReorder={reorderToolbarButtons}
            onToggle={toggleToolbarButton}
          />
        </Box>
      </SequenceCard>
    </Box>
  );
}

function Privacy() {
  const [hideActivity, setHideActivity] = useSetting(settingsAtom, 'hideActivity');
  const [hidePresence, setHidePresence] = useSetting(settingsAtom, 'hidePresence');
  const [privateReadReceipts, setPrivateReadReceipts] = useSetting(
    settingsAtom,
    'privateReadReceipts',
  );
  const [warnOnUnverifiedDevices, setWarnOnUnverifiedDevices] = useSetting(
    settingsAtom,
    'warnOnUnverifiedDevices',
  );

  return (
    <Box direction="Column" gap="100">
      <Text size="L400">Privacy</Text>
      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="Hide Typing & Read Receipts"
          description="Turn off both typing status and read receipts to keep your activity private."
          after={<Switch variant="Primary" value={hideActivity} onChange={setHideActivity} />}
        />
      </SequenceCard>
      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="Private Read Receipts"
          description="Send read receipts only to you and the server — other users won't see when you've read messages."
          after={
            <Switch
              variant="Primary"
              value={privateReadReceipts}
              onChange={setPrivateReadReceipts}
            />
          }
        />
      </SequenceCard>
      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="Hide Online Status"
          description="Appear offline to everyone while still using the app. Disables online, away, and idle indicators."
          after={<Switch variant="Primary" value={hidePresence} onChange={setHidePresence} />}
        />
      </SequenceCard>
      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="Warn before sending to unverified devices"
          description="Show a warning in the composer when sending to a room with unverified devices."
          after={
            <Switch
              variant="Primary"
              value={warnOnUnverifiedDevices}
              onChange={setWarnOnUnverifiedDevices}
            />
          }
        />
      </SequenceCard>
    </Box>
  );
}

function useKeyBind(setter: (code: string) => void) {
  const [listening, setListening] = useState(false);
  const listenerRef = useRef<((e: KeyboardEvent) => void) | null>(null);

  useEffect(
    () => () => {
      if (listenerRef.current) window.removeEventListener('keydown', listenerRef.current, true);
    },
    [],
  );

  const startListening = useCallback(() => {
    if (listening) return;
    setListening(true);
    const onKey = (e: KeyboardEvent) => {
      e.preventDefault();
      if (e.code !== 'Escape') setter(e.code);
      setListening(false);
      window.removeEventListener('keydown', onKey, true);
      listenerRef.current = null;
    };
    listenerRef.current = onKey;
    window.addEventListener('keydown', onKey, true);
  }, [listening, setter]);

  return { listening, startListening };
}

const keyLabel = (code: string) =>
  code === 'Space' ? 'Space' : code.replace('Key', '').replace('Digit', '');

function Calls() {
  const [cameraOnJoin, setCameraOnJoin] = useSetting(settingsAtom, 'cameraOnJoin');
  const [callNoiseSuppression, setCallNoiseSuppression] = useSetting(
    settingsAtom,
    'callNoiseSuppression',
  );
  const [callDenoiseModel, setCallDenoiseModel] = useSetting(settingsAtom, 'callDenoiseModel');
  const [callDenoiseNativeNS, setCallDenoiseNativeNS] = useSetting(
    settingsAtom,
    'callDenoiseNativeNS',
  );
  const [callDenoiseGate, setCallDenoiseGate] = useSetting(settingsAtom, 'callDenoiseGate');
  const [callDenoiseGateThreshold, setCallDenoiseGateThreshold] = useSetting(
    settingsAtom,
    'callDenoiseGateThreshold',
  );

  const [pttMode, setPttMode] = useSetting(settingsAtom, 'pttMode');
  const [pttKey, setPttKey] = useSetting(settingsAtom, 'pttKey');
  const [deafenKey, setDeafenKey] = useSetting(settingsAtom, 'deafenKey');
  const [afkAutoMute, setAfkAutoMute] = useSetting(settingsAtom, 'afkAutoMute');
  const [afkTimeoutMinutes, setAfkTimeoutMinutes] = useSetting(settingsAtom, 'afkTimeoutMinutes');
  const [callJoinLeaveSound, setCallJoinLeaveSound] = useSetting(
    settingsAtom,
    'callJoinLeaveSound',
  );
  const [ringtoneVolume, setRingtoneVolume] = useSetting(settingsAtom, 'ringtoneVolume');
  const [ringtoneId, setRingtoneId] = useSetting(settingsAtom, 'ringtoneId');

  const [callAudioBitrate, setCallAudioBitrate] = useSetting(settingsAtom, 'callAudioBitrate');
  const [screenshareBitrate, setScreenshareBitrate] = useSetting(
    settingsAtom,
    'screenshareBitrate',
  );
  const [screenshareFramerate, setScreenshareFramerate] = useSetting(
    settingsAtom,
    'screenshareFramerate',
  );
  const [soundboardEnabled, setSoundboardEnabled] = useSetting(settingsAtom, 'soundboardEnabled');
  const [soundboardVolume, setSoundboardVolume] = useSetting(settingsAtom, 'soundboardVolume');

  const handleJoinLeaveSoundChange = (value: 'off' | 'chime' | 'soft' | 'retro') => {
    setCallJoinLeaveSound(value);
    if (value !== 'off') playCallJoinSound(value);
  };

  const handleRingtoneChange = (value: RingtoneId) => {
    setRingtoneId(value);
    previewRingtone(value, Math.max(0, Math.min(1, ringtoneVolume / 100)));
  };

  const pttBind = useKeyBind(setPttKey);
  const deafenBind = useKeyBind(setDeafenKey);

  const mlSupported = isMLDenoiseSupported();
  const selectedDenoiseModel = DENOISE_MODELS.find((m) => m.id === callDenoiseModel);

  return (
    <Box direction="Column" gap="100">
      <Text size="L400">Calls</Text>
      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="Join with Camera On"
          description="Enable camera automatically when joining a voice or video call. Camera is off by default."
          after={<Switch variant="Primary" value={cameraOnJoin} onChange={setCameraOnJoin} />}
        />
      </SequenceCard>
      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="Noise Suppression"
          description={
            <Box direction="Column" gap="200">
              <Text>
                Filter background noise from your mic during calls. Browser-native uses the built-in
                WebRTC suppressor (Google NSNet2). ML runs a dedicated model for stronger removal.
              </Text>

              {!mlSupported && (
                <Box direction="Column" gap="100">
                  <Text size="T200" priority="400">
                    ML options are not supported in this browser.
                  </Text>
                  <Box as="ul" style={{ paddingLeft: '20px', margin: 0 }}>
                    {ML_DENOISE_REQUIREMENTS.map((req) => (
                      <Text as="li" key={req} size="T200">
                        {req}
                      </Text>
                    ))}
                  </Box>
                </Box>
              )}
            </Box>
          }
          after={
            <SettingsSelect<NoiseSuppressionMode>
              value={callNoiseSuppression}
              onChange={setCallNoiseSuppression}
              options={[
                { value: 'off', label: 'Off' },
                { value: 'browser', label: 'Browser-native' },
                {
                  value: 'ml',
                  label: 'ML (Advanced)',
                  disabled: !mlSupported,
                },
              ]}
            />
          }
        />

        {callNoiseSuppression === 'ml' && (
          <Box
            direction="Column"
            gap="400"
            style={{
              padding: '16px',
              marginTop: '8px',
              borderTop: `${config.borderWidth.B300} solid ${color.Surface.ContainerLine}`,
              background: color.Surface.Container,
            }}
          >
            {/* ── Model selection ───────────────────────────────────────── */}
            <Box direction="Column" gap="200">
              <Text size="L400">Model</Text>
              <SettingTile
                title="ML Model"
                description="Choose the machine learning model used for noise removal. Heavier models clean more aggressively at a higher CPU cost."
                after={
                  <SettingsSelect<DenoiseModelId>
                    value={callDenoiseModel}
                    onChange={setCallDenoiseModel}
                    options={DENOISE_MODELS.map((m) => ({ value: m.id, label: m.name }))}
                  />
                }
              />
              {selectedDenoiseModel && (
                <Box
                  direction="Column"
                  gap="200"
                  style={{
                    padding: '12px',
                    borderRadius: '8px',
                    border: `${config.borderWidth.B300} solid ${color.Surface.ContainerLine}`,
                    background: color.SurfaceVariant.Container,
                  }}
                >
                  <Text size="T300">{selectedDenoiseModel.name}</Text>
                  <Text size="T200" priority="300">
                    {selectedDenoiseModel.description}
                  </Text>
                  <Box direction="Row" gap="400" wrap="Wrap">
                    {(
                      [
                        { label: 'CPU', value: selectedDenoiseModel.cpuUsage },
                        { label: 'Voice quality', value: selectedDenoiseModel.voiceQuality },
                        { label: 'Transients', value: selectedDenoiseModel.transients },
                        { label: 'Download', value: selectedDenoiseModel.binarySize },
                      ] as const
                    ).map((stat) => (
                      <Box key={stat.label} direction="Column" gap="100">
                        <Text size="T200" priority="300">
                          {stat.label}
                        </Text>
                        <Text size="T300">{stat.value}</Text>
                      </Box>
                    ))}
                  </Box>
                </Box>
              )}

              <details>
                <summary style={{ cursor: 'pointer' }}>
                  <Text as="span" size="T200" priority="300">
                    Compare all models
                  </Text>
                </summary>
                <Box direction="Column" gap="100" style={{ overflowX: 'auto', marginTop: '8px' }}>
                  <Box
                    direction="Row"
                    gap="100"
                    style={{
                      borderBottom: `${config.borderWidth.B300} solid ${color.Surface.ContainerLine}`,
                      paddingBottom: '4px',
                    }}
                  >
                    <Box style={{ width: '160px' }}>
                      <Text size="T200" priority="300">
                        Model
                      </Text>
                    </Box>
                    <Box style={{ width: '80px' }}>
                      <Text size="T200" priority="300">
                        CPU
                      </Text>
                    </Box>
                    <Box style={{ width: '90px' }}>
                      <Text size="T200" priority="300">
                        Quality
                      </Text>
                    </Box>
                    <Box grow="Yes">
                      <Text size="T200" priority="300">
                        Transients
                      </Text>
                    </Box>
                  </Box>
                  {DENOISE_MODELS.map((model) => (
                    <Box key={model.id} direction="Row" gap="100">
                      <Box style={{ width: '160px' }}>
                        <Text size="T200">{model.name}</Text>
                      </Box>
                      <Box style={{ width: '80px' }}>
                        <Text size="T200">{model.cpuUsage}</Text>
                      </Box>
                      <Box style={{ width: '90px' }}>
                        <Text size="T200">{model.voiceQuality}</Text>
                      </Box>
                      <Box grow="Yes">
                        <Text size="T200">{model.transients}</Text>
                      </Box>
                    </Box>
                  ))}
                </Box>
              </details>

              <Text size="T200" priority="400">
                Note: Applying changes requires rejoining the call.
              </Text>
            </Box>

            {/* ── Enhancement toggles ───────────────────────────────────── */}
            <Box
              direction="Column"
              gap="300"
              style={{
                paddingTop: '12px',
                borderTop: `${config.borderWidth.B300} solid ${color.Surface.ContainerLine}`,
              }}
            >
              <Text size="L400">Enhancements</Text>
              <SettingTile
                title="Series Suppression"
                description="Run the browser's native stationary noise filter before the ML model. Recommended for eliminating fan hum."
                after={
                  <Switch
                    variant="Primary"
                    value={callDenoiseNativeNS}
                    onChange={setCallDenoiseNativeNS}
                  />
                }
              />

              <SettingTile
                title="Noise Gate"
                description="Hard-cut audio when you aren't speaking to ensure absolute silence between sentences."
                after={
                  <Switch variant="Primary" value={callDenoiseGate} onChange={setCallDenoiseGate} />
                }
              />

              {callDenoiseGate && (
                <Box direction="Column" gap="100">
                  <Box direction="Row" justifyContent="SpaceBetween">
                    <Text size="T200">Gate Threshold</Text>
                    <Text size="T200">{callDenoiseGateThreshold} dB</Text>
                  </Box>
                  <input
                    aria-label="Noise gate threshold"
                    type="range"
                    min="-100"
                    max="0"
                    step="1"
                    value={callDenoiseGateThreshold}
                    onChange={(e) => setCallDenoiseGateThreshold(parseInt(e.target.value, 10))}
                    style={{ width: '100%', accentColor: color.Primary.Main }}
                  />
                </Box>
              )}
            </Box>

            {/* ── Test & calibrate ──────────────────────────────────────── */}
            <Box
              direction="Column"
              gap="200"
              style={{
                paddingTop: '12px',
                borderTop: `${config.borderWidth.B300} solid ${color.Surface.ContainerLine}`,
              }}
            >
              <Text size="L400">Test &amp; calibrate</Text>
              <Text size="T200" priority="300">
                Audition the selected model and tune the gate without joining a call. Changes to the
                settings above apply the next time you start a monitor or play a clip.
              </Text>
              <DenoiseTester
                model={callDenoiseModel}
                useGate={callDenoiseGate}
                gateThreshold={callDenoiseGateThreshold}
                nativeNS={callDenoiseNativeNS}
              />
            </Box>
          </Box>
        )}
      </SequenceCard>
      <SequenceCard
        className={SequenceCardStyle}
        variant="SurfaceVariant"
        direction="Column"
        gap="400"
      >
        <SettingTile
          title="Push to Talk"
          description="Mute your microphone by default. Hold the PTT key to speak."
          after={<Switch variant="Primary" value={pttMode} onChange={setPttMode} />}
        />
        {pttMode && (
          <SettingTile
            title="PTT Key"
            description="Press a key to bind it as your push-to-talk key."
            after={
              <Button
                size="300"
                variant={pttBind.listening ? 'Warning' : 'Secondary'}
                fill={pttBind.listening ? 'Solid' : 'Soft'}
                radii="300"
                outlined
                onClick={pttBind.startListening}
                style={{ minWidth: '90px' }}
              >
                <Text size="B300">{pttBind.listening ? 'Press a key…' : keyLabel(pttKey)}</Text>
              </Button>
            }
          />
        )}
        <SettingTile
          title="Push to Deafen"
          description="Toggle speaker mute during a call. Press Escape to cancel rebind."
          after={
            <Button
              size="300"
              variant={deafenBind.listening ? 'Warning' : 'Secondary'}
              fill={deafenBind.listening ? 'Solid' : 'Soft'}
              radii="300"
              outlined
              onClick={deafenBind.startListening}
              style={{ minWidth: '90px' }}
            >
              <Text size="B300">{deafenBind.listening ? 'Press a key…' : keyLabel(deafenKey)}</Text>
            </Button>
          }
        />
      </SequenceCard>
      <SequenceCard
        className={SequenceCardStyle}
        variant="SurfaceVariant"
        direction="Column"
        gap="400"
      >
        <SettingTile
          title="AFK Auto-Mute"
          description="Automatically mute your microphone when you have been silent in a call."
          after={<Switch variant="Primary" value={afkAutoMute} onChange={setAfkAutoMute} />}
        />
        {afkAutoMute && (
          <SettingTile
            title="Idle Timeout"
            description="How long to wait before auto-muting."
            after={
              <SettingsSelect
                value={String(afkTimeoutMinutes ?? 10)}
                onChange={(v) => setAfkTimeoutMinutes(Number(v))}
                options={[
                  { value: '1', label: '1 minute' },
                  { value: '5', label: '5 minutes' },
                  { value: '10', label: '10 minutes' },
                  { value: '20', label: '20 minutes' },
                  { value: '30', label: '30 minutes' },
                ]}
              />
            }
          />
        )}
      </SequenceCard>
      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="Ringtone"
          description="Sound played for incoming calls. Selecting an option plays a preview."
          after={
            <SettingsSelect
              value={ringtoneId}
              onChange={(v) => handleRingtoneChange(v as RingtoneId)}
              options={RINGTONE_OPTIONS}
            />
          }
        />
      </SequenceCard>
      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile title="Ringtone Volume" description="Volume of the incoming call ringtone." />
        <Box
          direction="Row"
          alignItems="Center"
          gap="200"
          style={{ padding: `0 ${config.space.S400} ${config.space.S300}` }}
        >
          <input
            type="range"
            min="0"
            max="100"
            step="5"
            value={ringtoneVolume}
            onChange={(e) => setRingtoneVolume(parseInt(e.target.value, 10))}
            aria-label="Ringtone volume"
            style={{ flex: 1, accentColor: color.Primary.Main }}
          />
          <Text size="T200" style={{ minWidth: '32px', textAlign: 'right' }}>
            {ringtoneVolume}%
          </Text>
        </Box>
      </SequenceCard>
      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="Join & Leave Sounds"
          description="Play a sound when someone joins or leaves a call you are in. Selecting an option plays a preview."
          after={
            <SettingsSelect
              value={callJoinLeaveSound}
              onChange={(v) => handleJoinLeaveSoundChange(v as 'off' | 'chime' | 'soft' | 'retro')}
              options={[
                { value: 'off', label: 'Off' },
                { value: 'chime', label: 'Chime' },
                { value: 'soft', label: 'Soft' },
                { value: 'retro', label: 'Retro' },
              ]}
            />
          }
        />
      </SequenceCard>

      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="Microphone Bitrate"
          description="Cap the audio bitrate your mic sends in calls. Lower saves bandwidth; higher is clearer. Auto lets Element Call decide."
          after={
            <SettingsSelect<CallAudioBitrate>
              value={callAudioBitrate}
              onChange={setCallAudioBitrate}
              options={AUDIO_BITRATE_OPTIONS}
            />
          }
        />
      </SequenceCard>

      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="Screenshare Bitrate"
          description="Cap the bitrate used when you share your screen. Lower is smoother on poor connections; higher is sharper."
          after={
            <SettingsSelect<ScreenshareBitrate>
              value={screenshareBitrate}
              onChange={setScreenshareBitrate}
              options={SCREENSHARE_BITRATE_OPTIONS}
            />
          }
        />
      </SequenceCard>

      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="Screenshare Framerate"
          description="Cap the frames-per-second of your screenshare. 60 fps suits motion/gaming; 15 fps suits slides and saves bandwidth."
          after={
            <SettingsSelect<ScreenshareFramerate>
              value={screenshareFramerate}
              onChange={setScreenshareFramerate}
              options={SCREENSHARE_FRAMERATE_OPTIONS}
            />
          }
        />
      </SequenceCard>

      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="Soundboard"
          description="Show a soundboard button in the call bar. Upload short audio clips (like custom emojis) to play them into the call. Clips sync across your devices."
          after={
            <Switch variant="Primary" value={soundboardEnabled} onChange={setSoundboardEnabled} />
          }
        />
        {soundboardEnabled && (
          <>
            <SettingTile title="Soundboard Volume" />
            <Box
              alignItems="Center"
              gap="200"
              style={{ padding: `0 ${config.space.S400} ${config.space.S300}` }}
            >
              <input
                type="range"
                min={0}
                max={100}
                step={5}
                value={soundboardVolume}
                onChange={(e) => setSoundboardVolume(parseInt(e.target.value, 10))}
                style={{ flexGrow: 1 }}
                aria-label="Soundboard volume"
              />
              <Text size="T200" style={{ minWidth: toRem(36), textAlign: 'right' }}>
                {soundboardVolume}%
              </Text>
            </Box>
          </>
        )}
      </SequenceCard>
    </Box>
  );
}

const SEASONAL_OPTIONS: {
  value: Settings['seasonalThemeOverride'];
  label: string;
  emoji: string;
}[] = [
  { value: 'auto', label: 'Auto', emoji: '🗓' },
  { value: 'off', label: 'Off', emoji: '×' },
  { value: 'newyear', label: 'New Year', emoji: '🎆' },
  { value: 'lunar', label: 'Lunar New Year', emoji: '🏮' },
  { value: 'valentines', label: "Valentine's", emoji: '💖' },
  { value: 'stpatricks', label: "St. Patrick's", emoji: '🍀' },
  { value: 'aprilfools', label: 'April Fools', emoji: '?' },
  { value: 'earthday', label: 'Earth Day', emoji: '🌱' },
  { value: 'autumn', label: 'Autumn', emoji: '🍂' },
  { value: 'halloween', label: 'Halloween', emoji: '🎃' },
  { value: 'christmas', label: 'Christmas', emoji: '❄️' },
  { value: 'arcade', label: 'Arcade Day', emoji: '👾' },
  { value: 'deepspace', label: 'Deep Space', emoji: '🚀' },
];

function SeasonalBgGrid({
  value,
  onChange,
}: {
  value: Settings['seasonalThemeOverride'];
  onChange: (v: Settings['seasonalThemeOverride']) => void;
}) {
  return (
    <Box
      grow="Yes"
      style={{
        display: 'grid',
        gridTemplateColumns: `repeat(auto-fill, minmax(${toRem(76)}, 1fr))`,
        gap: config.space.S200,
      }}
    >
      {SEASONAL_OPTIONS.map((opt) => {
        const selected = value === opt.value;
        const isSpecial = opt.value === 'auto' || opt.value === 'off';
        return (
          <Box key={opt.value} direction="Column" gap="100" style={{ alignItems: 'center' }}>
            <button
              type="button"
              aria-label={opt.label}
              aria-pressed={selected}
              data-selected={selected}
              className={BgSwatchStyle}
              onClick={() => onChange(opt.value)}
              style={{
                position: 'relative',
                width: toRem(76),
                height: toRem(56),
                backgroundColor: '#030508',
              }}
            >
              {!isSpecial && <SeasonalPreview theme={opt.value as SeasonTheme} />}
              <div
                style={{
                  position: 'absolute',
                  inset: 0,
                  display: 'flex',
                  alignItems: 'center',
                  justifyContent: 'center',
                  backgroundImage:
                    opt.value === 'auto'
                      ? 'linear-gradient(135deg, rgba(255,100,0,0.2), rgba(255,200,0,0.2), rgba(0,200,100,0.2), rgba(0,100,255,0.2))'
                      : undefined,
                  pointerEvents: 'none',
                }}
              >
                {isSpecial && (
                  <span style={{ fontSize: '22px', opacity: opt.value === 'off' ? 0.4 : 1 }}>
                    {opt.emoji}
                  </span>
                )}
              </div>
            </button>
            <Text size="T200" style={selected ? { color: color.Primary.Main } : undefined}>
              {opt.label}
            </Text>
            {(opt.value === 'auto' || !isSpecial) && (
              <Text size="T200" style={{ opacity: 0.6, textAlign: 'center' }}>
                {opt.value === 'auto'
                  ? 'By calendar'
                  : SEASON_DATE_RANGES[opt.value as SeasonTheme]}
              </Text>
            )}
          </Box>
        );
      })}
    </Box>
  );
}

function ChatBgGrid() {
  const [chatBackground, setChatBackground] = useSetting(settingsAtom, 'chatBackground');
  const [, setSeasonalThemeOverride] = useSetting(settingsAtom, 'seasonalThemeOverride');
  const [pauseAnimations] = useSetting(settingsAtom, 'pauseAnimations');
  const theme = useTheme();
  const isDark = theme.kind === ThemeKind.Dark;
  const reduced = useReducedMotion();

  return (
    <Box
      grow="Yes"
      style={{
        display: 'grid',
        gridTemplateColumns: `repeat(auto-fill, minmax(${toRem(76)}, 1fr))`,
        gap: config.space.S200,
      }}
    >
      {BG_OPTIONS.map((opt) => (
        <Box key={opt.value} direction="Column" gap="100" style={{ alignItems: 'center' }}>
          <button
            type="button"
            aria-label={opt.label}
            aria-pressed={chatBackground === opt.value}
            data-selected={chatBackground === opt.value}
            className={BgSwatchStyle}
            onClick={() => {
              setChatBackground(opt.value as ChatBackground);
              if (opt.value !== 'none') setSeasonalThemeOverride('off');
            }}
            style={{
              width: toRem(76),
              height: toRem(50),
              ...getChatBg(opt.value as ChatBackground, isDark, pauseAnimations || reduced),
            }}
          />
          <Text
            size="T200"
            style={chatBackground === opt.value ? { color: color.Primary.Main } : undefined}
          >
            {opt.label}
          </Text>
        </Box>
      ))}
    </Box>
  );
}

function SelectMessageLayout() {
  const [menuCords, setMenuCords] = useState<RectCords>();
  const [messageLayout, setMessageLayout] = useSetting(settingsAtom, 'messageLayout');
  const messageLayoutItems = useMessageLayoutItems();

  const handleMenu: MouseEventHandler<HTMLButtonElement> = (evt) => {
    setMenuCords(evt.currentTarget.getBoundingClientRect());
  };

  const handleSelect = (layout: MessageLayout) => {
    setMessageLayout(layout);
    setMenuCords(undefined);
  };

  return (
    <>
      <Button
        size="300"
        variant="Secondary"
        outlined
        fill="Soft"
        radii="300"
        after={<Icon size="300" src={Icons.ChevronBottom} />}
        onClick={handleMenu}
      >
        <Text size="T300">
          {messageLayoutItems.find((i) => i.layout === messageLayout)?.name ?? messageLayout}
        </Text>
      </Button>
      <PopOut
        anchor={menuCords}
        offset={5}
        position="Bottom"
        align="End"
        content={
          <FocusTrap
            focusTrapOptions={{
              initialFocus: false,
              onDeactivate: () => setMenuCords(undefined),
              clickOutsideDeactivates: true,
              isKeyForward: (evt: KeyboardEvent) =>
                evt.key === 'ArrowDown' || evt.key === 'ArrowRight',
              isKeyBackward: (evt: KeyboardEvent) =>
                evt.key === 'ArrowUp' || evt.key === 'ArrowLeft',
              escapeDeactivates: stopPropagation,
            }}
          >
            <Menu>
              <Box direction="Column" gap="100" style={{ padding: config.space.S100 }}>
                {messageLayoutItems.map((item) => (
                  <MenuItem
                    key={item.layout}
                    size="300"
                    variant={messageLayout === item.layout ? 'Primary' : 'Surface'}
                    radii="300"
                    onClick={() => handleSelect(item.layout)}
                  >
                    <Text size="T300">{item.name}</Text>
                  </MenuItem>
                ))}
              </Box>
            </Menu>
          </FocusTrap>
        }
      />
    </>
  );
}

function SelectMessageSpacing() {
  const [menuCords, setMenuCords] = useState<RectCords>();
  const [messageSpacing, setMessageSpacing] = useSetting(settingsAtom, 'messageSpacing');
  const messageSpacingItems = useMessageSpacingItems();

  const handleMenu: MouseEventHandler<HTMLButtonElement> = (evt) => {
    setMenuCords(evt.currentTarget.getBoundingClientRect());
  };

  const handleSelect = (layout: MessageSpacing) => {
    setMessageSpacing(layout);
    setMenuCords(undefined);
  };

  return (
    <>
      <Button
        size="300"
        variant="Secondary"
        outlined
        fill="Soft"
        radii="300"
        after={<Icon size="300" src={Icons.ChevronBottom} />}
        onClick={handleMenu}
      >
        <Text size="T300">
          {messageSpacingItems.find((i) => i.spacing === messageSpacing)?.name ?? messageSpacing}
        </Text>
      </Button>
      <PopOut
        anchor={menuCords}
        offset={5}
        position="Bottom"
        align="End"
        content={
          <FocusTrap
            focusTrapOptions={{
              initialFocus: false,
              onDeactivate: () => setMenuCords(undefined),
              clickOutsideDeactivates: true,
              isKeyForward: (evt: KeyboardEvent) =>
                evt.key === 'ArrowDown' || evt.key === 'ArrowRight',
              isKeyBackward: (evt: KeyboardEvent) =>
                evt.key === 'ArrowUp' || evt.key === 'ArrowLeft',
              escapeDeactivates: stopPropagation,
            }}
          >
            <Menu>
              <Box direction="Column" gap="100" style={{ padding: config.space.S100 }}>
                {messageSpacingItems.map((item) => (
                  <MenuItem
                    key={item.spacing}
                    size="300"
                    variant={messageSpacing === item.spacing ? 'Primary' : 'Surface'}
                    radii="300"
                    onClick={() => handleSelect(item.spacing)}
                  >
                    <Text size="T300">{item.name}</Text>
                  </MenuItem>
                ))}
              </Box>
            </Menu>
          </FocusTrap>
        }
      />
    </>
  );
}

function Messages() {
  const [legacyUsernameColor, setLegacyUsernameColor] = useSetting(
    settingsAtom,
    'legacyUsernameColor',
  );
  const [hideMembershipEvents, setHideMembershipEvents] = useSetting(
    settingsAtom,
    'hideMembershipEvents',
  );
  const [hideNickAvatarEvents, setHideNickAvatarEvents] = useSetting(
    settingsAtom,
    'hideNickAvatarEvents',
  );
  const [mediaAutoLoad, setMediaAutoLoad] = useSetting(settingsAtom, 'mediaAutoLoad');
  const [urlPreview, setUrlPreview] = useSetting(settingsAtom, 'urlPreview');
  const [encUrlPreview, setEncUrlPreview] = useSetting(settingsAtom, 'encUrlPreview');
  const [inlineMediaEmbeds, setInlineMediaEmbeds] = useSetting(settingsAtom, 'inlineMediaEmbeds');
  const [showHiddenEvents, setShowHiddenEvents] = useSetting(settingsAtom, 'showHiddenEvents');
  const [enforceRetentionLocally, setEnforceRetentionLocally] = useSetting(
    settingsAtom,
    'enforceRetentionLocally',
  );
  const [translateTargetLang, setTranslateTargetLang] = useSetting(
    settingsAtom,
    'translateTargetLang',
  );
  const [autoTranslate, setAutoTranslate] = useSetting(settingsAtom, 'autoTranslate');
  const translationSupported = chromeTranslationEngine.isSupported();
  const selectedTargetLang = isSupportedTargetLang(translateTargetLang)
    ? normalizeLang(translateTargetLang)
    : 'en';

  return (
    <Box direction="Column" gap="100">
      <Text size="L400">Messages</Text>
      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile title="Message Layout" after={<SelectMessageLayout />} />
      </SequenceCard>
      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile title="Message Spacing" after={<SelectMessageSpacing />} />
      </SequenceCard>
      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="Legacy Username Color"
          after={
            <Switch
              variant="Primary"
              value={legacyUsernameColor}
              onChange={setLegacyUsernameColor}
            />
          }
        />
      </SequenceCard>
      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="Hide Membership Change"
          after={
            <Switch
              variant="Primary"
              value={hideMembershipEvents}
              onChange={setHideMembershipEvents}
            />
          }
        />
      </SequenceCard>
      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="Hide Profile Change"
          after={
            <Switch
              variant="Primary"
              value={hideNickAvatarEvents}
              onChange={setHideNickAvatarEvents}
            />
          }
        />
      </SequenceCard>
      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="Disable Media Auto Load"
          after={
            <Switch
              variant="Primary"
              value={!mediaAutoLoad}
              onChange={(v) => setMediaAutoLoad(!v)}
            />
          }
        />
      </SequenceCard>
      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="URL Preview"
          description="Your homeserver fetches and caches link previews. It will see the URLs of all links you preview."
          after={<Switch variant="Primary" value={urlPreview} onChange={setUrlPreview} />}
        />
      </SequenceCard>
      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="URL Preview in Encrypted Rooms"
          description={
            <Box direction="Column" gap="100">
              <Text size="T200" priority="300">
                Your homeserver fetches link previews on your behalf. It cannot decrypt your
                messages, but will see every URL you preview in encrypted rooms, potentially
                revealing conversation topics.
              </Text>
              <Chip
                variant="Warning"
                fill="Soft"
                radii="300"
                size="400"
                style={{ alignSelf: 'flex-start' }}
              >
                <Text size="T200">Privacy risk — enabled by default</Text>
              </Chip>
            </Box>
          }
          after={<Switch variant="Primary" value={encUrlPreview} onChange={setEncUrlPreview} />}
        />
      </SequenceCard>
      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="Inline Media Players"
          description="Play YouTube, Vimeo, TikTok, Twitch, Spotify and more right in the chat. Nothing loads from the third-party site until you press play."
          after={
            <Switch variant="Primary" value={inlineMediaEmbeds} onChange={setInlineMediaEmbeds} />
          }
        />
      </SequenceCard>
      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="Show Hidden Events"
          after={
            <Switch variant="Primary" value={showHiddenEvents} onChange={setShowHiddenEvents} />
          }
        />
      </SequenceCard>
      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="Enforce Message Retention"
          description="Permanently delete your own messages once a room's retention window (Room Settings → Message Retention) has passed. Off by default; only affects your own messages."
          after={
            <Switch
              variant="Primary"
              value={enforceRetentionLocally}
              onChange={setEnforceRetentionLocally}
            />
          }
        />
      </SequenceCard>
      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile
          title="Translate Messages Into"
          description={
            translationSupported
              ? 'Translate messages written in other languages into this one. Runs entirely on your device — message text never leaves your computer or touches a cloud service.'
              : 'On-device translation isn’t available in this browser. Use a Chromium desktop browser (Chrome/Edge 138+) or the Lotus desktop app.'
          }
          after={
            <SettingsSelect
              value={selectedTargetLang}
              onChange={(v) => setTranslateTargetLang(v)}
              disabled={!translationSupported}
              aria-label="Translate messages into"
              options={TRANSLATE_TARGET_LANGUAGES.map((l) => ({ value: l.code, label: l.name }))}
            />
          }
        />
        {translationSupported && (
          <SettingTile
            title="Auto-translate Incoming Messages"
            description="Automatically translate messages that aren’t already in your language. When off, translate individual messages from their menu. The first time a language is used, its model downloads once (a few MB)."
            after={<Switch variant="Primary" value={autoTranslate} onChange={setAutoTranslate} />}
          />
        )}
      </SequenceCard>
    </Box>
  );
}

function AppUpdates() {
  const { isTauri, status, check, install } = useTauriUpdater();
  if (!isTauri) return null;

  const description =
    status.state === 'checking'
      ? 'Checking for updates...'
      : status.state === 'up-to-date'
        ? 'Lotus Chat is up to date.'
        : status.state === 'available'
          ? `Update available: v${status.version}`
          : status.state === 'installing'
            ? 'Installing update, the app will restart shortly...'
            : status.state === 'error'
              ? `Update check failed: ${status.message}`
              : 'Check for a new version of Lotus Chat.';

  const after =
    status.state === 'available' ? (
      <Button size="300" radii="300" onClick={install}>
        <Text size="B300">Install &amp; Restart</Text>
      </Button>
    ) : status.state === 'checking' || status.state === 'installing' ? (
      <Spinner variant="Secondary" size="200" />
    ) : (
      <Button size="300" radii="300" variant="Secondary" onClick={check}>
        <Text size="B300">Check</Text>
      </Button>
    );

  return (
    <Box direction="Column" gap="100">
      <Text size="L400">App Updates</Text>
      <SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
        <SettingTile title="Check for Updates" description={description} after={after} />
      </SequenceCard>
    </Box>
  );
}

type GeneralProps = {
  requestClose: () => void;
};
export function General({ requestClose }: GeneralProps) {
  return (
    <Page>
      <PageHeader outlined={false}>
        <Box grow="Yes" gap="200">
          <Box grow="Yes" alignItems="Center" gap="200">
            <Text size="H3" truncate>
              General
            </Text>
          </Box>
          <Box shrink="No">
            <IconButton onClick={requestClose} variant="Surface" aria-label="Close">
              <Icon src={Icons.Cross} />
            </IconButton>
          </Box>
        </Box>
      </PageHeader>
      <Box grow="Yes">
        <Scroll hideTrack visibility="Hover">
          <PageContent>
            <Box direction="Column" gap="700">
              <Appearance />
              <DateAndTime />
              <Editor />
              <Messages />
              <Privacy />
              <Calls />
              <AppUpdates />
            </Box>
          </PageContent>
        </Scroll>
      </Box>
    </Page>
  );
}
