import { A, useLocation, useNavigate, useSearchParams } from "@solidjs/router";
import {
  For,
  Show,
  createEffect,
  createMemo,
  createSignal,
  onCleanup,
  onMount,
  type JSX,
} from "solid-js";
import { Bell, Moon, Search, Settings, Sun } from "lucide-solid";
import AdminSidebar from "./AdminSidebar";
import { isExternalIdentity } from "~/lib/admin-auth";
import { normalizeAllowedModules } from "~/lib/admin/module-access";
import { sessionExpired, clearSessionExpired, installSessionExpiryWatcher } from "~/lib/session-expired";
import { installSessionRefreshLoop } from "~/lib/session-refresh";

type Tab = { href: string; label: string; exact?: boolean };
type SearchResult = { id: string; title: string; subtitle: string; href: string };
type SearchGroup = { label: string; viewAllHref: string; results: SearchResult[] };

const PAGE_TITLES: Array<{ prefix: string; label: string; exact?: boolean }> = [
  { prefix: "/admin", label: "Dashboard", exact: true },
  { prefix: "/admin/department", label: "Department Management" },
  { prefix: "/admin/designation", label: "Designation Management" },
  { prefix: "/admin/roles", label: "Internal Role Management" },
  { prefix: "/admin/employees", label: "Employee Management" },
  { prefix: "/admin/external-roles", label: "External Role Management" },
  { prefix: "/admin/internal-dashboard-management", label: "Internal Dashboard Management" },
  { prefix: "/admin/external-dashboard-management", label: "External Dashboard Management" },
  { prefix: "/admin/role-ui-configs", label: "External Dashboard Management" },
  { prefix: "/admin/verification", label: "Verification Management" },
  { prefix: "/admin/verification-status", label: "Verification Management" },
  { prefix: "/admin/approval", label: "Approval Management" },
  { prefix: "/admin/approvals", label: "Approval Management" },
  { prefix: "/admin/approval-management", label: "Approval Management" },
  { prefix: "/admin/users", label: "Users Management" },
  { prefix: "/admin/company", label: "Company Management" },
  { prefix: "/admin/candidate", label: "Candidate Management" },
  { prefix: "/admin/customer", label: "Customer Management" },
  { prefix: "/admin/photographer", label: "Photographer Management" },
  { prefix: "/admin/makeup-artist", label: "Makeup Artist Management" },
  { prefix: "/admin/tutors", label: "Tutors Management" },
  { prefix: "/admin/developers", label: "Developers Management" },
  { prefix: "/admin/video-editors", label: "Video Editor Management" },
  { prefix: "/admin/fitness-trainers", label: "Fitness Trainer Management" },
  { prefix: "/admin/catering-services", label: "Catering Services Management" },
  { prefix: "/admin/ugc-content-creators", label: "UGC Content Creator Management" },
  { prefix: "/admin/graphic-designers", label: "Graphic Designer Management" },
  { prefix: "/admin/social-media-managers", label: "Social Media Manager Management" },
  { prefix: "/admin/jobs", label: "Jobs Management" },
  { prefix: "/admin/leads", label: "Leads Management" },
  { prefix: "/admin/applications", label: "Applications Management" },
  { prefix: "/admin/responses", label: "Responses Management" },
  { prefix: "/admin/pricing", label: "Pricing Management" },
  { prefix: "/admin/credit", label: "Credit Management" },
  { prefix: "/admin/coupon", label: "Coupon Management" },
  { prefix: "/admin/discount", label: "Discount Management" },
  { prefix: "/admin/tax", label: "Tax Management" },
  { prefix: "/admin/order", label: "Order Management" },
  { prefix: "/admin/invoice", label: "Invoice Management" },
  { prefix: "/admin/payment-gateway", label: "Payment Gateway Management" },
  { prefix: "/admin/smtp", label: "SMTP Management" },
  { prefix: "/admin/kb", label: "Knowledge Base Management" },
  { prefix: "/admin/notifications", label: "Notifications" },
  { prefix: "/admin/review", label: "Review Management" },
  { prefix: "/admin/support", label: "Support Management" },
  { prefix: "/admin/report", label: "Report Management" },
  { prefix: "/admin/ledger", label: "Ledger Management" },
];

