/**
 * Main application layout.
 *
 * Provides the persistent sidebar navigation and the main content area
 * for all authenticated pages.  The sidebar collapses from 240 px to
 * icon-only (48 px) on small screens.
 */

import { useCallback, useEffect, useState } from "react";
import {
  Badge,
  Button,
  makeStyles,
  mergeClasses,
  MessageBar,
  MessageBarBody,
  MessageBarTitle,
  Text,
  tokens,
  Tooltip,
} from "@fluentui/react-components";
import {
  GridRegular,
  MapRegular,
  ShieldRegular,
  SettingsRegular,
  HistoryRegular,
  ListRegular,
  SignOutRegular,
  NavigationRegular,
} from "@fluentui/react-icons";
import { NavLink, Outlet, useNavigate } from "react-router-dom";
import { useAuth } from "../providers/AuthProvider";
import { useServerStatus } from "../hooks/useServerStatus";
import { useBlocklistStatus } from "../hooks/useBlocklist";

// ---------------------------------------------------------------------------
// Styles
// ---------------------------------------------------------------------------

const SIDEBAR_FULL = "240px";
const SIDEBAR_COLLAPSED = "48px";

const useStyles = makeStyles({
  root: {
    display: "flex",
    height: "100vh",
    overflow: "hidden",
    backgroundColor: tokens.colorNeutralBackground3,
  },

  // Sidebar
  sidebar: {
    display: "flex",
    flexDirection: "column",
    width: SIDEBAR_FULL,
    minWidth: SIDEBAR_COLLAPSED,
    backgroundColor: tokens.colorNeutralBackground1,
    borderRightWidth: "1px",
    borderRightStyle: "solid",
    borderRightColor: tokens.colorNeutralStroke2,
    transition: "width 200ms ease",
    overflow: "hidden",
    flexShrink: 0,
    // Honour the OS/browser reduced-motion preference.
    "@media (prefers-reduced-motion: reduce)": {
      transition: "none",
    },
  },
  sidebarCollapsed: {
    width: SIDEBAR_COLLAPSED,
  },

  sidebarHeader: {
    display: "flex",
    alignItems: "center",
    height: "52px",
    paddingLeft: tokens.spacingHorizontalM,
    paddingRight: tokens.spacingHorizontalS,
    gap: tokens.spacingHorizontalS,
    borderBottomWidth: "1px",
    borderBottomStyle: "solid",
    borderBottomColor: tokens.colorNeutralStroke2,
    flexShrink: 0,
  },
  logo: {
    fontWeight: 600,
    fontSize: "16px",
    whiteSpace: "nowrap",
    overflow: "hidden",
    color: tokens.colorBrandForeground1,
    flexGrow: 1,
  },

  // Nav items list
  navList: {
    display: "flex",
    flexDirection: "column",
    gap: "2px",
    padding: tokens.spacingVerticalS,
    overflowY: "auto",
    flexGrow: 1,
  },

  navLinkContent: {
    display: "flex",
    alignItems: "center",
    gap: tokens.spacingHorizontalS,
    flexGrow: 1,
  },
  navLink: {
    display: "flex",
    alignItems: "center",
    gap: tokens.spacingHorizontalS,
    padding: `${tokens.spacingVerticalSNudge} ${tokens.spacingHorizontalS}`,
    borderRadius: tokens.borderRadiusMedium,
    textDecoration: "none",
    color: tokens.colorNeutralForeground2,
    whiteSpace: "nowrap",
    overflow: "hidden",
    ":hover": {
      backgroundColor: tokens.colorNeutralBackground1Hover,
      color: tokens.colorNeutralForeground1,
    },
  },
  navLinkActive: {
    backgroundColor: tokens.colorNeutralBackground1Selected,
    color: tokens.colorBrandForeground1,
    ":hover": {
      backgroundColor: tokens.colorNeutralBackground1Selected,
    },
  },
  navLabel: {
    fontSize: "14px",
    lineHeight: "20px",
    overflow: "hidden",
    textOverflow: "ellipsis",
  },

  // Sidebar footer (logout)
  sidebarFooter: {
    borderTopWidth: "1px",
    borderTopStyle: "solid",
    borderTopColor: tokens.colorNeutralStroke2,
    padding: tokens.spacingVerticalS,
    flexShrink: 0,
  },
  versionText: {
    display: "block",
    color: tokens.colorNeutralForeground4,
    fontSize: "11px",
    paddingLeft: tokens.spacingHorizontalS,
    paddingRight: tokens.spacingHorizontalS,
    paddingBottom: tokens.spacingVerticalXS,
    whiteSpace: "nowrap",
    overflow: "hidden",
  },

  // Main content
  main: {
    display: "flex",
    flexDirection: "column",
    flexGrow: 1,
    overflow: "auto",
  },
  warningBar: {
    flexShrink: 0,
    paddingLeft: tokens.spacingHorizontalM,
    paddingRight: tokens.spacingHorizontalM,
    paddingTop: tokens.spacingVerticalXS,
    paddingBottom: tokens.spacingVerticalXS,
  },
  content: {
    flexGrow: 1,
    maxWidth: "1440px",
    width: "100%",
    margin: "0 auto",
    padding: tokens.spacingVerticalL,
  },
});

// ---------------------------------------------------------------------------
// Nav item data
// ---------------------------------------------------------------------------

interface NavItem {
  label: string;
  to: string;
  icon: React.ReactElement;
  end?: boolean;
}

