"use client";

import {
  AppstoreOutlined,
  BankOutlined,
  ClusterOutlined,
  CloudOutlined,
  DashboardOutlined,
  EnvironmentOutlined,
  FileTextOutlined,
  GlobalOutlined,
  InboxOutlined,
  LogoutOutlined,
  MenuFoldOutlined,
  MenuOutlined,
  MenuUnfoldOutlined,
  SafetyOutlined,
  SearchOutlined,
  SettingOutlined,
  ShopOutlined,
  ShoppingCartOutlined,
  SwapOutlined,
  TeamOutlined,
  TrophyOutlined,
  ReadOutlined,
} from "@ant-design/icons";
import Link from "next/link";
import { App, Avatar, Button, Drawer, Dropdown, Layout, Menu, Spin } from "antd";
import { usePathname, useRouter } from "next/navigation";
import { useEffect, useState } from "react";
import { useAuth } from "@/lib/auth/AuthProvider";
import { useSiteBranding } from "@/lib/branding/BrandingProvider";
import { useThemeSettings } from "@/lib/theme/ThemeProvider";
import { titleCase } from "@/lib/utils/text";
import packageJson from "../../package.json";
import { AssistantWidget } from "./AssistantWidget";
import { ThemeModeSelector } from "./ThemeModeSelector";

// package.json's version is bumped automatically on every commit by the
// repo's pre-commit hook (see .githooks/pre-commit) - nothing here needs
// updating by hand.
const SIDEBAR_COLLAPSED_KEY = "dms:sidebar-collapsed";

const APP_VERSION = `v${packageJson.version}`;

/**
 * `tabKey` matches one of Settings > Menu's toggle keys - see
 * SIDEBAR_ITEMS in SidebarTab.tsx and User::canAccessTab() on the backend.
 * `extraGate` (Buy Domains/Auctions/Users only) is an additional, pre-
 * existing check on top of the tab toggle: their own action permission
 * still has to be granted too, same as before menu visibility existed -
 * turning the tab off hides the item regardless of that permission, but
 * turning it on doesn't skip the permission check.
 */
const ALL_NAV_ITEMS = [
  { key: "/", tabKey: "dashboard", icon: <DashboardOutlined />, label: "Dashboard", className: "dms_nav_dashboard" },
  { key: "/registrars", tabKey: "registrars", icon: <BankOutlined />, label: "Registrars", className: "dms_nav_registrars" },
  {
    key: "/acquire",
    tabKey: "acquire",
    icon: <ShoppingCartOutlined />,
    label: "Buy Domains",
    extraGate: "acquire.access",
    className: "dms_nav_acquire",
  },
  {
    key: "/acquire/auctions",
    tabKey: "auctions",
    icon: <TrophyOutlined />,
    label: "Auctions",
    extraGate: "auction.access",
    className: "dms_nav_auctions",
  },
  {
    key: "domains-group",
    tabKey: "domains",
    icon: <GlobalOutlined />,
    label: "Domains",
    className: "dms_nav_domains_group",
    children: [
      { key: "/domains", label: "All Domains", className: "dms_nav_domains_all" },
      { key: "/domains/brands", icon: <ShopOutlined />, label: "Projects", className: "dms_nav_domains_brands" },
      { key: "/domains/regions", icon: <EnvironmentOutlined />, label: "Regions", className: "dms_nav_domains_regions" },
      { key: "/domains/archived", icon: <InboxOutlined />, label: "Archived Domains", className: "dms_nav_domains_archived" },
    ],
  },
  { key: "/wordpress", tabKey: "wordpress", icon: <ReadOutlined />, label: "WordPress", className: "dms_nav_wordpress" },
  { key: "/servers", tabKey: "servers", icon: <ClusterOutlined />, label: "Servers", className: "dms_nav_servers" },
  { key: "/seo", tabKey: "seo", icon: <SearchOutlined />, label: "SEO Controls", className: "dms_nav_seo" },
  {
    key: "cloudflare-group",
    tabKey: "cloudflare",
    icon: <CloudOutlined />,
    label: "Cloudflare",
    className: "dms_nav_cloudflare_group",
    children: [
      { key: "/cloudflare", label: "Accounts", className: "dms_nav_cloudflare_accounts" },
      { key: "/redirects", icon: <SwapOutlined />, label: "Redirects", className: "dms_nav_redirects" },
    ],
  },
  { key: "/logs", tabKey: "logs", icon: <FileTextOutlined />, label: "Activity Log", className: "dms_nav_logs" },
  {
    key: "/users",
    tabKey: "users",
    icon: <TeamOutlined />,
    label: "Users",
    extraGate: "users.manage",
    className: "dms_nav_users",
  },
];

/**
 * The nav item that owns the current URL - the longest route that is the
 * path itself or a parent of it, so /domains/42 still highlights Domains.
 */
/** The expandable group that owns a route, so the sidebar opens it automatically. */
function groupKeyFor(route: string): string | undefined {
  return ALL_NAV_ITEMS.find((item) => item.children?.some((child) => child.key === route))?.key;
}