const TAB_SETS: Array<{ prefixes: string[]; tabs: Tab[] }> = [];
const ROUTE_MODULE_KEYS: Array<{ prefix: string; keys: string[] }> = [
  { prefix: "/admin", keys: ["ADMIN_DASHBOARD", "DASHBOARD"] },
  { prefix: "/admin/department", keys: ["DEPARTMENT_MANAGEMENT", "DEPARTMENTS"] },
  { prefix: "/admin/department-management", keys: ["DEPARTMENT_MANAGEMENT", "DEPARTMENTS"] },
  { prefix: "/admin/designation", keys: ["DESIGNATION_MANAGEMENT", "DESIGNATIONS"] },
  { prefix: "/admin/designation-management", keys: ["DESIGNATION_MANAGEMENT", "DESIGNATIONS"] },
  { prefix: "/admin/roles", keys: ["INTERNAL_ROLE_MANAGEMENT", "ROLES"] },
  { prefix: "/admin/employees", keys: ["EMPLOYEE_MANAGEMENT", "EMPLOYEES"] },
  { prefix: "/admin/external-roles", keys: ["EXTERNAL_ROLE_MANAGEMENT", "EXTERNAL_ROLES"] },
  {
    prefix: "/admin/internal-dashboard-management",
    keys: ["INTERNAL_DASHBOARD_MANAGEMENT", "INTERNAL_DASHBOARDS", "INTERNAL_DASHBOARD_CONFIG"],
  },
  {
    prefix: "/admin/external-dashboard-management",
    keys: [
      "DASHBOARD_CONFIG_MANAGEMENT",
      "EXTERNAL_DASHBOARD_MANAGEMENT",
      "EXTERNAL_DASHBOARDS",
      "EXTERNAL_DASHBOARD_CONFIG",
      "RUNTIME_ROLES",
    ],
  },
  {
    prefix: "/admin/role-ui-configs",
    keys: [
      "DASHBOARD_CONFIG_MANAGEMENT",
      "EXTERNAL_DASHBOARD_MANAGEMENT",
      "EXTERNAL_DASHBOARDS",
      "EXTERNAL_DASHBOARD_CONFIG",
      "RUNTIME_ROLES",
    ],
  },
  { prefix: "/admin/verification", keys: ["VERIFICATION_MANAGEMENT", "VERIFICATIONS"] },
  { prefix: "/admin/verification-status", keys: ["VERIFICATION_MANAGEMENT", "VERIFICATIONS"] },
  { prefix: "/admin/approval", keys: ["APPROVAL_MANAGEMENT", "APPROVALS"] },
  { prefix: "/admin/approvals", keys: ["APPROVAL_MANAGEMENT", "APPROVALS"] },
  { prefix: "/admin/approval-management", keys: ["APPROVAL_MANAGEMENT", "APPROVALS"] },
  { prefix: "/admin/users", keys: ["USER_MANAGEMENT", "USERS"] },
  { prefix: "/admin/company", keys: ["COMPANY_MANAGEMENT", "COMPANIES"] },
  { prefix: "/admin/candidate", keys: ["CANDIDATE_MANAGEMENT", "CANDIDATES"] },
  { prefix: "/admin/customer", keys: ["CUSTOMER_MANAGEMENT", "CUSTOMERS"] },
  { prefix: "/admin/photographer", keys: ["PHOTOGRAPHER_MANAGEMENT", "PHOTOGRAPHERS"] },
  { prefix: "/admin/makeup-artist", keys: ["MAKEUP_ARTIST_MANAGEMENT", "MAKEUP_ARTISTS"] },
  { prefix: "/admin/tutors", keys: ["TUTOR_MANAGEMENT", "TUTORS"] },
  { prefix: "/admin/developers", keys: ["DEVELOPER_MANAGEMENT", "DEVELOPERS"] },
  { prefix: "/admin/video-editors", keys: ["VIDEO_EDITOR_MANAGEMENT", "VIDEO_EDITORS"] },
  { prefix: "/admin/fitness-trainers", keys: ["FITNESS_TRAINER_MANAGEMENT", "FITNESS_TRAINERS"] },
  {
    prefix: "/admin/catering-services",
    keys: ["CATERING_SERVICES_MANAGEMENT", "CATERING_SERVICES"],
  },
  {
    prefix: "/admin/ugc-content-creator",
    keys: ["UGC_CONTENT_CREATOR_MANAGEMENT", "UGC_CONTENT_CREATOR"],
  },
  {
    prefix: "/admin/graphic-designers",
    keys: ["GRAPHIC_DESIGNER_MANAGEMENT", "GRAPHIC_DESIGNERS"],
  },
  {
    prefix: "/admin/social-media-managers",
    keys: ["SOCIAL_MEDIA_MANAGEMENT", "SOCIAL_MEDIA_MANAGER_MANAGEMENT", "SOCIAL_MEDIA_MANAGERS"],
  },
  { prefix: "/admin/jobs", keys: ["JOBS_MANAGEMENT", "JOBS"] },
  {
    prefix: "/admin/leads",
    keys: ["LEADS_MANAGEMENT", "LEADS", "REQUIREMENTS_MANAGEMENT", "REQUIREMENTS"],
  },
  { prefix: "/admin/applications", keys: ["APPLICATIONS_MANAGEMENT", "APPLICATIONS"] },
  { prefix: "/admin/responses", keys: ["RESPONSES_MANAGEMENT", "RESPONSES"] },
  { prefix: "/admin/pricing", keys: ["PRICING_MANAGEMENT", "PRICING"] },
  { prefix: "/admin/credit", keys: ["CREDIT_MANAGEMENT", "CREDITS"] },
  { prefix: "/admin/coupon", keys: ["COUPON_MANAGEMENT", "COUPONS"] },
  { prefix: "/admin/discount", keys: ["DISCOUNT_MANAGEMENT", "DISCOUNTS"] },
  { prefix: "/admin/tax", keys: ["TAX_MANAGEMENT", "TAXES"] },
  { prefix: "/admin/order", keys: ["ORDER_MANAGEMENT", "ORDERS"] },
  { prefix: "/admin/invoice", keys: ["INVOICE_MANAGEMENT", "INVOICES"] },
  { prefix: "/admin/payment-gateway", keys: ["PAYMENT_GATEWAY_MANAGEMENT", "PAYMENT_GATEWAY"] },
  { prefix: "/admin/smtp", keys: ["SMTP_MANAGEMENT", "SMTP"] },
  { prefix: "/admin/kb", keys: ["KNOWLEDGE_BASE_MANAGEMENT", "KNOWLEDGE_BASE", "KB"] },
  { prefix: "/admin/notifications", keys: ["NOTIFICATIONS_MANAGEMENT", "NOTIFICATIONS"] },
  { prefix: "/admin/review", keys: ["REVIEW_MANAGEMENT", "REVIEWS"] },
  { prefix: "/admin/support", keys: ["SUPPORT_MANAGEMENT", "SUPPORT"] },
  { prefix: "/admin/report", keys: ["REPORT_MANAGEMENT", "REPORTS"] },
  { prefix: "/admin/ledger", keys: ["LEDGER", "LEDGER_MANAGEMENT"] },
];

