import {
  Fragment,
  useEffect,
  useRef,
  useState,
  type KeyboardEvent,
  type MouseEvent,
  type ReactNode
} from 'react';
import {
  Bell,
  CalendarClock,
  Cast,
  Check,
  ChevronDown,
  CircleHelp,
  ClipboardCopy,
  Info,
  ListVideo,
  PanelLeftClose,
  PanelLeftOpen,
  Plus,
  Search
} from 'lucide-react';
import chicoryMarkUrl from '../../../design-system/assets/chicory-mark.svg';
import {
  useChannelsQuery,
  useDashboardVersionQuery,
  type DashboardHealthQueryState
} from '../api';
import {
  Button,
  IconButton,
  NavItem
} from '../components';
import {
  designSystemThemes,
  type DesignSystemThemeId
} from '../designSystem';
import { usePrimaryActionHandler } from '../primaryAction';
import { navigateToPath } from '../routing';
import { useSidebarState } from './sidebarState';
import { DashboardHealthSummary } from '../screens/DashboardScreen';
import { UnauthorizedBanner } from '../UnauthorizedBanner';
import { UserMenu } from '../UserMenu';
import {
  routeById,
  routeHref,
  sidebarNavGroups,
  type ScreenId,
  type ScreenRoute
} from './routes';

type NavigateHandler = (route: ScreenRoute, event: MouseEvent) => void;

function SidebarNavGroup({
  activeRoute,
  collapsed,
  ids,
  onNavigate
}: {
  activeRoute: ScreenRoute | null;
  collapsed: boolean;
  ids: readonly ScreenId[];
  onNavigate: NavigateHandler;
}) {
  return (
    <>
      {ids.map((id) => {
        const route = routeById.get(id)!;

        return (
          <NavItem
            key={route.id}
            icon={route.icon}
            label={route.label}
            // In the collapsed rail, the label is visually hidden (kept for a11y) so surface it
            // as a native tooltip; expanded shows the label inline so a tooltip would be redundant.
            title={collapsed ? route.label : undefined}
            active={route.id === activeRoute?.id}
            badge={route.badge}
            badgeTone="warn"
            href={routeHref(route)}
            onClick={(event) => onNavigate(route, event)}
          />
        );
      })}
    </>
  );
}

function ThemeSwitcher({
  theme,
  onThemeChange
}: {
  theme: DesignSystemThemeId;
  onThemeChange: (theme: DesignSystemThemeId) => void;
}) {
  return (
    <section className="ctv-theme-switcher" aria-label="Theme">
      <span>Theme</span>
      <div>
        {designSystemThemes.map(({ id, description, label }) => (
          <button
            key={id}
            type="button"
            className={`ctv-theme-swatch ctv-theme-swatch-${id}`}
            aria-label={description}
            aria-pressed={theme === id}
            title={label}
            onClick={() => onThemeChange(id)}
          />
        ))}
      </div>
    </section>
  );
}