function activeNavKey(pathname: string, routes: string[]): string {
  const matches = routes.filter((route) => (route === "/" ? pathname === "/" : pathname === route || pathname.startsWith(`${route}/`)));

  return matches.sort((a, b) => b.length - a.length)[0] ?? pathname;
}

export default function DashboardLayout({ children }: LayoutProps<"/">) {
  const { user, isLoading, logout, refresh } = useAuth();
  const branding = useSiteBranding();
  const { modal } = App.useApp();
  const router = useRouter();
  const pathname = usePathname();
  const [isDrawerOpen, setIsDrawerOpen] = useState(false);
  const [logoFailed, setLogoFailed] = useState(false);
  const theme = useThemeSettings();
  const isSidebar = theme.nav_layout === "sidebar";
  const [openKeys, setOpenKeys] = useState<string[]>([]);
  const [isCollapsed, setIsCollapsed] = useState(false);

  useEffect(() => {
    // The saved sidebar width - a browser-only read, so it runs after mount.
    try {
      // eslint-disable-next-line react-hooks/set-state-in-effect
      setIsCollapsed(window.localStorage.getItem(SIDEBAR_COLLAPSED_KEY) === "1");
    } catch {
      // Storage blocked - the sidebar simply starts expanded.
    }
  }, []);

  function toggleCollapsed() {
    setIsCollapsed((current) => {
      const next = !current;
      try {
        window.localStorage.setItem(SIDEBAR_COLLAPSED_KEY, next ? "1" : "0");
      } catch {
        // Not persisted - still collapses for this visit.
      }

      return next;
    });
  }

  useEffect(() => {
    if (!isLoading && !user) {
      router.replace("/login");
    }
  }, [isLoading, user, router]);

  useEffect(() => {
    // Sidebar layout: open the group the current page belongs to.
    const allRoutes = ALL_NAV_ITEMS.flatMap((item) => (item.children ? item.children.map((c) => c.key) : [item.key]));
    const group = groupKeyFor(activeNavKey(pathname, allRoutes));
    // eslint-disable-next-line react-hooks/set-state-in-effect
    if (group) setOpenKeys((keys) => (keys.includes(group) ? keys : [...keys, group]));
  }, [pathname]);

  useEffect(() => {
    // Close the small-screen menu on every navigation so it never stays
    // open over the page the link was just clicked to reach.
    // eslint-disable-next-line react-hooks/set-state-in-effect
    setIsDrawerOpen(false);
  }, [pathname]);

  useEffect(() => {
    // EnsureNotForceLoggedOut (backend) already killed the session by the
    // time this fires - apiFetch just tells us it happened, since it has
    // no React context of its own to show this notice directly. See
    // UserController::forceLogout().
    function handleForceLogout(event: Event) {
      const message = (event as CustomEvent<{ message: string }>).detail?.message;
      modal.warning({
        title: "You've been signed out",
        content: message || "You have been signed out by an administrator.",
        okText: "OK",
        onOk: async () => {
          await refresh();
          router.push("/login");
        },
      });
    }

    window.addEventListener("dms:force-logout", handleForceLogout);
    return () => window.removeEventListener("dms:force-logout", handleForceLogout);
  }, [modal, refresh, router]);

  if (isLoading || !user) {
    return (
      <div className="flex flex-1 min-h-screen items-center justify-center dms__loading">
        <Spin size="large" />
      </div>
    );
  }

  const tabPermissions = user.tab_permissions ?? [];
  const hasTab = (tabKey: string) => user.is_admin || tabPermissions.includes("*") || tabPermissions.includes(tabKey);
  const actionPermissions = user.action_permissions ?? [];
  const hasAction = (action: string) => user.is_admin || actionPermissions.includes("*") || actionPermissions.includes(action);

  const visibleItems = ALL_NAV_ITEMS.filter((item) => hasTab(item.tabKey) && (!item.extraGate || hasAction(item.extraGate)));
  const routes = visibleItems.flatMap((item) => (item.children ? item.children.map((c) => c.key) : [item.key]));
  const selectedKey = activeNavKey(pathname, routes);

  const navLink = (key: string, label: string) => (
    <Link
      href={key}
      className="dms__nav-link"
      onClick={(event) => {
        const isPlainClick = event.button === 0 && !event.ctrlKey && !event.metaKey && !event.shiftKey && !event.altKey;
        if (isPlainClick) event.preventDefault();
      }}
    >
      {label}
    </Link>
  );
  const toMenuItem = (item: (typeof ALL_NAV_ITEMS)[number], withIcon: boolean) => ({
    key: item.key,
    icon: withIcon ? item.icon : undefined,
    label: item.children ? item.label : navLink(item.key, item.label),
    className: item.className,
    children: item.children?.map((child) => ({ ...child, label: navLink(child.key, child.label) })),
  });
  const topNavItems = visibleItems.map((item) => toMenuItem(item, false));
  const drawerItems = visibleItems.map((item) => toMenuItem(item, true));
  const goTo = ({ key, domEvent }: { key: string; domEvent: { ctrlKey?: boolean; metaKey?: boolean; shiftKey?: boolean; altKey?: boolean } }) => {
    const isModified = domEvent.ctrlKey || domEvent.metaKey || domEvent.shiftKey || domEvent.altKey;
    if (key.startsWith("/") && !isModified) router.push(key);
  };

  const userMenuItems = [
    {
      key: "profile",
      icon: <SafetyOutlined />,
      label: "Profile",
      onClick: () => router.push("/profile"),
    },
    ...(user.is_crowned_or_above
      ? [
          {
            key: "settings",
            icon: <SettingOutlined />,
            label: "Settings",
            onClick: () => router.push("/settings"),
          },
        ]
      : []),
    { type: "divider" as const },
    {
      key: "logout",
      icon: <LogoutOutlined />,
      label: "Log out",
      onClick: async () => {
        await logout();
        router.push("/login");
      },
    },
  ];

  const brand = (
    <div className="dms__brand dms__logo">
      {branding.site_logo && !logoFailed ? (
        // eslint-disable-next-line @next/next/no-img-element
        <img src={branding.site_logo} alt={branding.site_title} style={{ height: 32 }} onError={() => setLogoFailed(true)} />
      ) : (
        <span className="dms__brand-logo">
          <AppstoreOutlined style={{ fontSize: 18 }} />
        </span>
      )}
      <span className="truncate">{branding.site_title}</span>
      <small className="shrink-0">{APP_VERSION}</small>
    </div>
  );

  const userBlock = (placement: "bottomRight" | "topLeft") => (
    <Dropdown menu={{ items: userMenuItems }} trigger={["click"]} placement={placement} arrow>
      <div className="dms__user dms__user-menu">
        <Avatar className="dms__user-avatar">{user.name.charAt(0).toUpperCase()}</Avatar>
        <div className="flex flex-col min-w-0">
          <span className="truncate dms__user-name">{user.name}</span>
          {user.role && <span className="truncate dms__user-role">{titleCase(user.role.label)}</span>}
        </div>
      </div>
    </Dropdown>
  );

  return (
    <Layout className={`min-h-screen dms__shell${isSidebar ? " dms__shell--sidebar" : ""}${branding.demo_mode ? " dms__shell--demo" : ""}`}>
      {branding.demo_mode && (
        <div className="dms__demo-bar" role="status">
          <span>
            This website is running <b>demo content</b> - the sample records are not real.
          </span>
          {user.is_crowned_or_above && (
            <a href="/settings" onClick={(event) => { event.preventDefault(); router.push("/settings"); }}>
              Turn it off in Settings &gt; Extra
            </a>
          )}
        </div>
      )}
      {isSidebar && (
        <aside className={`dms__side${isCollapsed ? " dms__side--collapsed" : ""}`}>
          <div className="dms__side-top">
            {brand}
            <Button
              type="text"
              size="small"
              icon={isCollapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
              onClick={toggleCollapsed}
              aria-label={isCollapsed ? "Expand sidebar" : "Collapse sidebar"}
              className="dms__side-toggle"
            />
          </div>
          <Menu
            mode="inline"
            inlineCollapsed={isCollapsed}
            selectedKeys={[selectedKey]}
            {...(isCollapsed ? {} : { openKeys, onOpenChange: setOpenKeys })}
            items={drawerItems}
            onClick={goTo}
            className="dms__nav dms__side-menu"
          />
          <div className="dms__side-foot">
            {!isCollapsed && <ThemeModeSelector block />}
            {userBlock("topLeft")}
          </div>
        </aside>
      )}
      <header className="dms__header">
        <div className="dms__header-inner">
          <Button
            type="text"
            icon={<MenuOutlined />}
            onClick={() => setIsDrawerOpen(true)}
            aria-label="Open navigation"
            className="dms__nav-toggle"
          />
          {brand}
          <Menu
            mode="horizontal"
            selectedKeys={[selectedKey]}
            items={topNavItems}
            onClick={goTo}
            className="dms__topnav dms__nav"
          />
          <div className="dms__header-right">
            <ThemeModeSelector />
            {userBlock("bottomRight")}
          </div>
        </div>
      </header>

      <Drawer
        placement="left"
        open={isDrawerOpen}
        onClose={() => setIsDrawerOpen(false)}
        size={290}
        title={branding.site_title}
        className="dms__drawer"
        styles={{ body: { padding: 8 } }}
      >
        <Menu
          mode="inline"
          selectedKeys={[selectedKey]}
          items={drawerItems}
          onClick={goTo}
          style={{ border: "none", background: "transparent" }}
          className="dms__nav"
        />
      </Drawer>

      <Layout className="dms__main" style={{ background: "transparent" }}>
        <Layout.Content className="dms__content">{children}</Layout.Content>
      </Layout>
      <AssistantWidget />
    </Layout>
  );
}