const SEARCH_MODULES = [
  {
    label: "Users",
    viewAllHref: "/admin/users",
    api: "/api/admin/users",
    listKeys: ["users", "items"],
    titleKeys: ["full_name", "name"],
    subtitleKeys: ["email", "phone"],
    detailBase: "/admin/users",
  },
  {
    label: "Companies",
    viewAllHref: "/admin/company",
    api: "/api/admin/companies",
    listKeys: ["companies", "items"],
    titleKeys: ["name", "companyName"],
    subtitleKeys: ["email", "phone"],
    detailBase: "/admin/company",
  },
  {
    label: "Employees",
    viewAllHref: "/admin/employees",
    api: "/api/admin/employees",
    listKeys: ["employees", "items"],
    titleKeys: ["full_name", "name"],
    subtitleKeys: ["email", "department_name"],
    detailBase: "/admin/employees",
  },
  {
    label: "Jobs",
    viewAllHref: "/admin/jobs",
    api: "/api/admin/jobs",
    listKeys: ["jobs", "items"],
    titleKeys: ["title", "name"],
    subtitleKeys: ["status", "company_name"],
    detailBase: "/admin/jobs",
  },
  {
    label: "Leads",
    viewAllHref: "/admin/leads",
    api: "/api/admin/leads",
    listKeys: ["leads", "items"],
    titleKeys: ["name", "full_name"],
    subtitleKeys: ["email", "status"],
    detailBase: "/admin/leads",
  },
];

function pickStr(obj: Record<string, any>, keys: string[]): string {
  for (const k of keys) if (obj[k]) return String(obj[k]);
  return "—";
}

function extractList(data: any, keys: string[]): any[] {
  if (Array.isArray(data)) return data;
  for (const k of keys) if (Array.isArray(data[k])) return data[k];
  return [];
}