function Sidebar({
  activeRoute,
  collapsed,
  healthState,
  isGroupCollapsed,
  onNavigate,
  onToggleCollapsed,
  onToggleGroup
}: {
  activeRoute: ScreenRoute | null;
  collapsed: boolean;
  healthState: DashboardHealthQueryState;
  isGroupCollapsed: (groupKey: string) => boolean;
  onNavigate: NavigateHandler;
  onToggleCollapsed: () => void;
  onToggleGroup: (groupKey: string) => void;
}) {
  return (
    <aside className="ctv-sidebar">
      <div className="ctv-brand">
        <img src={chicoryMarkUrl} alt="ChicoryTV" />
        <span className="ctv-brand-wordmark">
          Chicory<span>TV</span>
        </span>
        <button
          type="button"
          className="ctv-sidebar-toggle"
          aria-label={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}
          aria-pressed={collapsed}
          title={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}
          onClick={onToggleCollapsed}
        >
          {collapsed ? (
            <PanelLeftOpen aria-hidden="true" size={17} />
          ) : (
            <PanelLeftClose aria-hidden="true" size={17} />
          )}
        </button>
      </div>

      <nav aria-label="Primary" className="ctv-nav">
        {sidebarNavGroups.map((group) => {
          const { ids, key, label } = group;

          // Primary (unlabeled) is always open, in both the rail and the expanded sidebar.
          if (!label) {
            return (
              <SidebarNavGroup
                key="Primary"
                activeRoute={activeRoute}
                collapsed={collapsed}
                ids={ids}
                onNavigate={onNavigate}
              />
            );
          }

          const groupKey = key ?? label;
          // Accordions only apply in the expanded sidebar; the rail always shows every item
          // (separated by a hairline divider), so group-collapse is ignored there.
          const groupCollapsed = isGroupCollapsed(groupKey);
          // Always reveal the group that contains the active route so its active item is visible +
          // highlighted in the expanded sidebar, even when the group's stored state is collapsed
          // (e.g. a fresh deep-link to /app/settings). This does NOT persist — navigating away
          // reverts to the stored preference (acceptance: "active route marked in both states").
          const containsActive =
            activeRoute != null && ids.includes(activeRoute.id);
          const expanded = containsActive || !groupCollapsed;
          const showItems = collapsed || expanded;

          return (
            <Fragment key={groupKey}>
              {collapsed ? (
                <div className="ctv-nav-divider" role="presentation" />
              ) : (
                <button
                  type="button"
                  className="ctv-nav-group-header"
                  aria-expanded={expanded}
                  onClick={() => onToggleGroup(groupKey)}
                >
                  <span>{label}</span>
                  <ChevronDown className="ctv-nav-group-chevron" aria-hidden="true" size={14} />
                </button>
              )}
              {showItems ? (
                <SidebarNavGroup
                  activeRoute={activeRoute}
                  collapsed={collapsed}
                  ids={ids}
                  onNavigate={onNavigate}
                />
              ) : null}
            </Fragment>
          );
        })}
      </nav>

      <div className="ctv-sidebar-health">
        <div>
          <span>ChicoryTV</span>
          <SidebarVersion />
        </div>
        <DashboardHealthSummary healthState={healthState} />
      </div>
    </aside>
  );
}

function SidebarVersion() {
  const versionQuery = useDashboardVersionQuery();

  if (versionQuery.status === 'success') {
    return <code>{versionQuery.version.appVersion ?? 'version unknown'}</code>;
  }

  if (versionQuery.status === 'error') {
    return <code>version unavailable</code>;
  }

  return <code>loading version</code>;
}

function EndpointRow({ icon, label, url }: { icon: ReactNode; label: string; url: string }) {
  const [copied, setCopied] = useState(false);

  const copyUrl = () => {
    void navigator.clipboard?.writeText(url);
    setCopied(true);
    window.setTimeout(() => setCopied(false), 1200);
  };

  return (
    <div className="ctv-connect-endpoint">
      <span className="ctv-connect-endpoint-icon">{icon}</span>
      <div>
        <strong>{label}</strong>
        <code>{url}</code>
      </div>
      <button type="button" onClick={copyUrl}>
        {copied ? <Check aria-hidden="true" size={13} /> : <ClipboardCopy aria-hidden="true" size={13} />}
        {copied ? 'Copied' : 'Copy'}
      </button>
    </div>
  );
}

