import React, { useCallback, useEffect, useMemo, useState } from 'react';
import {
  Avatar,
  Box,
  Button,
  config,
  IconButton,
  Input,
  MenuItem,
  Overlay,
  OverlayBackdrop,
  OverlayCenter,
  Text,
} from 'folds';
import { Icon, Icons, IconSrc } from '../../components/icons';
import FocusTrap from 'focus-trap-react';
import { General } from './general';
import { PageNav, PageNavContent, PageNavHeader, PageRoot } from '../../components/page';
import { ScreenSize, useScreenSizeContext } from '../../hooks/useScreenSize';
import { Account } from './account';
import { useUserProfile } from '../../hooks/useUserProfile';
import { useMatrixClient } from '../../hooks/useMatrixClient';
import { getMxIdLocalPart, mxcUrlToHttp } from '../../utils/matrix';
import { useMediaAuthentication } from '../../hooks/useMediaAuthentication';
import { UserAvatar } from '../../components/user-avatar';
import { nameInitials } from '../../utils/common';
import { Notifications } from './notifications';
import { Devices } from './devices';
import { EmojisStickers } from './emojis-stickers';
import { DeveloperTools } from './developer-tools';
import { About } from './about';
import { Audio } from './audio';
import { Plugins } from './plugins';
import { UseStateProvider } from '../../components/UseStateProvider';
import { stopPropagation } from '../../utils/keyboard';
import { LogoutDialog } from '../../components/LogoutDialog';
import { fuzzyFilter } from '../../utils/fuzzy';
import { SETTINGS_SEARCH_CATALOG } from './settingsSearchCatalog';
import { SettingsPages } from './SettingsPages';
import { SettingsSearchResult } from './styles.css';
import { BreakWord } from '../../styles/Text.css';
import {
  SettingsFocusProvider,
  settingAnchorId,
} from '../../components/setting-tile';

export { SettingsPages };

type SettingsMenuItem = {
  page: SettingsPages;
  name: string;
  icon: IconSrc;
};

const useSettingsMenuItems = (): SettingsMenuItem[] =>
  useMemo(
    () => [
      {
        page: SettingsPages.GeneralPage,
        name: 'General',
        icon: Icons.Setting,
      },
      {
        page: SettingsPages.AccountPage,
        name: 'Account',
        icon: Icons.User,
      },
      {
        page: SettingsPages.NotificationPage,
        name: 'Notifications',
        icon: Icons.Bell,
      },
      {
        page: SettingsPages.AudioPage,
        name: 'Audio & Video',
        icon: Icons.Phone,
      },
      {
        page: SettingsPages.DevicesPage,
        name: 'Devices',
        icon: Icons.Monitor,
      },
      {
        page: SettingsPages.EmojisStickersPage,
        name: 'Emojis & Stickers',
        icon: Icons.Smile,
      },
      {
        page: SettingsPages.PluginsPage,
        name: 'Plugins',
        icon: Icons.Download,
      },
      {
        page: SettingsPages.DeveloperToolsPage,
        name: 'Developer Tools',
        icon: Icons.Terminal,
      },
      {
        page: SettingsPages.AboutPage,
        name: 'About',
        icon: Icons.Info,
      },
    ],
    []
  );