function _GlobalSearch() {
  const [query, setQuery] = createSignal("");
  const [open, setOpen] = createSignal(false);
  const [groups, setGroups] = createSignal<SearchGroup[]>([]);
  const [searching, setSearching] = createSignal(false);
  let wrapRef!: HTMLDivElement;
  let timer: ReturnType<typeof setTimeout>;

  const doSearch = async (q: string) => {
    const trimmed = q.trim();
    if (trimmed.length < 2) {
      setGroups([]);
      setOpen(false);
      return;
    }
    setSearching(true);
    const settled = await Promise.allSettled(
      SEARCH_MODULES.map(async (mod) => {
        const res = await fetch(`${mod.api}?search=${encodeURIComponent(trimmed)}&limit=4`).catch(
          () => null
        );
        if (!res?.ok) return null;
        const data = await res.json().catch(() => null);
        if (!data) return null;
        const items = extractList(data, mod.listKeys).slice(0, 4);
        if (!items.length) return null;
        return {
          label: mod.label,
          viewAllHref: mod.viewAllHref,
          results: items.map((item: any) => ({
            id: item.id,
            title: pickStr(item, mod.titleKeys),
            subtitle: pickStr(item, mod.subtitleKeys),
            href: `${mod.detailBase}/${item.id}`,
          })),
        } satisfies SearchGroup;
      })
    );
    setGroups(settled.flatMap((r) => (r.status === "fulfilled" && r.value ? [r.value] : [])));
    setOpen(true);
    setSearching(false);
  };

  const handleInput = (val: string) => {
    setQuery(val);
    clearTimeout(timer);
    if (val.trim().length < 2) {
      setGroups([]);
      setOpen(false);
      return;
    }
    timer = setTimeout(() => doSearch(val), 350);
  };

  const close = () => {
    setOpen(false);
    setQuery("");
    setGroups([]);
  };
  const onOutside = (e: MouseEvent) => {
    if (!wrapRef.contains(e.target as Node)) setOpen(false);
  };

  onMount(() => document.addEventListener("mousedown", onOutside));
  onCleanup(() => document.removeEventListener("mousedown", onOutside));

  return (
    <div ref={wrapRef!} class="relative ml-10 w-[560px] shrink-0">
      <Search
        size={20}
        class="pointer-events-none absolute left-5 top-1/2 -translate-y-1/2 text-[#9498ad]"
      />
      <input
        type="text"
        value={query()}
        placeholder="Search system resources..."
        onInput={(e) => handleInput(e.currentTarget.value)}
        onFocus={() => groups().length > 0 && setOpen(true)}
        onKeyDown={(e) => e.key === "Escape" && close()}
        class="h-[68px] w-full rounded-[24px] border-2 border-transparent bg-[#f4f5f8] pl-[60px] pr-6 text-[16px] text-[#0D0D2A] placeholder:text-[rgba(13,13,42,0.4)] outline-none transition-all focus:border-[#e5e7eb] focus:bg-white"
      />

      <Show when={open() && groups().length > 0}>
        <div class="absolute left-0 right-0 top-[calc(100%+8px)] z-[300] max-h-[460px] overflow-y-auto rounded-2xl border border-[#e5e7eb] bg-white shadow-xl">
          <For each={groups()}>
            {(group) => (
              <div class="border-b border-[#f1f2f5] px-4 py-3 last:border-b-0">
                <div class="mb-2 flex items-center justify-between">
                  <span class="text-[10px] font-bold uppercase tracking-[0.12em] text-[#9aa0b9]">
                    {group.label}
                  </span>
                  <A
                    href={group.viewAllHref}
                    onClick={close}
                    class="text-[12px] font-semibold text-[#FF5E13]"
                  >
                    View all
                  </A>
                </div>
                <div class="space-y-1">
                  <For each={group.results}>
                    {(item) => (
                      <A
                        href={item.href}
                        onClick={close}
                        class="flex items-center gap-3 rounded-xl px-2 py-2 hover:bg-[#f9fafb]"
                      >
                        <div class="flex h-8 w-8 items-center justify-center rounded-full bg-[rgba(255,94,19,0.12)] text-[12px] font-bold text-[#FF5E13]">
                          {item.title.trim().slice(0, 1).toUpperCase()}
                        </div>
                        <div class="min-w-0">
                          <p class="truncate text-[13px] font-semibold text-[#0D0D2A]">
                            {item.title}
                          </p>
                          <p class="truncate text-[12px] text-[rgba(13,13,42,0.55)]">
                            {item.subtitle}
                          </p>
                        </div>
                      </A>
                    )}
                  </For>
                </div>
              </div>
            )}
          </For>
        </div>
      </Show>

      <Show when={open() && !searching() && query().trim().length >= 2 && groups().length === 0}>
        <div class="absolute left-0 right-0 top-[calc(100%+8px)] z-[300] rounded-2xl border border-[#e5e7eb] bg-white px-4 py-6 text-center text-[13px] text-[rgba(13,13,42,0.55)]">
          No results for "{query()}"
        </div>
      </Show>
    </div>
  );
}

