import React, {
  ChangeEventHandler,
  FormEventHandler,
  KeyboardEventHandler,
  MouseEventHandler,
  useCallback,
  useEffect,
  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 { 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 {
  ChatBackground,
  ComposerToolbarSettings,
  DateFormat,
  DenoiseModelId,
  MessageLayout,
  MessageSpacing,
  NoiseSuppressionMode,
  RingtoneId,
  Settings,
  settingsAtom,
} from '../../../state/settings';
import { SeasonalPreview, SeasonTheme } from '../../../components/seasonal/SeasonalEffect';
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 { BG_OPTIONS, getChatBg } from '../../lotus/chatBackground';
import { resetBootSequence, runLotusBootSequence } from '../../../../lotus-boot';
import { useMessageLayoutItems } from '../../../hooks/useMessageLayout';
import { useMessageSpacingItems } from '../../../hooks/useMessageSpacing';
import { SequenceCardStyle } from '../styles.css';
import { useTauriUpdater } from '../../../hooks/useTauriUpdater';
import { useDateFormatItems } from '../../../hooks/useDateFormat';
import { playCallJoinSound } from '../../../utils/callSounds';
import { previewRingtone, RINGTONE_OPTIONS } from '../../../utils/ringtones';
import { DenoiseTester } from './DenoiseTester';

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>
        }
      />
    </>
  );
}

type SettingsSelectOption<T extends string> = { value: T; label: string; disabled?: boolean };

function SettingsSelect<T extends string>({
  value,
  options,
  onChange,
}: {
  value: T;
  options: SettingsSelectOption<T>[];
  onChange: (v: T) => void;
}) {
  const [menuCords, setMenuCords] = useState<RectCords>();
  const selectedLabel = options.find((o) => o.value === value)?.label ?? value;

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

  const handleSelect = (v: T) => {
    onChange(v);
    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">{selectedLabel}</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 }}>
                {options.map((opt) => (
                  <MenuItem
                    key={opt.value}
                    size="300"
                    variant={opt.value === value ? 'Primary' : 'Surface'}
                    radii="300"
                    disabled={opt.disabled}
                    onClick={() => !opt.disabled && handleSelect(opt.value)}
                  >
                    <Text size="T300">{opt.label}</Text>
                  </MenuItem>
                ))}
              </Box>
            </Menu>
          </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 [glassmorphismSidebar, setGlassmorphismSidebar] = useSetting(
    settingsAtom,
    'glassmorphismSidebar',
  );
  const [pauseAnimations, setPauseAnimations] = useSetting(settingsAtom, 'pauseAnimations');
  const [mentionHighlightColor, setMentionHighlightColor] = useSetting(
    settingsAtom,
    'mentionHighlightColor',
  );
  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>

      <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. Preview below — click to select."
        />
        <Box style={{ padding: `0 ${config.space.S400} ${config.space.S300}` }}>
          <SeasonalBgGrid
            value={seasonalThemeOverride ?? 'auto'}
            onChange={(v) => {
              setSeasonalThemeOverride(v);
              if (v !== 'auto' && 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="100"
            style={{ padding: `0 ${config.space.S400} ${config.space.S300}` }}
          >
            <Text size="T200" priority="300">
              Intensity: {nightLightOpacity}%
            </Text>
            <input
              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>
        )}
      </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>
    </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>
  );
}

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 toggleToolbarButton = (key: keyof ComposerToolbarSettings) => {
    setComposerToolbarButtons({ ...composerToolbarButtons, [key]: !composerToolbarButtons[key] });
  };

  const TOOLBAR_CHIPS: Array<{ key: keyof ComposerToolbarSettings; label: string }> = [
    { key: 'showFormat', label: 'Format' },
    { key: 'showEmoji', label: 'Emoji' },
    { key: 'showSticker', label: 'Sticker' },
    { key: 'showGif', label: 'GIF' },
    { key: 'showLocation', label: 'Location' },
    { key: 'showPoll', label: 'Poll' },
    { key: 'showVoice', label: 'Voice' },
    { key: 'showSchedule', label: 'Schedule' },
  ];

  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="Tap a button to show or hide it in the message composer."
        />
        <Box
          wrap="Wrap"
          gap="200"
          style={{ padding: `0 ${config.space.S400} ${config.space.S300}` }}
        >
          {TOOLBAR_CHIPS.map(({ key, label }) => {
            const active = composerToolbarButtons?.[key] ?? true;
            return (
              <Chip
                key={key}
                variant={active ? 'Primary' : 'Secondary'}
                outlined={active}
                radii="Pill"
                onClick={() => toggleToolbarButton(key)}
                aria-pressed={active}
              >
                <Text size="T300">{label}</Text>
              </Chip>
            );
          })}
        </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 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: '1px solid var(--border-color)',
              background: 'var(--bg-card)',
            }}
          >
            {/* ── 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: '1px solid var(--border-color)',
                    background: 'var(--bg-input)',
                  }}
                >
                  <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: '1px solid var(--border-color)',
                      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: '1px solid var(--border-color)' }}
            >
              <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
                    type="range"
                    min="-100"
                    max="0"
                    step="1"
                    value={callDenoiseGateThreshold}
                    onChange={(e) => setCallDenoiseGateThreshold(parseInt(e.target.value, 10))}
                    style={{ width: '100%', accentColor: 'var(--accent-orange)' }}
                  />
                </Box>
              )}
            </Box>

            {/* ── Test & calibrate ──────────────────────────────────────── */}
            <Box
              direction="Column"
              gap="200"
              style={{ paddingTop: '12px', borderTop: '1px solid var(--border-color)' }}
            >
              <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."
          after={
            <Box direction="Row" alignItems="Center" gap="200" style={{ minWidth: '160px' }}>
              <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: 'var(--accent-orange)' }}
              />
              <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>
    </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 wrap="Wrap" gap="200">
      {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>
          </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;

  return (
    <Box wrap="Wrap" gap="200">
      {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),
            }}
          />
          <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 [showHiddenEvents, setShowHiddenEvents] = useSetting(settingsAtom, 'showHiddenEvents');

  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="Show Hidden Events"
          after={
            <Switch variant="Primary" value={showHiddenEvents} onChange={setShowHiddenEvents} />
          }
        />
      </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>
  );
}