function ConnectMenu() {
  const [open, setOpen] = useState(false);
  const menuRef = useRef<HTMLDivElement>(null);
  const channelsQuery = useChannelsQuery();
  const playlistUrl = `${window.location.origin}/iptv/channels.m3u`;
  const guideUrl = `${window.location.origin}/iptv/xmltv.xml`;

  useEffect(() => {
    if (open) {
      menuRef.current?.focus();
    }
  }, [open]);

  const onMenuKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
    if (event.key === 'Escape') {
      setOpen(false);
    }
  };

  const channelCountLabel = (() => {
    if (channelsQuery.status === 'loading') {
      return 'Loading channels';
    }

    if (channelsQuery.status === 'error') {
      return 'Channels unavailable';
    }

    const count = channelsQuery.channels.length;
    return `${count} ${count === 1 ? 'channel' : 'channels'}`;
  })();

  return (
    <div className="ctv-connect">
      <button
        type="button"
        className="ctv-connect-button"
        aria-expanded={open}
        onClick={() => setOpen((current) => !current)}
      >
        <Cast aria-hidden="true" size={15} />
        Connect
        <ChevronDown aria-hidden="true" size={13} />
      </button>

      {open && (
        <>
          <button
            type="button"
            className="ctv-connect-dismiss"
            aria-label="Close Connect menu"
            onClick={() => setOpen(false)}
          />
          <div
            className="ctv-connect-menu"
            role="dialog"
            aria-label="Connect a player"
            tabIndex={-1}
            ref={menuRef}
            onKeyDown={onMenuKeyDown}
          >
            <div className="ctv-connect-header">
              <strong>Connect a player</strong>
              <span>{channelCountLabel}</span>
            </div>
            <EndpointRow
              icon={<ListVideo aria-hidden="true" size={15} />}
              label="M3U playlist"
              url={playlistUrl}
            />
            <EndpointRow
              icon={<CalendarClock aria-hidden="true" size={15} />}
              label="XMLTV guide"
              url={guideUrl}
            />
            <p>
              <Info aria-hidden="true" size={13} />
              Works with any IPTV player.
            </p>
          </div>
        </>
      )}
    </div>
  );
}

function TopBar({ route }: { route: ScreenRoute | null }) {
  const title = route?.title ?? 'Page not found';
  const kicker = route?.kicker ?? 'Unknown route';
  const primaryAction = route?.primaryAction ?? '';
  const primaryActionHandler = usePrimaryActionHandler(route?.id ?? '');
  const [searchValue, setSearchValue] = useState('');

  return (
    <header className="ctv-topbar">
      <div className="ctv-topbar-title">
        <p>{kicker}</p>
        <h1>{title}</h1>
      </div>

      <form
        className="ctv-topbar-search"
        onSubmit={(event) => {
          event.preventDefault();
          const trimmed = searchValue.trim();
          if (trimmed) {
            navigateToPath(`/app/search?query=${encodeURIComponent(trimmed)}`);
          }
        }}
        role="search"
      >
        <Search aria-hidden="true" size={15} />
        <input
          aria-label="Search"
          onChange={(event) => setSearchValue(event.target.value)}
          placeholder="Search movies, shows, music..."
          type="search"
          value={searchValue}
        />
      </form>

      <div className="ctv-topbar-spacer" />

      <div className="ctv-topbar-tools">
        <UserMenu />
        <ConnectMenu />
        <span className="ctv-topbar-divider" />
        <IconButton title="Documentation" size="sm">
          <CircleHelp aria-hidden="true" size={17} />
        </IconButton>
        <IconButton title="Notifications" size="sm">
          <Bell aria-hidden="true" size={17} />
        </IconButton>
        <span className="ctv-avatar">TB</span>
      </div>

      {route && primaryAction && primaryActionHandler ? (
        <Button
          startIcon={<Plus aria-hidden="true" size={15} />}
          onClick={primaryActionHandler}
        >
          {primaryAction}
        </Button>
      ) : null}
    </header>
  );
}

export function AppShell({
  children,
  healthState,
  onNavigate,
  onThemeChange,
  route,
  theme
}: {
  children: ReactNode;
  healthState: DashboardHealthQueryState;
  onNavigate: NavigateHandler;
  onThemeChange: (theme: DesignSystemThemeId) => void;
  route: ScreenRoute | null;
  theme: DesignSystemThemeId;
}) {
  const { collapsed, isGroupCollapsed, toggleCollapsed, toggleGroup } = useSidebarState();

  return (
    <div className={`ctv-app-shell${collapsed ? ' ctv-app-shell-collapsed' : ''}`}>
      <Sidebar
        activeRoute={route}
        collapsed={collapsed}
        healthState={healthState}
        isGroupCollapsed={isGroupCollapsed}
        onNavigate={onNavigate}
        onToggleCollapsed={toggleCollapsed}
        onToggleGroup={toggleGroup}
      />
      <div className="ctv-shell-body">
        <TopBar route={route} />
        <UnauthorizedBanner />
        <main className="ctv-main">
          <ThemeSwitcher theme={theme} onThemeChange={onThemeChange} />
          {children}
        </main>
      </div>
    </div>
  );
}