function _ShowTabs(props: {
  tabs: Tab[];
  isTabActive: (tab: Tab) => boolean;
  setTabsTrackEl: (el: HTMLDivElement) => void;
  setTabRefs: (
    fn: (prev: Record<string, HTMLAnchorElement>) => Record<string, HTMLAnchorElement>
  ) => void;
  tabIndicator: () => { left: number; width: number; ready: boolean };
}) {
  return (
    <Show when={props.tabs.length > 0}>
    <div
      ref={props.setTabsTrackEl}
      class="relative mb-6 mt-1 flex items-center gap-1 border-b border-[#e5e7eb]"
    >
      <For each={props.tabs}>
        {(tab) => (
          <A
            href={tab.href}
            ref={(el) => props.setTabRefs((prev) => ({ ...prev, [tab.href]: el }))}
            aria-current={props.isTabActive(tab) ? "page" : undefined}
            class={`px-4 pb-3 pt-3 text-[14px] font-semibold transition-colors ${
              props.isTabActive(tab)
                ? "text-[#FF5E13]"
                : "text-[rgba(13,13,42,0.6)] hover:text-[#0D0D2A]"
            }`}
          >
            {tab.label}
          </A>
        )}
      </For>
      <div
        class={`absolute bottom-0 h-[2px] bg-[#FF5E13] transition-all duration-300 ease-out ${props.tabIndicator().ready ? "opacity-100" : "opacity-0"}`}
        style={{ left: `${props.tabIndicator().left}px`, width: `${props.tabIndicator().width}px` }}
      />
    </div>
    </Show>
  );
}