type SettingsProps = {
  initialPage?: SettingsPages;
  requestClose: () => void;
};
export function Settings({ initialPage, requestClose }: SettingsProps) {
  const mx = useMatrixClient();
  const useAuthentication = useMediaAuthentication();
  const userId = mx.getUserId()!;
  const profile = useUserProfile(userId);
  const displayName = profile.displayName ?? getMxIdLocalPart(userId) ?? userId;
  const avatarUrl = profile.avatarUrl
    ? mxcUrlToHttp(mx, profile.avatarUrl, useAuthentication, 96, 96, 'crop') ?? undefined
    : undefined;

  const screenSize = useScreenSizeContext();
  const [activePage, setActivePage] = useState<SettingsPages | undefined>(() => {
    if (initialPage) return initialPage;
    return screenSize === ScreenSize.Mobile ? undefined : SettingsPages.GeneralPage;
  });
  const [searchQuery, setSearchQuery] = useState('');
  const [focusAnchorId, setFocusAnchorId] = useState<string | undefined>();
  const menuItems = useSettingsMenuItems();
  const pageNameByPage = useMemo(() => {
    const map = new Map<SettingsPages, string>();
    menuItems.forEach((item) => map.set(item.page, item.name));
    return map;
  }, [menuItems]);
  const iconByPage = useMemo(() => {
    const map = new Map<SettingsPages, IconSrc>();
    menuItems.forEach((item) => map.set(item.page, item.icon));
    return map;
  }, [menuItems]);

  const searchResults = useMemo(() => {
    const q = searchQuery.trim();
    if (!q) return [];
    return fuzzyFilter(SETTINGS_SEARCH_CATALOG, q, (entry) => [
      entry.title,
      pageNameByPage.get(entry.page),
      ...(entry.keywords ?? []),
    ]).slice(0, 40);
  }, [pageNameByPage, searchQuery]);

  const settingsFocus = useMemo(
    () => ({
      anchorId: focusAnchorId,
      setAnchorId: setFocusAnchorId,
    }),
    [focusAnchorId]
  );

  const handlePageRequestClose = () => {
    if (screenSize === ScreenSize.Mobile) {
      setActivePage(undefined);
      return;
    }
    requestClose();
  };

  const openSearchResult = useCallback(
    (page: SettingsPages, title: string) => {
      const pageName = pageNameByPage.get(page);
      // Page-level entries just open the page; tile entries scroll into view.
      if (title !== pageName) {
        setFocusAnchorId(settingAnchorId(title));
      } else {
        setFocusAnchorId(undefined);
      }
      setActivePage(page);
      setSearchQuery('');
    },
    [pageNameByPage]
  );

  // Fallback scroll if the page just mounted and tiles need a moment to render.
  useEffect(() => {
    if (!focusAnchorId) return;
    let cancelled = false;
    let attempts = 0;

    const tryScroll = () => {
      if (cancelled) return;
      const el = document.getElementById(focusAnchorId);
      if (el) {
        el.scrollIntoView({ behavior: 'smooth', block: 'center' });
        return;
      }
      if (attempts < 40) {
        attempts += 1;
        window.requestAnimationFrame(tryScroll);
      } else {
        setFocusAnchorId(undefined);
      }
    };

    const timer = window.setTimeout(tryScroll, 50);
    return () => {
      cancelled = true;
      window.clearTimeout(timer);
    };
  }, [focusAnchorId, activePage]);

  const isSearching = searchQuery.trim().length > 0;

  return (
    <SettingsFocusProvider value={settingsFocus}>
      <PageRoot
        nav={
          screenSize === ScreenSize.Mobile && activePage !== undefined ? undefined : (
            <PageNav size="300">
              <PageNavHeader outlined={false}>
                <Box grow="Yes" gap="200">
                  <Avatar size="200" radii="300">
                    <UserAvatar
                      userId={userId}
                      src={avatarUrl}
                      renderFallback={() => <Text size="H6">{nameInitials(displayName)}</Text>}
                    />
                  </Avatar>
                  <Text size="H4" truncate>
                    Settings
                  </Text>
                </Box>
                <Box shrink="No">
                  {screenSize === ScreenSize.Mobile && (
                    <IconButton onClick={requestClose} variant="Background">
                      <Icon src={Icons.Cross} />
                    </IconButton>
                  )}
                </Box>
              </PageNavHeader>
              <Box grow="Yes" direction="Column">
                <Box
                  style={{ padding: `${config.space.S100} ${config.space.S200}`, width: '100%' }}
                  shrink="No"
                >
                  <Input
                    style={{ width: '100%' }}
                    variant="Background"
                    size="400"
                    placeholder="Search"
                    value={searchQuery}
                    onChange={(evt) => setSearchQuery(evt.target.value)}
                    before={<Icon size="100" src={Icons.Search} />}
                    after={
                      searchQuery ? (
                        <IconButton
                          size="300"
                          variant="Background"
                          radii="300"
                          onClick={() => setSearchQuery('')}
                          aria-label="Clear search"
                        >
                          <Icon size="50" src={Icons.Cross} />
                        </IconButton>
                      ) : undefined
                    }
                  />
                </Box>
                <PageNavContent>
                  <div style={{ flexGrow: 1 }}>
                    {isSearching ? (
                      searchResults.length > 0 ? (
                        searchResults.map(({ item }) => (
                          <button
                            key={`${item.page}-${item.title}`}
                            type="button"
                            className={SettingsSearchResult}
                            aria-pressed={activePage === item.page}
                            onClick={() => openSearchResult(item.page, item.title)}
                          >
                            <Box shrink="No" style={{ paddingTop: config.space.S100 }}>
                              <Icon
                                src={iconByPage.get(item.page) ?? Icons.Setting}
                                size="100"
                                filled={activePage === item.page}
                              />
                            </Box>
                            <Box grow="Yes" direction="Column" gap="100" style={{ minWidth: 0 }}>
                              <Text
                                className={BreakWord}
                                size="T300"
                                style={{
                                  fontWeight:
                                    activePage === item.page ? config.fontWeight.W600 : undefined,
                                }}
                              >
                                {item.title}
                              </Text>
                              <Text size="T200" priority="300">
                                {pageNameByPage.get(item.page)}
                              </Text>
                            </Box>
                          </button>
                        ))
                      ) : (
                        <Box
                          direction="Column"
                          alignItems="Center"
                          style={{ padding: config.space.S400 }}
                        >
                          <Text size="T300" priority="300" className={BreakWord}>
                            No settings match “{searchQuery.trim()}”
                          </Text>
                        </Box>
                      )
                    ) : (
                      menuItems.map((item) => (
                        <MenuItem
                          key={item.name}
                          variant="Background"
                          radii="400"
                          aria-pressed={activePage === item.page}
                          before={
                            <Icon src={item.icon} size="100" filled={activePage === item.page} />
                          }
                          onClick={() => setActivePage(item.page)}
                        >
                          <Text
                            style={{
                              fontWeight:
                                activePage === item.page ? config.fontWeight.W600 : undefined,
                            }}
                            size="T300"
                            truncate
                          >
                            {item.name}
                          </Text>
                        </MenuItem>
                      ))
                    )}
                  </div>
                </PageNavContent>
                <Box style={{ padding: config.space.S200 }} shrink="No" direction="Column">
                  <UseStateProvider initial={false}>
                    {(logout, setLogout) => (
                      <>
                        <Button
                          size="300"
                          variant="Critical"
                          fill="None"
                          radii="Pill"
                          before={<Icon src={Icons.Power} size="100" />}
                          onClick={() => setLogout(true)}
                        >
                          <Text size="B400">Logout</Text>
                        </Button>
                        {logout && (
                          <Overlay open backdrop={<OverlayBackdrop />}>
                            <OverlayCenter>
                              <FocusTrap
                                focusTrapOptions={{
                                  onDeactivate: () => setLogout(false),
                                  clickOutsideDeactivates: true,
                                  escapeDeactivates: stopPropagation,
                                }}
                              >
                                <LogoutDialog handleClose={() => setLogout(false)} />
                              </FocusTrap>
                            </OverlayCenter>
                          </Overlay>
                        )}
                      </>
                    )}
                  </UseStateProvider>
                </Box>
              </Box>
            </PageNav>
          )
        }
      >
        {activePage === SettingsPages.GeneralPage && (
          <General requestClose={handlePageRequestClose} />
        )}
        {activePage === SettingsPages.AccountPage && (
          <Account requestClose={handlePageRequestClose} />
        )}
        {activePage === SettingsPages.NotificationPage && (
          <Notifications requestClose={handlePageRequestClose} />
        )}
        {activePage === SettingsPages.AudioPage && (
          <Audio requestClose={handlePageRequestClose} />
        )}
        {activePage === SettingsPages.DevicesPage && (
          <Devices requestClose={handlePageRequestClose} />
        )}
        {activePage === SettingsPages.EmojisStickersPage && (
          <EmojisStickers requestClose={handlePageRequestClose} />
        )}
        {activePage === SettingsPages.PluginsPage && (
          <Plugins requestClose={handlePageRequestClose} />
        )}
        {activePage === SettingsPages.DeveloperToolsPage && (
          <DeveloperTools requestClose={handlePageRequestClose} />
        )}
        {activePage === SettingsPages.AboutPage && <About requestClose={handlePageRequestClose} />}
      </PageRoot>
    </SettingsFocusProvider>
  );
}
