import { useEffect, useState } from 'react';
import { NavLink, Outlet, useLocation } from 'react-router-dom';
import { Icon } from './Icon';
import { OmniSearch } from './OmniSearch';
import { NotificationBell } from './NotificationBell';
import { nav } from '../nav';
import { useNotifications } from '../lib/notifications';
import { useGateway } from '../lib/gateway';
import { time } from '../lib/format';
import { Confirm } from './ui';

/* The shell: a top bar spanning the width, a rail down the left, and the page.
 *
 * Everything in the bar is true of the console rather than of any one screen — which
 * gateway this is, whether it is answering, the page search, the activity bell and the way
 * out. Everything in the rail is a destination. Nothing else lives in either. */

function Rail({ open, onNavigate }: { open: boolean; onNavigate: () => void }) {
  const { unread } = useNotifications();
  const location = useLocation();

  // A group holding the current page is open regardless of what was collapsed last time:
  // a rail that hides the page you are on is a rail that has lost its place.
  const [collapsed, setCollapsed] = useState<Record<string, boolean>>(() => {
    try {
      return JSON.parse(localStorage.getItem('memby-admin-nav') ?? '{}') as Record<string, boolean>;
    } catch {
      return {};
    }
  });

  const toggle = (id: string) => {
    setCollapsed((current) => {
      const next = { ...current, [id]: !current[id] };
      try {
        localStorage.setItem('memby-admin-nav', JSON.stringify(next));
      } catch {
        // Private-mode storage refusals cost the memory of which groups were open, and
        // nothing else.
      }
      return next;
    });
  };

  return (
    <nav className="rail" id="rail" data-open={open || undefined} aria-label="Console sections">
      {nav.map((group) => {
        const items = group.items.filter((item) => !item.hidden);
        if (items.length === 0) return null;
        const holdsCurrent = items.some((item) => location.pathname === item.path);
        const expanded = holdsCurrent || !collapsed[group.id];
        return (
          <div className="rail-group" key={group.id}>
            {group.label ? (
              <button
                type="button"
                className="rail-head"
                aria-expanded={expanded}
                onClick={() => toggle(group.id)}
              >
                {group.label}
                <Icon name="caret" className="ico caret" />
              </button>
            ) : null}
            {expanded
              ? items.map((item) => (
                  <NavLink
                    key={item.id}
                    to={item.path}
                    end={item.path === '/admin'}
                    onClick={onNavigate}
                    className={({ isActive }) => (isActive ? 'on' : '')}
                    aria-current={undefined}
                  >
                    {({ isActive }) => (
                      <span
                        style={{ display: 'contents' }}
                        // NavLink's own aria-current lands on the anchor; this mirrors it
                        // onto the element the stylesheet selects.
                        ref={(node) => {
                          const anchor = node?.parentElement;
                          if (anchor) {
                            if (isActive) anchor.setAttribute('aria-current', 'page');
                            else anchor.removeAttribute('aria-current');
                          }
                        }}
                      >
                        {item.icon ? <Icon name={item.icon} /> : null}
                        {item.label}
                        {item.badge === 'notifications' && unread > 0 ? (
                          <span className="rail-badge">{unread > 99 ? '99+' : unread}</span>
                        ) : null}
                      </span>
                    )}
                  </NavLink>
                ))
              : null}
          </div>
        );
      })}
    </nav>
  );
}

export function Layout() {
  const { version, online, checkedAt, status, setMaintenance } = useGateway();
  const [railOpen, setRailOpen] = useState(false);
  const [changingAvailability, setChangingAvailability] = useState(false);
  const [confirmingOffline, setConfirmingOffline] = useState(false);
  const location = useLocation();
  const offline = Boolean(status?.maintenance?.enabled);

  const toggleAvailability = async () => {
    if (changingAvailability || !status) return;
    setChangingAvailability(true);
    try {
      await setMaintenance(!offline);
    } finally {
      setChangingAvailability(false);
    }
  };

  // The rail is an overlay on a narrow screen, so a navigation has to close it — otherwise
  // it sits over the page that was just opened.
  useEffect(() => setRailOpen(false), [location.pathname]);

  return (
    <>
      <a className="skip" href="#main">
        Skip to content
      </a>

      <header className="topbar">
        <a className="topbar-brand" href="/admin">
          <span className="brand-mark" aria-hidden="true">
            M
          </span>
          <span className="brand-word">Memby Gateway</span>
        </a>
        <button
          type="button"
          className="rail-toggle"
          aria-label="Sections"
          aria-expanded={railOpen}
          aria-controls="rail"
          onClick={() => setRailOpen((current) => !current)}
        >
          <Icon name="menu" />
        </button>

        <div className="topbar-spacer" />

        <div className="topbar-tools">
          <OmniSearch />
          <span className="topbar-version">gateway {version || 'unknown'}</span>
          {/* This is both a live gateway verdict and its operator-controlled availability.
              The switch is intentionally in the shared header: taking the household
              offline should not require leaving the page where the operator noticed it. */}
          <button
            type="button"
            className="topbar-status"
            data-tone={online && !offline ? 'ok' : 'bad'}
            aria-pressed={offline}
            disabled={!status || changingAvailability}
            title={offline ? 'Bring Memby back online' : 'Take Memby offline'}
            onClick={() => offline ? void toggleAvailability() : setConfirmingOffline(true)}
          >
            <span className="dot" />
            <b>{offline ? 'offline' : online ? 'online' : 'not responding'}</b>
            <span>{offline ? 'click to bring back online' : checkedAt ? `updated ${time(checkedAt)}` : ''}</span>
          </button>
          <NotificationBell />
          <form method="post" action="/admin/logout">
            <button type="submit" data-variant="quiet" data-size="sm" title="Sign out">
              <Icon name="logout" />
            </button>
          </form>
        </div>
      </header>

      <Rail open={railOpen} onNavigate={() => setRailOpen(false)} />

      <main className="page" id="main">
        <Outlet />
      </main>
      {confirmingOffline ? (
        <Confirm
          title="Take Memby offline?"
          body="Every television will stop working immediately. Viewers will see the maintenance message configured on the Maintenance page, while this console remains available."
          confirmLabel="Go offline"
          destructive
          busy={changingAvailability}
          onConfirm={() => {
            setConfirmingOffline(false);
            void toggleAvailability();
          }}
          onCancel={() => setConfirmingOffline(false)}
        />
      ) : null}
    </>
  );
}