export default function AdminShell(props: { children: JSX.Element }) {
  const location = useLocation();
  const navigate = useNavigate();
  const [_searchParams] = useSearchParams();

  const [checkedSession, setCheckedSession] = createSignal(true);
  const [adminName, setAdminName] = createSignal("Admin User");
  const [allowedModules, setAllowedModules] = createSignal<string[] | null>(null);
  const [isSuperAdmin, setIsSuperAdmin] = createSignal(false);
  const [sidebarOpen, setSidebarOpen] = createSignal(false);
  const [sidebarCollapsed, setSidebarCollapsed] = createSignal(false);
  const [unreadCount, setUnreadCount] = createSignal(0);
  const [pendingVerificationCount, setPendingVerificationCount] = createSignal(0);
  const [theme, setTheme] = createSignal<"light" | "dark">("light");
  const [routeTransitioning, _setRouteTransitioning] = createSignal(false);

  const [tabsTrackEl, _setTabsTrackEl] = createSignal<HTMLDivElement>();
  const [tabRefs, _setTabRefs] = createSignal<Record<string, HTMLAnchorElement>>({});
  const [_tabIndicator, _setTabIndicator] = createSignal({ left: 0, width: 0, ready: false });
  let contentScrollRef: HTMLDivElement | undefined;

  const logout = async () => {
    try {
      const accessToken =
        typeof sessionStorage !== "undefined"
          ? sessionStorage.getItem("nxtgauge_admin_access_token") || ""
          : "";
      await fetch("/api/auth/logout", {
        method: "POST",
        headers: {
          Accept: "application/json",
          "x-portal-target": "admin",
          ...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {}),
        },
        credentials: "include",
      }).catch(() => null);
    } finally {
      if (typeof sessionStorage !== "undefined") {
        sessionStorage.removeItem("nxtgauge_admin_access_token");
        sessionStorage.removeItem("nxtgauge_admin_preview");
      }
      navigate("/login", { replace: true });
    }
  };

  const tabs = createMemo<Tab[]>(() => {
    const path = location.pathname;
    for (const set of TAB_SETS) {
      if (set.prefixes.some((p) => path === p || path.startsWith(`${p}/`))) return set.tabs;
    }
    return [];
  });

  const isTabActive = (tab: Tab) =>
    tab.exact
      ? location.pathname === tab.href
      : location.pathname === tab.href || location.pathname.startsWith(`${tab.href}/`);

  const refreshTabIndicator = () => {
    const activeTab = tabs().find((tab) => isTabActive(tab));
    const track = tabsTrackEl();
    if (!activeTab || !track) {
      setTabIndicator((p) => ({ ...p, ready: false }));
      return;
    }
    const el = tabRefs()[activeTab.href];
    if (!el) return;
    setTabIndicator({ left: el.offsetLeft, width: el.offsetWidth, ready: true });
  };

  createEffect(() => {
    tabs();
    location.pathname;
    requestAnimationFrame(refreshTabIndicator);
  });

  createEffect(() => {
    location.pathname;
    if (contentScrollRef) {
      contentScrollRef.scrollTop = 0;
    }
  });

  const goToLoginAfterExpiry = () => {
    clearSessionExpired();
    if (typeof sessionStorage !== "undefined") {
      sessionStorage.removeItem("nxtgauge_admin_access_token");
      sessionStorage.removeItem("nxtgauge_admin_preview");
    }
    navigate(`/login?from=${encodeURIComponent(location.pathname + location.search)}`, {
      replace: true,
    });
  };

  onMount(() => {
    installSessionExpiryWatcher();
    installSessionRefreshLoop();

    const savedTheme = (
      typeof localStorage !== "undefined" ? localStorage.getItem("nxtgauge_admin_theme") : null
    ) as "light" | "dark" | null;
    const nextTheme = savedTheme === "dark" ? "dark" : "light";
    setTheme(nextTheme);
    if (typeof document !== "undefined") {
      document.documentElement.setAttribute("data-theme", nextTheme);
    }

    window.addEventListener("resize", refreshTabIndicator);
    onCleanup(() => window.removeEventListener("resize", refreshTabIndicator));

    // Fetch unread notification count and poll every 30 seconds
    const fetchUnreadCount = async () => {
      try {
        const accessToken =
          typeof sessionStorage !== "undefined"
            ? sessionStorage.getItem("nxtgauge_admin_access_token") || ""
            : "";
        if (!accessToken) return;
        const res = await fetch("/api/me/notifications/unread-count", {
          method: "GET",
          headers: {
            Accept: "application/json",
            "x-portal-target": "admin",
            ...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {}),
          },
          credentials: "include",
        });
        if (res.ok) {
          const data = await res.json();
          setUnreadCount(data.unread_count || 0);
        }
      } catch (e) {
        console.error("Failed to fetch unread count:", e);
      }
    };

    fetchUnreadCount();
    const interval = setInterval(fetchUnreadCount, 30000);
    onCleanup(() => clearInterval(interval));

    // Fetch pending-verification count and poll every 60 s so the sidebar badge
    // stays up-to-date as users submit role registrations.
    const fetchPendingVerificationCount = async () => {
      try {
        const accessToken =
          typeof sessionStorage !== "undefined"
            ? sessionStorage.getItem("nxtgauge_admin_access_token") || ""
            : "";
        if (!accessToken) return;
        const res = await fetch("/api/admin/verifications?status=PENDING&limit=200", {
          headers: {
            Accept: "application/json",
            ...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {}),
          },
          credentials: "include",
        });
        if (res.ok) {
          const data = await res.json().catch(() => ({}));
          const items = Array.isArray(data?.items) ? data.items : [];
          setPendingVerificationCount(items.length);
        }
      } catch {
        // non-fatal — badge stays at its last value
      }
    };

    fetchPendingVerificationCount();
    const verificationPoll = setInterval(fetchPendingVerificationCount, 60000);
    onCleanup(() => clearInterval(verificationPoll));

    const verify = async () => {
      // The server-verified session check below (/api/auth/session) is the
      // real security boundary. It redirects to /login on any failure.
      try {
        const accessToken =
          typeof sessionStorage !== "undefined"
            ? sessionStorage.getItem("nxtgauge_admin_access_token") || ""
            : "";
        const response = await fetch("/api/auth/session", {
          method: "GET",
          headers: {
            Accept: "application/json",
            "x-portal-target": "admin",
            ...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {}),
          },
          credentials: "include",
        });
        const payload = await response.json().catch(() => ({}));
        if (!response.ok || isExternalIdentity(payload)) throw new Error("Unauthorized");
        if (payload?.full_name) setAdminName(payload.full_name);

        const roleKey = String(
          payload?.active_role ||
            payload?.role ||
            payload?.user?.active_role ||
            payload?.user?.active_role_key ||
            payload?.user?.role ||
            payload?.user?.role_key ||
            ""
        ).toUpperCase();
        setIsSuperAdmin(roleKey === "SUPER_ADMIN");

        try {
          const res = await fetch("/api/runtime-config", {
            method: "GET",
            headers: {
              Accept: "application/json",
              "x-portal-target": "admin",
              ...(accessToken ? { Authorization: `Bearer ${accessToken}` } : {}),
            },
            credentials: "include",
          });
          const runtime = await res.json().catch(() => ({}));
          if (res.ok) {
            setAllowedModules(normalizeAllowedModules(runtime));
            const activeRole = String(
              runtime?.active_role || runtime?.user?.active_role || roleKey || ""
            ).toUpperCase();
            if (activeRole) setIsSuperAdmin(activeRole === "SUPER_ADMIN");
          } else {
            setAllowedModules(null);
          }
        } catch {
          setAllowedModules(null);
        }

        setCheckedSession(true);
      } catch {
          navigate(`/login?from=${encodeURIComponent(location.pathname + location.search)}`, {
          replace: true,
        });
      }
    };

    void verify();
  });

  const _pageTitle = createMemo(() => {
    const path = location.pathname;
    for (const entry of PAGE_TITLES) {
      if (
        entry.exact
          ? path === entry.prefix
          : path === entry.prefix || path.startsWith(`${entry.prefix}/`)
      ) {
        return entry.label;
      }
    }
    return "Admin";
  });

  const adminInitials = createMemo(() => {
    if (adminName().trim().toLowerCase() === "admin user") return "AD";
    const parts = adminName()
      .split(" ")
      .map((s) => s.trim())
      .filter(Boolean);
    if (parts.length === 0) return "U";
    if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
    return `${parts[0][0]}${parts[1][0]}`.toUpperCase();
  });

  createEffect(() => {
    const t = theme();
    if (typeof localStorage !== "undefined") localStorage.setItem("nxtgauge_admin_theme", t);
    if (typeof document !== "undefined") document.documentElement.setAttribute("data-theme", t);
  });

  const toggleTheme = () => setTheme((v) => (v === "dark" ? "light" : "dark"));
  const isDark = () => theme() === "dark";

  createEffect(() => {
    if (!checkedSession()) return;
    if (isSuperAdmin()) return;

    const modules = allowedModules();
    if (!modules || modules.length === 0) return;

    const path = location.pathname;
    if (path === "/admin") return;

    const matches = ROUTE_MODULE_KEYS.filter(
      (entry) => path === entry.prefix || path.startsWith(`${entry.prefix}/`)
    );
    const guard = matches.sort((a, b) => b.prefix.length - a.prefix.length)[0];
    if (!guard) return;

    const allowed = new Set(
      modules
        .map((m) =>
          String(m || "")
            .trim()
            .toUpperCase()
        )
        .filter(Boolean)
    );
    const ok = guard.keys.some((k) => allowed.has(String(k).toUpperCase()));
    if (ok) return;

    navigate(
      `/admin?denied=${encodeURIComponent(guard.keys[0])}&from=${encodeURIComponent(path)}`,
      { replace: true }
    );
  });

  return (
    <div
      class="min-h-screen"
      style={{
        background: isDark() ? "#0B1220" : "#F9FAFB",
        color: isDark() ? "#E5E7EB" : "#0D0D2A",
      }}
    >
      <Show when={sessionExpired()}>
        <div
          style="position:fixed;inset:0;z-index:100;display:flex;align-items:center;justify-content:center;background:rgba(13,13,42,0.55);padding:16px"
          role="alertdialog"
          aria-modal="true"
          aria-labelledby="session-expired-title"
        >
          <div
            style={{
              background: isDark() ? "#111827" : "white",
              color: isDark() ? "#E5E7EB" : "#0D0D2A",
              "border-radius": "12px",
              padding: "24px",
              "max-width": "380px",
              width: "100%",
              "box-shadow": "0 20px 40px rgba(0,0,0,0.25)",
            }}
          >
            <h2 id="session-expired-title" style="font-size:16px;font-weight:700;margin:0 0 8px">
              You have been logged out
            </h2>
            <p style="font-size:14px;color:#6B7280;margin:0 0 20px">
              Your session has expired for security reasons. Please log in again to continue.
            </p>
            <button
              type="button"
              onClick={goToLoginAfterExpiry}
              style="width:100%;padding:10px 16px;border-radius:8px;border:none;background:#FF5E13;color:white;font-size:14px;font-weight:600;cursor:pointer"
            >
              Log In Again
            </button>
          </div>
        </div>
      </Show>

      <Show
        when={checkedSession()}
        fallback={
          <div class="flex min-h-screen items-center justify-center text-[14px] text-[rgba(13,13,42,0.55)]">
            Checking session…
          </div>
        }
      >
        <div style="display:flex;height:100vh;overflow:hidden">
          <div
            class={`fixed inset-0 z-20 bg-black/30 transition-opacity lg:hidden ${sidebarOpen() ? "pointer-events-auto opacity-100" : "pointer-events-none opacity-0"}`}
            onClick={() => setSidebarOpen(false)}
          />

          <div style="height:100%;display:flex;flex-shrink:0">
            <AdminSidebar
              collapsed={sidebarCollapsed()}
              onToggle={() => setSidebarCollapsed((v) => !v)}
              onNavigate={() => setSidebarOpen(false)}
              adminName={adminName()}
              adminInitials={adminInitials()}
              theme={theme()}
              allowedModules={allowedModules()}
              isSuperAdmin={isSuperAdmin()}
              badgeCounts={{ pendingVerifications: pendingVerificationCount() }}
            />
          </div>

          <div class="flex min-w-0 flex-1 flex-col">
            <header
              style={`height:64px;border-bottom:1px solid ${isDark() ? "#1F2937" : "#E5E7EB"};background:${isDark() ? "#111827" : "white"};flex-shrink:0`}
            >
              <div style="display:flex;height:100%;width:100%;align-items:center;justify-content:flex-end;padding:0 32px">
                <div style="display:flex;align-items:center;gap:4px">
                  <button
                    type="button"
                    onClick={toggleTheme}
                    style={`display:inline-flex;width:36px;height:36px;align-items:center;justify-content:center;border-radius:8px;color:${isDark() ? "#CBD5E1" : "#6B7280"};background:none;border:none;cursor:pointer`}
                    aria-label="Toggle theme"
                  >
                    <Show when={isDark()} fallback={<Moon size={18} />}>
                      <Sun size={18} />
                    </Show>
                  </button>
                  <button
                    type="button"
                    style={`position:relative;display:inline-flex;width:36px;height:36px;align-items:center;justify-content:center;border-radius:8px;color:${isDark() ? "#CBD5E1" : "#6B7280"};background:none;border:none;cursor:pointer`}
                    aria-label="Notifications"
                  >
                    <Bell size={18} />
                    <Show when={unreadCount() > 0}>
                      <span
                        style={`position:absolute;right:8px;top:8px;width:7px;height:7px;border-radius:50%;border:2px solid ${isDark() ? "#111827" : "white"};background:#FF5E13`}
                      />
                    </Show>
                  </button>
                  <button
                    type="button"
                    style={`display:inline-flex;width:36px;height:36px;align-items:center;justify-content:center;border-radius:8px;color:${isDark() ? "#CBD5E1" : "#6B7280"};background:none;border:none;cursor:pointer`}
                    aria-label="Settings"
                  >
                    <Settings size={18} />
                  </button>
                  <div
                    style={`width:1px;height:24px;background:${isDark() ? "#1F2937" : "#E5E7EB"};margin:0 8px`}
                  />
                  <button
                    type="button"
                    style="display:inline-flex;align-items:center;gap:8px;border-radius:8px;padding:4px 8px 4px 4px;background:none;border:none;cursor:pointer"
                    aria-label="User menu"
                  >
                    <div style="width:32px;height:32px;border-radius:50%;background:linear-gradient(135deg,#FF5E13,#ff7a3d);display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;color:white;flex-shrink:0">
                      {adminInitials()}
                    </div>
                    <div style="text-align:left">
                      <p
                        style={`font-size:13px;font-weight:600;color:${isDark() ? "#E5E7EB" : "#111827"};line-height:1.3`}
                      >
                        {adminName()}
                      </p>
                      <p
                        style={`font-size:11px;color:${isDark() ? "#94A3B8" : "#6B7280"};line-height:1.3`}
                      >
                        Super Admin
                      </p>
                    </div>
                  </button>
                  <button
                    type="button"
                    onClick={() => void logout()}
                    style={`height:32px;border-radius:8px;border:1px solid ${isDark() ? "#374151" : "#E5E7EB"};background:${isDark() ? "#1F2937" : "white"};padding:0 12px;font-size:12px;font-weight:600;color:${isDark() ? "#E5E7EB" : "#374151"};cursor:pointer`}
                  >
                    Logout
                  </button>
                </div>
              </div>
            </header>

            <div
              ref={(el) => {
                contentScrollRef = el;
              }}
              class="min-h-0 flex-1 overflow-y-scroll"
              style={{ background: isDark() ? "#0B1220" : "#F9FAFB", "scrollbar-gutter": "stable" }}
            >
              <main
                class="admin-main"
                style={{
                  width: "100%",
                  padding: "28px 24px 36px 24px",
                  filter: isDark() ? "brightness(0.96)" : "none",
                  transition: "opacity 150ms ease",
                  opacity: routeTransitioning() ? "0.92" : "1",
                }}
              >
                {props.children}
              </main>
            </div>
          </div>
        </div>
      </Show>
    </div>
  );
}
