import { useCallback, useMemo, useState } from 'react';
import { AppShell, Box, Drawer, Group, Text } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
import { Outlet, useLocation, useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { BrandMark, logout, useIdleTimer } from '@ema-platform/auth';
import { AppHeader, AppSidebar } from '@ema-platform/ui';
import type { NavItem, NavSection } from '@ema-platform/ui';
import { notify } from '@ema-platform/ui';
import { AppTopNav, filterByPermissions } from '@ema-platform/ui';
import { SkipLink, MAIN_CONTENT_ID } from '@ema-platform/ui';
import { baseApi, useGetQueueCountsQuery } from '@ema-platform/api';
import { usePermissions } from '@ema-platform/auth';
import { SUPPORTED_LANGUAGES } from '../i18n/config';
import { useAppDispatch, useAppSelector } from '../store/hooks';
import { NAV_SECTIONS } from './nav-config';
import { CommandPalette } from './CommandPalette';

/**
 * How often the pending-work badges refresh.
 *
 * Polled on a timer rather than refetched per navigation: the counts sit in
 * the chrome and are visible on every screen, so tying them to route changes
 * would fire a request each time an officer clicked anything.
 */
const BADGE_POLL_MS = 60_000;

const HEADER_HEIGHT = 116;

/**
 * Horizontal inset of the header chrome. `AppHeader` adds its own `px="lg"`
 * inside this, so the nav strip below needs the sum to line up with the
 * controls above it — it used to start 20px to their left.
 */
const CHROME_PAD_X = 32;
const NAV_STRIP_PAD_X = CHROME_PAD_X + 20;

/**
 * A desk left unlocked with a license-review or medical-record screen open is
 * the actual threat model here, not a slow token. 15 minutes of no mouse,
 * key, scroll, or touch activity signs the officer out automatically.
 */
const IDLE_TIMEOUT_MS = 15 * 60 * 1000;

export function BackofficeLayout() {
  const { t } = useTranslation();
  const navigate = useNavigate();
  const location = useLocation();
  const dispatch = useAppDispatch();
  const [opened, { toggle: toggleNav, close: closeNav }] = useDisclosure();
  const [collapsed, setCollapsed] = useState(false);
  const user = useAppSelector((state) => state.auth.user);
  const layoutMode = useAppSelector((state) => state.preferences.layoutMode);
  const { permissions: granted, known } = usePermissions();

  // Badges reflect real pending work. One grouped request on a timer, shared
  // by the sidebar and the top bar via the RTK cache.
  const { data: counts } = useGetQueueCountsQuery(undefined, {
    pollingInterval: BADGE_POLL_MS,
    refetchOnMountOrArgChange: false,
  });

  const sections = useMemo<NavSection[]>(() => {
    const withBadges = NAV_SECTIONS.map((section) => ({
      ...section,
      items: section.items.map((item) =>
        item.to === '/licence-review' && counts?.unassigned
          ? { ...item, badge: counts.unassigned }
          : item,
      ),
    }));
    // Unfiltered until the grant list has loaded, matching PortalLayout and
    // RequirePermission: a moment of extra nav beats a flash of empty nav.
    return known ? filterByPermissions(withBadges, granted) : withBadges;
  }, [counts?.unassigned, granted, known]);

  /** Flat list used for breadcrumbs and active-route lookup. */
  const navItems = useMemo<NavItem[]>(
    () =>
      sections.flatMap((section) =>
        section.items.flatMap((item) => [item, ...(item.children ?? [])]),
      ),
    [sections],
  );

  const displayName = user?.name?.en || user?.username || '';
  const initials = displayName
    ? displayName
        .split(/\s+/)
        .map((s) => s[0])
        .join("")
        .toUpperCase()
        .slice(0, 2)
    : "?";

  const handleLogout = useCallback(() => {
    dispatch(logout());
    dispatch(baseApi.util.resetApiState());
    navigate("/");
  }, [dispatch, navigate]);

  useIdleTimer(IDLE_TIMEOUT_MS, () => {
    notify.info(
      t(
        'common.idleLogoutMessage',
        'You were signed out after 15 minutes of inactivity.',
      ),
      t('common.idleLogoutTitle', 'Session ended'),
    );
    handleLogout();
  });

  const segments = location.pathname.split('/').filter(Boolean);
  // Label each crumb from the nav item it corresponds to, falling back to a
  // readable form of the path segment. Every crumb was previously labelled
  // "Dashboard", which made the trail useless.
  const crumbs = [
    { label: t("nav.dashboard"), path: "/dashboard" },
    ...segments
      .map((_, i) => '/' + segments.slice(0, i + 1).join('/'))
      .filter((path) => path !== '/dashboard')
      .filter((path) => !path.startsWith('/um'))
      .map((path) => {
        const match = navItems.find((item) => item.to === path);
        if (match) return { label: t(match.label), path };
        const segment = path.split('/').pop() ?? '';
        // Ids get a generic label rather than a raw uuid in the trail.
        const isId = /^[0-9a-f-]{8,}$/i.test(segment) || /^\d+$/.test(segment);
        return {
          label: isId
            ? t('nav.details', 'Details')
            : segment.replace(/-/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase()),
          path,
        };
      }),
  ];

  const go = (item: NavItem) => {
    if (item.soon) {
      notify.info(`${t(item.label)} — coming soon.`);
      return;
    }
    if (item.to) {
      navigate(item.to);
    }
  };

  const handleToggleCollapse = useCallback(() => {
    setCollapsed((prev) => !prev);
  }, []);

  const isSidebar = layoutMode === "sidebar";

  return (
    <>
      {/* First focusable element on the page, so a keyboard user can bypass
          the 20-plus nav items instead of tabbing through them every time. */}
      <SkipLink />
    <AppShell
      // The top layout drops its nav strip on small screens — the drawer is
      // the nav there — so the header shrinks back to a single row with it.
      header={{ height: isSidebar ? 74 : { base: 74, sm: HEADER_HEIGHT } }}
      navbar={
        isSidebar
          ? {
              width: collapsed ? 72 : 264,
              breakpoint: "sm",
              // Mobile has its own Drawer below — AppShell's built-in mobile
              // navbar takes over the full viewport width, which felt like
              // it swallowed the page. Always collapsed here on mobile.
              collapsed: { mobile: true },
            }
          : undefined
      }
      padding="lg"
    >
      <AppShell.Header
        style={{
          background: "var(--mantine-color-body)",
          borderBottom: "1px solid var(--mantine-color-default-border)",
          display: "flex",
          flexDirection: "column",
        }}
      >
        <div
          style={{ height: 74, flexShrink: 0, padding: `0 ${CHROME_PAD_X}px` }}
        >
          <AppHeader
            brand={
              isSidebar ? undefined : (
                <Group gap="xs" wrap="nowrap">
                  <BrandMark size={28} />
                  <Text fw={700} size="sm" lh={1.1} visibleFrom="xs">
                    {t('app.name')}
                  </Text>
                </Group>
              )
            }
            // Nothing to toggle on a desktop top bar; on mobile it opens the
            // drawer below.
            burgerHiddenFrom={isSidebar ? undefined : 'sm'}
            onToggleNav={toggleNav}
            onToggleSidebar={isSidebar ? handleToggleCollapse : toggleNav}
            navOpened={opened}
            breadcrumbs={crumbs}
            onNavigate={navigate}
            onLogout={handleLogout}
            userName={displayName || t("app.name")}
            userInitials={initials}
            supportedLanguages={SUPPORTED_LANGUAGES}
          />
        </div>

        {!isSidebar && (
          <Box
            visibleFrom="sm"
            style={{
              display: 'flex',
              alignItems: 'center',
              padding: `0 ${NAV_STRIP_PAD_X}px`,
              height: 42,
              borderTop: '1px solid var(--mantine-color-default-border)',
              flexShrink: 0,
            }}
          >
            {/* Grouped dropdowns. Previously every destination rendered as a
                sibling button in one horizontally scrolling row. */}
            <AppTopNav
              navItems={sections}
              activePath={location.pathname}
              onNavigate={go}
            />
          </Box>
        )}
      </AppShell.Header>

      {isSidebar && (
        <AppShell.Navbar
          p={0}
          style={{
            overflow: "hidden",
            transition: "width 200ms ease",
            background: "var(--mantine-color-body)",
            borderRight: "1px solid var(--mantine-color-default-border)",
          }}
        >
          <AppSidebar
            navItems={sections}
            collapsed={collapsed}
            activePath={location.pathname}
            onToggleCollapse={handleToggleCollapse}
            onNavigate={go}
            brandName={t('app.name')}
            brandSubtitle={t('app.authority')}
            brandLogo={<BrandMark size={32} />}
          />
        </AppShell.Navbar>
      )}

      <AppShell.Main id={MAIN_CONTENT_ID}>
        <div key={location.pathname} className="ema-page-enter">
          <Outlet />
        </div>
      </AppShell.Main>

      {/* Registered once for the whole backoffice; opens on ⌘K anywhere. */}
      <CommandPalette sections={sections} />

      {/* Mobile nav: a proper Drawer (sized, backdrop, closes on outside
          click) instead of AppShell's full-width mobile navbar. Mirrors the
          landing page's mobile menu. */}
      <Drawer
        opened={opened}
        onClose={closeNav}
        hiddenFrom="sm"
        size="75%"
        padding={0}
        withCloseButton={false}
      >
        <AppSidebar
          navItems={sections}
          collapsed={false}
          activePath={location.pathname}
          onToggleCollapse={handleToggleCollapse}
          onNavigate={(item) => {
            go(item);
            closeNav();
          }}
          brandName={t('app.name')}
          brandSubtitle={t('app.authority')}
          brandLogo={<BrandMark size={32} />}
        />
      </Drawer>
    </AppShell>
    </>
  );
}