const NAV_ITEMS: NavItem[] = [
  { label: "Dashboard", to: "/", icon: <GridRegular />, end: true },
  { label: "World Map", to: "/map", icon: <MapRegular /> },
  { label: "Jails", to: "/jails", icon: <ShieldRegular /> },
  { label: "History", to: "/history", icon: <HistoryRegular /> },
  { label: "Blocklists", to: "/blocklists", icon: <ListRegular /> },
  { label: "Configuration", to: "/config", icon: <SettingsRegular /> },
];

// ---------------------------------------------------------------------------
// Component
// ---------------------------------------------------------------------------

/**
 * Main application shell with sidebar navigation and content area.
 *
 * Renders child routes via `<Outlet />`.  Use inside React Router
 * as a layout route wrapping all authenticated pages.
 */
export function MainLayout(): React.JSX.Element {
  const styles = useStyles();
  const { logout } = useAuth();
  const navigate = useNavigate();
  // Initialise collapsed based on screen width so narrow viewports start
  // with the icon-only sidebar rather than the full-width one.
  const [collapsed, setCollapsed] = useState(() => window.innerWidth < 640);
  const { status } = useServerStatus();
  const { hasErrors: blocklistHasErrors } = useBlocklistStatus();

  /** True only after the first successful poll and fail2ban is unreachable. */
  const serverOffline = status !== null && !status.online;

  // Auto-collapse / auto-expand when the viewport crosses the 640 px breakpoint.
  useEffect(() => {
    const mq = window.matchMedia("(max-width: 639px)");
    const handler = (e: MediaQueryListEvent): void => {
      setCollapsed(e.matches);
    };
    mq.addEventListener("change", handler);
    return (): void => { mq.removeEventListener("change", handler); };
  }, []);

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

  const handleLogout = useCallback(async () => {
    await logout();
    navigate("/login", { replace: true });
  }, [logout, navigate]);

  return (
    <div className={styles.root}>
      {/* ---------------------------------------------------------------- */}
      {/* Sidebar                                                           */}
      {/* ---------------------------------------------------------------- */}
      <nav
        className={mergeClasses(
          styles.sidebar,
          collapsed && styles.sidebarCollapsed,
        )}
        aria-label="Main navigation"
      >
        {/* Header */}
        <div className={styles.sidebarHeader}>
          {!collapsed && <span className={styles.logo}>BanGUI</span>}
          <Tooltip content={collapsed ? "Expand sidebar" : "Collapse sidebar"} relationship="label">
            <Button
              appearance="subtle"
              icon={<NavigationRegular />}
              onClick={toggleCollapse}
              aria-label={collapsed ? "Expand sidebar" : "Collapse sidebar"}
            />
          </Tooltip>
        </div>

        {/* Nav links */}
        <ul className={styles.navList} role="list" aria-label="Pages">
          {NAV_ITEMS.map((item) => {
            const showBadge = item.to === "/blocklists" && blocklistHasErrors;
            return (
              <li key={item.to} role="listitem">
                <Tooltip
                  content={collapsed ? item.label : ""}
                  relationship="label"
                  positioning="after"
                >
                  <NavLink
                    to={item.to}
                    end={item.end}
                    className={({ isActive }) =>
                      mergeClasses(
                        styles.navLink,
                        isActive && styles.navLinkActive,
                      )
                    }
                    aria-label={collapsed ? item.label : undefined}
                  >
                    <span className={styles.navLinkContent}>
                      {item.icon}
                      {!collapsed && (
                        <Text className={styles.navLabel}>{item.label}</Text>
                      )}
                    </span>
                    {showBadge && (
                      <Badge
                        appearance="filled"
                        color="warning"
                        size="extra-small"
                        aria-label="Import errors"
                      />
                    )}
                  </NavLink>
                </Tooltip>
              </li>
            );
          })}
        </ul>

        {/* Footer — Logout */}
        <div className={styles.sidebarFooter}>
          {!collapsed && (
            <Text className={styles.versionText}>
              BanGUI v{__APP_VERSION__}
            </Text>
          )}
          <Tooltip
            content={collapsed ? "Sign out" : ""}
            relationship="label"
            positioning="after"
          >
            <Button
              appearance="subtle"
              icon={<SignOutRegular />}
              onClick={() => void handleLogout()}
              aria-label="Sign out"
              style={{ width: "100%", justifyContent: collapsed ? "center" : "flex-start" }}
            >
              {!collapsed && "Sign out"}
            </Button>
          </Tooltip>
        </div>
      </nav>

      {/* ---------------------------------------------------------------- */}
      {/* Main content                                                      */}
      {/* ---------------------------------------------------------------- */}
      <main className={styles.main}>
        {/* Connection health warning — shown when fail2ban is unreachable */}
        {serverOffline && (
          <div className={styles.warningBar} role="alert">
            <MessageBar intent="warning">
              <MessageBarBody>
                <MessageBarTitle>fail2ban Unreachable</MessageBarTitle>
                The connection to the fail2ban server has been lost. Some
                features may be temporarily unavailable.
              </MessageBarBody>
            </MessageBar>
          </div>
        )}
        {/* Blocklist import error warning — shown when the last scheduled import had errors */}
        {blocklistHasErrors && (
          <div className={styles.warningBar} role="alert">
            <MessageBar intent="warning">
              <MessageBarBody>
                <MessageBarTitle>Blocklist Import Errors</MessageBarTitle>
                The most recent blocklist import encountered errors. Check the
                Blocklists page for details.
              </MessageBarBody>
            </MessageBar>
          </div>
        )}
        <div className={styles.content}>
          <Outlet />
        </div>
      </main>
    </div>
  );
}
