"use client";

import { useState, useEffect, useMemo, useCallback } from "react";
import type { ReactNode } from "react";
import Link from "next/link";
import { usePathname, useRouter } from "next/navigation";
import {
  LayoutDashboard,
  UserCheck,
  Award,
  FileText,
  ClipboardList,
  Users,
  UserPlus,
  BarChart2,
  ChevronRight,
  LogOut,
  Settings,
  Building2,
  Search,
  Layers, // ✅ NEW — icon for Scheme
  FileWarning,
  ShieldAlert,
  
} from "lucide-react";
import toast from "react-hot-toast";
import { useAuthStore } from "@/store/authStore";
import styles from "./Sidebar.module.css";

interface SidebarProps {
  isClientPortal?: boolean;
}
// ─────────────────────────────────────────────────────────────────────
// API base — adjust if your env var name is different
// ─────────────────────────────────────────────────────────────────────
const API_BASE =
  process.env.NEXT_PUBLIC_API_URL?.replace(/\/api$/, "") ??
  "http://localhost:3007";

// ✅ Super-admin user IDs that get full access (bypass permission checks)
// Add more IDs here if you need to grant unrestricted access to additional users.
const SUPER_ADMIN_IDS = new Set<number>([1, 8, 29]);

const isSuperAdmin = (userId: number | null): boolean =>
  userId !== null && SUPER_ADMIN_IDS.has(userId);

// ✅ NEW — role names that get full access (case-insensitive)
// If a user has any of these roles, they bypass permission checks.
const SUPER_ADMIN_ROLES = new Set<string>(["admin", "scheme", "super-admin", "superadmin"]);

const hasAdminRole = (roleNames: string[]): boolean =>
  roleNames.some((r) => SUPER_ADMIN_ROLES.has(r.toLowerCase()));

// ═════════════════════════════════════════════════════════════════════
// ✅ NEW — Permission cache (stale-while-revalidate)
// Fixes slow first-click on route change / after network switch.
// Strategy:
//   1. On mount, hydrate from localStorage INSTANTLY → sidebar renders fast.
//   2. Kick off network fetch in background to revalidate.
//   3. If cache is missing/expired, fall back to the original blocking fetch.
//   4. Fetch has hard timeout so bad wifi never hangs the UI.
// ═════════════════════════════════════════════════════════════════════
const PERM_CACHE_KEY = "qrs_sidebar_perm_cache_v1";
const PERM_CACHE_TTL = 1000 * 60 * 30; // 30 minutes — tune to taste
const FETCH_TIMEOUT_MS = 8000; // hard cap so bad networks don't hang forever

interface PermCache {
  userId: number;
  titles: string[];
  slugs: string[];
  hasAdminRole: boolean;
  savedAt: number;
}

function readPermCache(userId: number): PermCache | null {
  try {
    if (typeof window === "undefined") return null;
    const raw = localStorage.getItem(PERM_CACHE_KEY);
    if (!raw) return null;
    const c: PermCache = JSON.parse(raw);
    if (!c || typeof c !== "object") return null;
    if (c.userId !== userId) return null; // different user logged in
    if (Date.now() - c.savedAt > PERM_CACHE_TTL) return null; // expired
    return c;
  } catch {
    return null;
  }
}

function writePermCache(c: PermCache) {
  try {
    if (typeof window === "undefined") return;
    localStorage.setItem(PERM_CACHE_KEY, JSON.stringify(c));
  } catch {
    /* quota exceeded / private mode — ignore */
  }
}

function clearPermCache() {
  try {
    if (typeof window === "undefined") return;
    localStorage.removeItem(PERM_CACHE_KEY);
  } catch {
    /* ignore */
  }
}

// fetch with a hard timeout — critical for slow/switched wifi.
// Prevents the sidebar from waiting 30s on a dead socket.
function fetchWithTimeout(
  url: string,
  init: RequestInit = {},
  ms = FETCH_TIMEOUT_MS,
): Promise<Response> {
  const ctrl = new AbortController();
  const t = setTimeout(() => ctrl.abort(), ms);
  return fetch(url, { ...init, signal: ctrl.signal }).finally(() =>
    clearTimeout(t),
  );
}

type SubItem = { href: string; title: string; badge?: string | number; slug?: string };
type NavItem =
  | { href: string; icon: ReactNode; title: string; children?: never }
  | { href?: never; icon: ReactNode; title: string; children: SubItem[] };

// ─────────────────────────────────────────────────────────────────────
// NAV — your existing menu, untouched (Scheme added)
// ─────────────────────────────────────────────────────────────────────
const NAV: NavItem[] = [
  {
    icon: <LayoutDashboard size={16} />,
    title: "Dashboard",
    children: [{ href: "/dashboard", title: "Overview" }],
  },
  {
    icon: <Users size={16} />,
    title: "User Management",
    children: [
      { href: "/modules/users", title: "All Users" },
      { href: "/modules/users/roles", title: "All Roles" },
      { href: "/modules/users/permissions", title: "User Permissions" },
      { href: "/modules/users/matrix", title: "Permission Matrix" },
      { href: "/modules/users/conditions", title: "Conditions" },
      { href: "/modules/email-settings", title: "Email Settings" },
    ],
  },
  {
    icon: <Building2 size={16} />,
    title: "Companies",
    children: [
      { href: "/modules/companies", title: "All Companies" },
      { href: "/modules/companies/report", title: "Reports" },
    ],
  },
  {
    icon: <Search size={16} />,
    title: "Inquiries",
    children: [

      { href: "/modules/inquiries", title: "All Inquiries" },
      //  { href: "/modules/audit-requests", title: "Audit Request" },
      { href: "/modules/inquiries/report", title: "Reports" },
      { href: "/modules/notifications", title: "Notifications" },
    ],
  },
  {
    icon: <Search size={16} />,
    title: "Markeeting Executive",
    children: [

      { href: "/modules/leads", title: "Lead Management" },
      { href: "/modules/clients", title: "Client Module" },
    ],
  },
  {
    icon: <UserCheck size={16} />,
    title: "Audit Management",
    children: [
      { href: "/modules/audit-requests", title: "Audit Request", slug: "audit-requests" },
      { href: "/modules/company-audits", title: "Auditor Compliance", slug: "company-audits" },
      { href: "/modules/audit-schedules", title: "Schedule Audit", slug: "audit-schedules" },
      { href: "/modules/my-audits", title: "My Audits", slug: "my-audits" },
      { href: "/modules/checklist-templates", title: "Audit Checklist", slug: "checklist-templates" },
      { href: "/modules/audit-report", title: "Audit Report", slug: "audit-report" },
      { href: "/modules/meetings", title: "Meetings", slug: "meetings" },
      { href: "/modules/documents", title: "Iso-Document", slug: "documents" }, // ✅ Fixed slug
    ],
  },
  // ✅ NEW — NC section
  {
    icon: <FileWarning size={16} />,
    title: "NC",
    children: [
      { href: "/modules/previous-nc", title: "Previous NC", slug: "previous-nc" },
    ],
  },
  {
    icon: <Award size={16} />,
    title: "Certificate",
    children: [
      { href: "/modules/certificates", title: "All Certificates" },
      { href: "/modules/training-certificates", title: "Training Certificates" },
      { href: "/modules/aging-analysis", title: "Aging Analysis Report" },
      // { href: "/new_certificates", title: "New Certificates" },
      // { href: "/expired-certificate", title: "Expired", badge: 3 },
      // { href: "/iso_draft_certificate", title: "ISO Certificate" },
      { href: "/modules/excel", title: "Previous Certificates" },
    ],
  },
  // ✅ NEW — Scheme menu group
  // {
  //   icon: <Layers size={16} />,
  //   title: "Scheme",
  //   children: [
  //     { href: "/modules/schemes", title: "All Schemes" },
  //   ],
  // },
  {
    icon: <FileText size={16} />,
    title: "Template",
    children: [
      { href: "/modules/templates", title: "Templates" },
      { href: "/templates/templates_content", title: "Template Content" },
    ],
  },
  {
    icon: <ClipboardList size={16} />,
    title: "Job Registrar",
    children: [
      { href: "/modules/jobs", title: "Job List" },
      // { href: "/code_book", title: "Code Book" },
    ],
  },
  // {
  //   icon: <UserPlus size={16} />,
  //   title: "Lead Assign",
  //   children: [
  //     { href: "/clients", title: "Client Dashboard" },
  //     { href: "/client", title: "Client Data" },
  //   ],
  // },
  // {
  //   icon: <BarChart2 size={16} />,
  //   title: "Reports",
  //   children: [
  //     { href: "/reports", title: "Reports" },
  //     { href: "/projects", title: "Projects" },
  //   ],
  // },
  {
    icon: <Settings size={16} />,
    title: "Settings",
    children: [
      { href: "/modules/standards", title: "Standards" },
      { href: "/modules/countries", title: "Countries" },
    ],
  },
];

// ═══════════════════════════════════════════════════════════════════════════════
// ✨ CLIENT PORTAL NAV - simplified menu for client users
// ═══════════════════════════════════════════════════════════════════════════════
const CLIENT_NAV: NavItem[] = [
  {
    icon: <LayoutDashboard size={16} />,
    title: "Dashboard",
    children: [{ href: "/client/dashboard", title: "Overview" }],
  },
  {
    icon: <Building2 size={16} />,
    title: "Client",
    children: [
      { href: "/client/dashboard/company", title: "Client Info" },
      { href: "/client/dashboard/branches", title: "Branches" },
    ],
  },
  {
    icon: <Award size={16} />,
    title: "Audits",
    children: [
      { href: "/client/dashboard/audits", title: "My Audits" },
      // { href: "/client/dashboard/audits/progress", title: "Audit Progress" },
      // { href: "/client/dashboard/audits/report", title: "Audit Reports" },
    ],
  },
  {
    icon: <ShieldAlert size={16} />,
    title: "Non-Conformities",
    children: [
      { href: "/client/dashboard/ncs", title: "My NCs" },
    ],
  },
  {
    icon: <FileText size={16} />,
    title: "Certificates",
    children: [
      { href: "/client/dashboard/certificates", title: "My Certificates" },
      { href: "/client/dashboard/certificates/expiry", title: "Expiry Tracker" },
      // { href: "/client/dashboard/certificates/download", title: "Downloads" },
    ],
  },
  {
    icon: <Users size={16} />,
    title: "Team",
    children: [
      { href: "/client/dashboard/team", title: "Team Access" },
    ],
  },

];
// ─────────────────────────────────────────────────────────────────────
// Maps DB module slug → sidebar menu title
const MODULE_SLUG_TO_MENU: Record<string, string> = {
  // Dashboard
  dashboard: "Dashboard",

  // User Management
  users: "User Management",
  roles: "User Management",
  permissions: "User Management",
  "user-permissions": "User Management",
  "permission-matrix": "User Management",
  conditions: "User Management",
  "email-settings": "User Management",
  documents: "Audit Management",  // This maps the slug to the menu group
  "iso-document": "Audit Management",
  // Companies
  companies: "Companies",
  "company-reports": "Companies",

  // Inquiries
  inquiries: "Inquiries",
  "inquiry-reports": "Inquiries",
  notifications: "Inquiries",

  // Audit Management
  // ✅ FIXED — values must match the NAV title exactly ("Audit Management",
  // not "Auditor") or the menu group would never appear.
  auditor: "Audit Management",
  "company-audits": "Audit Management",
  audits: "Audit Management",
  "audit-requests": "Audit Management",
  "audit-schedules": "Audit Management",
  "my-audits": "Audit Management",
  "previous-nc": "NC",
  // Certificates
  certificates: "Certificate",
  // "new-certificates": "Certificate",
  // "expired-certificates": "Certificate",
  // "iso-certificates": "Certificate",
  "previous-certificates": "Certificate",

  // // ✅ NEW — Scheme
  // scheme: "Scheme",
  // schemes: "Scheme",

  // Templates
  templates: "Template",
  "template-content": "Template",

  // Job Registrar
  jobs: "Job Registrar",
  "code-book": "Job Registrar",

  // Lead Assign
  clients: "Lead Assign",
  leads: "Lead Assign",
  "lead-assign": "Lead Assign",

  // Reports
  // reports: "Reports",
  // projects: "Reports",

  // Settings
  standards: "Settings",
  countries: "Settings",
  settings: "Settings",
};

const ADMIN_ONLY_MENUS = new Set<string>([
  "User Management",
  "Settings",
  "Companies", // ← ADD THIS LINE
]);
// ─────────────────────────────────────────────────────────────────────
// Types
// ─────────────────────────────────────────────────────────────────────
interface PermissionModule {
  id: number;
  name: string;
  slug: string;
}

// ─────────────────────────────────────────────────────────────────────
// Helpers
// ─────────────────────────────────────────────────────────────────────
function getToken(): string {
  if (typeof window === "undefined") return "";
  return (
    localStorage.getItem("access_token") ||
    sessionStorage.getItem("access_token") ||
    ""
  );
}

function getUserIdFromToken(): number | null {
  try {
    const token = getToken();
    if (!token) return null;
    const payload = JSON.parse(atob(token.split(".")[1]));
    const id = payload?.sub ?? payload?.id ?? payload?.userId;
    return id ? Number(id) : null;
  } catch {
    return null;
  }
}

function getClientUserDisplay(): { name: string; initial: string } {
  if (typeof window === "undefined") return { name: "Client", initial: "C" };
  try {
    const clientUser = JSON.parse(localStorage.getItem("clientUser") || "{}");
    const fullName = [clientUser.firstName, clientUser.lastName]
      .filter(Boolean)
      .join(" ");
    const name = fullName || clientUser.email || "Client";
    return { name, initial: name[0]?.toUpperCase() || "C" };
  } catch {
    return { name: "Client", initial: "C" };
  }
}

// ─────────────────────────────────────────────────────────────────────
// 👇 NEW — Brand logos per scheme (QRS uses local file, TQS uses external URL)
// ─────────────────────────────────────────────────────────────────────
const SCHEME_LOGOS: Record<'QRS' | 'TQS', string> = {
  QRS: '/logo.png',
  TQS: 'https://crm.tqs.ae/tqslogo.png',
};

// 👇 NEW — reads the scheme (QRS or TQS) synchronously from the JWT.
//         Works for BOTH internal staff AND client portal users.
//         Runs SSR-safe (returns 'QRS' during server render) and is
//         cheap enough to call directly from useState initializer for
//         zero-delay first paint (no "QRS logo flash then TQS" glitch).
function getSchemeFromToken(isClientPortal: boolean): 'QRS' | 'TQS' {
  try {
    if (typeof window === 'undefined') return 'QRS';

    // Client portal — decode the client's JWT (has "scheme" claim)
    if (isClientPortal) {
      const token = localStorage.getItem('clientPortalToken') || '';
      if (!token) return 'QRS';
      const payload = JSON.parse(atob(token.split('.')[1]));
      const scheme = String(payload?.scheme || 'QRS').toUpperCase();
      return scheme === 'TQS' ? 'TQS' : 'QRS';
    }

    // Internal staff — decode the staff JWT (has "primary_scheme" claim)
    const token =
      localStorage.getItem('access_token') ||
      sessionStorage.getItem('access_token') ||
      '';
    if (!token) return 'QRS';
    const payload = JSON.parse(atob(token.split('.')[1]));
    const scheme = String(
      payload?.primary_scheme || payload?.scheme || 'QRS',
    ).toUpperCase();
    return scheme === 'TQS' ? 'TQS' : 'QRS';
  } catch {
    return 'QRS';
  }
}

// ✅ NEW — extract a module identifier from a permission object,
// regardless of which shape the backend returns it in.
// Returns { id?: number; slug?: string; name?: string }
function extractModuleInfo(perm: any): {
  id: number | null;
  slug: string | null;
  name: string | null;
} {
  if (!perm) return { id: null, slug: null, name: null };

  const id =
    perm?.module?.id ??
    perm?.module_id ??
    perm?.permission?.module?.id ??
    perm?.permission?.module_id ??
    null;

  const slug =
    perm?.module?.slug ??
    perm?.permission?.module?.slug ??
    null;

  const name =
    perm?.module?.name ??
    perm?.permission?.module?.name ??
    null;

  return {
    id: id !== null ? Number(id) : null,
    slug: slug ? String(slug).toLowerCase() : null,
    name: name ? String(name) : null,
  };
}

// ═════════════════════════════════════════════════════════════════════
// COMPONENT
// ═════════════════════════════════════════════════════════════════════
export default function Sidebar({ isClientPortal }: SidebarProps) {
  const pathname = usePathname();
  const router = useRouter();
  const logout = useAuthStore((s) => s.logout);
  const user = useAuthStore((s) => s.user);
  const hydrate = useAuthStore((s) => s.hydrate);

  // ── State ────────────────────────────────────────────────────────
  const [logoBroken, setLogoBroken] = useState(false);
  const [drawerOpen, setDrawerOpen] = useState(false);

  // 👇 NEW — brand scheme (QRS or TQS) for logo swap.
  //         Uses lazy initializer so the correct logo shows on FIRST render
  //         (no "QRS flash then TQS" for TQS users). Cheap synchronous JWT decode.
  const [scheme, setScheme] = useState<'QRS' | 'TQS'>(() =>
    getSchemeFromToken(!!isClientPortal),
  );

  // 👇 NEW — re-resolve scheme when the user changes (e.g. logout → login as
  //         a different scheme user without full page reload). Also handles the
  //         edge case where the JWT wasn't in localStorage at first paint.
  useEffect(() => {
    setScheme(getSchemeFromToken(!!isClientPortal));
    // Listen for storage changes across tabs (also fires on same-tab writes
    // in some browsers) so if user logs in a new session, sidebar updates.
    const onStorage = () => setScheme(getSchemeFromToken(!!isClientPortal));
    if (typeof window !== 'undefined') {
      window.addEventListener('storage', onStorage);
      return () => window.removeEventListener('storage', onStorage);
    }
  }, [isClientPortal, user]);

  // 👇 NEW — reset logoBroken when scheme changes so the new logo gets a chance
  //         to load (otherwise switching TQS→QRS would still show the fallback letter).
  useEffect(() => {
    setLogoBroken(false);
  }, [scheme]);

  // ── Permission state ─────────────────────────────────────────────
  const [currentUserId, setCurrentUserId] = useState<number | null>(null);
  const [permittedMenuTitles, setPermittedMenuTitles] =
    useState<Set<string> | null>(null);
  const [permissionsReady, setPermissionsReady] = useState(false);
  // ✅ NEW — track if user has admin role (separate from super-admin ID list)
  const [userHasAdminRole, setUserHasAdminRole] = useState(false);
  // ✅ NEW — permitted module slugs, used for per-child menu filtering.
  // null = show all children (admin / fail-open). A Set = filter children
  // whose `slug` is not present.
  const [permittedSlugSet, setPermittedSlugSet] =
    useState<Set<string> | null>(null);

  useEffect(() => {
    if (!isClientPortal) {
      hydrate();
    }
  }, [hydrate, isClientPortal]);

  // ── Resolve user ID on mount ─────────────────────────────────────
  const resolveUserId = useCallback((): number | null => {
    return getUserIdFromToken();
  }, []);

  // ── Load permissions ─────────────────────────────────────────────
  useEffect(() => {
    // Track whether the component is still mounted so a slow background
    // revalidate doesn't call setState after unmount.
    let isMounted = true;

    const loadPermissions = async () => {
      const userId = resolveUserId();
      if (isMounted) setCurrentUserId(userId);

      // ✅ Super admins (id 1, 8, etc.) see everything — skip fetch
      if (!userId || isSuperAdmin(userId)) {
        if (!isMounted) return;
        setPermittedMenuTitles(null); // null = show all
        setPermittedSlugSet(null); // ✅ null = show all children
        setPermissionsReady(true);
        return;
      }

      const token = getToken();
      if (!token) {
        if (!isMounted) return;
        setPermittedMenuTitles(null);
        setPermittedSlugSet(null); // ✅ null = show all children
        setPermissionsReady(true);
        return;
      }

      // ═══════════════════════════════════════════════════════════════
      // ✅ STEP 1 — Hydrate from cache INSTANTLY so sidebar renders fast.
      // This is what makes route-change / re-mount feel instant.
      // ═══════════════════════════════════════════════════════════════
      const cached = readPermCache(userId);
      if (cached && isMounted) {
        setPermittedMenuTitles(new Set(cached.titles));
        setPermittedSlugSet(new Set(cached.slugs));
        setUserHasAdminRole(cached.hasAdminRole);
        setPermissionsReady(true); // ← sidebar renders NOW, no skeleton wait
      }

      // ═══════════════════════════════════════════════════════════════
      // ✅ STEP 2 — Revalidate in background (or first-time fetch).
      // Wrapped in try/catch so a network error keeps the cached UI.
      // ═══════════════════════════════════════════════════════════════
      try {
        const headers = { Authorization: `Bearer ${token}` };

        // ✅ ROBUST — fetch modules + user (with roles & permissions)
        // + direct user-permissions in parallel.
        // The /api/users/:id endpoint returns the user with roles[] and
        // permissions[] populated (per your backend findOne).
        // We still keep /api/user-permissions/user/:id as a fallback in
        // case some deployments don't include relations on the user route.
        // ✅ NEW — fetchWithTimeout prevents hangs on dead wifi sockets.
        const [modulesRes, userRes, permsRes] = await Promise.all([
          fetchWithTimeout(`${API_BASE}/api/modules`, { headers }),
          fetchWithTimeout(`${API_BASE}/api/users/${userId}`, { headers }),
          fetchWithTimeout(
            `${API_BASE}/api/user-permissions/user/${userId}`,
            { headers },
          ),
        ]);

        // Modules MUST succeed — needed for slug→title mapping.
        if (!modulesRes.ok) {
          throw new Error(`Modules fetch failed: ${modulesRes.status}`);
        }

        const modulesData = await modulesRes.json();
        const moduleList: PermissionModule[] = Array.isArray(modulesData)
          ? modulesData
          : Array.isArray(modulesData?.data)
            ? modulesData.data
            : [];

        // Build a lookup: moduleId → module record
        const moduleById = new Map<number, PermissionModule>();
        for (const m of moduleList) moduleById.set(Number(m.id), m);

        // ── Collect permissions from BOTH sources ──────────────────
        const allPermissions: any[] = [];
        const roleNames: string[] = [];

        // Source 1: user object (gives us role permissions + direct permissions)
        if (userRes.ok) {
          try {
            const userData = await userRes.json();

            // Direct user permissions
            if (Array.isArray(userData?.permissions)) {
              allPermissions.push(...userData.permissions);
            }

            // Permissions inherited via roles
            if (Array.isArray(userData?.roles)) {
              for (const role of userData.roles) {
                if (role?.name) roleNames.push(String(role.name));
                if (Array.isArray(role?.permissions)) {
                  allPermissions.push(...role.permissions);
                }
              }
            }
          } catch (e) {
            console.warn("[Sidebar] User payload parse failed:", e);
          }
        } else {
          console.warn(
            `[Sidebar] /users/${userId} returned ${userRes.status} — falling back to user-permissions only.`,
          );
        }

        // Source 2: /user-permissions/user/:id (direct override permissions)
        if (permsRes.ok) {
          try {
            const permsData = await permsRes.json();
            const permList: any[] = Array.isArray(permsData)
              ? permsData
              : Array.isArray(permsData?.data)
                ? permsData.data
                : Array.isArray(permsData?.permissions)
                  ? permsData.permissions
                  : [];
            allPermissions.push(...permList);
          } catch (e) {
            console.warn("[Sidebar] user-permissions parse failed:", e);
          }
        }

        // ✅ Track admin role for ADMIN_ONLY_MENUS visibility
        const adminRoleDetected = hasAdminRole(roleNames);
        if (isMounted) setUserHasAdminRole(adminRoleDetected);

        // ── Build set of permitted module identifiers ──────────────
        const permittedModuleIds = new Set<number>();
        const permittedSlugs = new Set<string>();
        const permittedNames = new Set<string>();

        for (const perm of allPermissions) {
          const info = extractModuleInfo(perm);
          if (info.id !== null) permittedModuleIds.add(info.id);
          if (info.slug) permittedSlugs.add(info.slug);
          if (info.name) permittedNames.add(info.name.toLowerCase());
        }

        // ✅ Also resolve slugs from module IDs — some permission payloads
        // only carry module_id (no nested module.slug). Without this, a
        // permission known only by id would never match a child's `slug`.
        for (const modId of permittedModuleIds) {
          const mod = moduleById.get(modId);
          if (mod?.slug) permittedSlugs.add(mod.slug.toLowerCase());
        }

        // ── Map permitted modules → menu titles ────────────────────
        const titles = new Set<string>();
        titles.add("Dashboard"); // Always show Dashboard

        // Strategy A: walk every module from /modules; if its id is in
        // permittedModuleIds, map slug/name → menu title.
        for (const mod of moduleList) {
          const matchedById = permittedModuleIds.has(Number(mod.id));
          const matchedBySlug =
            mod.slug && permittedSlugs.has(mod.slug.toLowerCase());
          const matchedByName =
            mod.name && permittedNames.has(mod.name.toLowerCase());

          if (!matchedById && !matchedBySlug && !matchedByName) continue;

          // 1) try slug match
          const slugKey = mod.slug?.toLowerCase();
          if (slugKey && MODULE_SLUG_TO_MENU[slugKey]) {
            titles.add(MODULE_SLUG_TO_MENU[slugKey]);
          }

          // 2) try name match (kebab-cased)
          const nameKey = mod.name?.toLowerCase().replace(/\s+/g, "-");
          if (nameKey && MODULE_SLUG_TO_MENU[nameKey]) {
            titles.add(MODULE_SLUG_TO_MENU[nameKey]);
          }

          // 3) try direct title match
          const exactMatch = NAV.find(
            (m) => m.title.toLowerCase() === mod.name?.toLowerCase(),
          );
          if (exactMatch) titles.add(exactMatch.title);
        }

        // Strategy B (fallback): some permissions arrive WITHOUT being
        // in the modules list (e.g. legacy data). Map their slugs directly.
        for (const slug of permittedSlugs) {
          if (MODULE_SLUG_TO_MENU[slug]) {
            titles.add(MODULE_SLUG_TO_MENU[slug]);
          }
        }
        for (const name of permittedNames) {
          const nameKey = name.replace(/\s+/g, "-");
          if (MODULE_SLUG_TO_MENU[nameKey]) {
            titles.add(MODULE_SLUG_TO_MENU[nameKey]);
          }
          const exactMatch = NAV.find(
            (m) => m.title.toLowerCase() === name,
          );
          if (exactMatch) titles.add(exactMatch.title);
        }

        // ── Debug log (helpful while you wire things up) ───────────
        if (process.env.NODE_ENV !== "production") {
          // eslint-disable-next-line no-console
          console.log("[Sidebar] Permission resolution", {
            userId,
            roleNames,
            adminRoleDetected,
            permittedModuleIds: [...permittedModuleIds],
            permittedSlugs: [...permittedSlugs],
            permittedNames: [...permittedNames],
            visibleTitles: [...titles],
            fromCache: !!cached,
          });
        }

        // ✅ Save the permitted slugs to state — used by visibleNav to
        // filter individual children (e.g. show only "Auditor Request"
        // for a marketing user, only "Schedule Audit" for an auditor).
        if (isMounted) {
          setPermittedSlugSet(permittedSlugs);
          setPermittedMenuTitles(titles);
        }

        // ═══════════════════════════════════════════════════════════
        // ✅ STEP 3 — Save fresh result to cache for next mount.
        // ═══════════════════════════════════════════════════════════
        writePermCache({
          userId,
          titles: [...titles],
          slugs: [...permittedSlugs],
          hasAdminRole: adminRoleDetected,
          savedAt: Date.now(),
        });
      } catch (err) {
        console.error("[Sidebar] Permission fetch failed:", err);
        // ✅ If we already rendered from cache, KEEP that UI.
        // Only fail-open (show all) if we have nothing on screen yet.
        if (!cached && isMounted) {
          setPermittedMenuTitles(null); // fail open: show all
          setPermittedSlugSet(null); // ✅ fail open: show all children
        }
      } finally {
        if (isMounted) setPermissionsReady(true);
      }
    };

    loadPermissions();

    return () => {
      isMounted = false;
    };
  }, [resolveUserId]);

  // ── Compute visible NAV ──────────────────────────────────────────
  const visibleNav = useMemo(() => {
    // ✅ Super admins (id 1, 8, etc.) bypass all permission checks
    // ✅ Users with admin role also bypass permission checks
    const isAdmin = isSuperAdmin(currentUserId) || userHasAdminRole;

    // ✅ NEW — even super admin user 8 should NOT see "User Management"
    const hideUserManagement = currentUserId === 8;
    // ✨ CLIENT PORTAL: use simplified menu
    const navToUse = isClientPortal ? CLIENT_NAV : NAV;
    if (permittedMenuTitles === null) {
      // null = admin OR fetch failed → show all (still hide admin-only from non-admins)
      const baseNav = isAdmin
        ? navToUse
        : navToUse.filter((item) => !ADMIN_ONLY_MENUS.has(item.title));
      return hideUserManagement
        ? baseNav.filter((item) => item.title !== "User Management")
        : baseNav;
    }

    const filtered = navToUse.filter((item) => {
      if (ADMIN_ONLY_MENUS.has(item.title) && !isAdmin) return false;
      return permittedMenuTitles.has(item.title);
    }).map((item) => {
      // ✅ Per-child filtering: if a group's children have `slug`, only
      // show the children the user has permission for. Children WITHOUT a
      // `slug` are always shown (this leaves all other menus untouched).
      // Admins skip this entirely (they see every child).
      if (item.children && !isAdmin && permittedSlugSet) {
        const filteredChildren = item.children.filter((child) => {
          if (!child.slug) return true; // no slug = always visible
          return permittedSlugSet.has(child.slug);
        });
        return { ...item, children: filteredChildren };
      }
      return item;
    });

    return hideUserManagement
      ? filtered.filter((item) => item.title !== "User Management")
      : filtered;

  }, [permittedMenuTitles, currentUserId, userHasAdminRole, permittedSlugSet, isClientPortal]);

  // ── Auto-open menu containing active route ───────────────────────
  const activeMenuTitle = useMemo(
    () =>
      visibleNav.find((item) => item.children?.some((c) => c.href === pathname))
        ?.title,
    [pathname, visibleNav],
  );

  const [openMenus, setOpenMenus] = useState<string[]>(() =>
    activeMenuTitle ? [activeMenuTitle] : ["Dashboard"],
  );

  useEffect(() => {
    if (activeMenuTitle && !openMenus.includes(activeMenuTitle)) {
      setOpenMenus((prev) => [...prev, activeMenuTitle]);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [activeMenuTitle]);

  // ── Drawer behavior (unchanged) ──────────────────────────────────
  useEffect(() => {
    setDrawerOpen(false);
  }, [pathname]);

  useEffect(() => {
    if (drawerOpen) document.body.classList.add("qrs-no-scroll");
    else document.body.classList.remove("qrs-no-scroll");
    return () => document.body.classList.remove("qrs-no-scroll");
  }, [drawerOpen]);

  useEffect(() => {
    if (!drawerOpen) return;
    const handler = (e: KeyboardEvent) => {
      if (e.key === "Escape") setDrawerOpen(false);
    };
    window.addEventListener("keydown", handler);
    return () => window.removeEventListener("keydown", handler);
  }, [drawerOpen]);

  useEffect(() => {
    const mq = window.matchMedia("(min-width: 1025px)");
    const handler = (e: MediaQueryListEvent) => {
      if (e.matches) setDrawerOpen(false);
    };
    mq.addEventListener("change", handler);
    return () => mq.removeEventListener("change", handler);
  }, []);

  const toggle = (title: string) =>
    setOpenMenus((prev) =>
      prev.includes(title) ? prev.filter((t) => t !== title) : [...prev, title],
    );

  const handleLogout = async () => {
    try {
      await fetch("/api/auth/logout", { method: "POST" });
    } catch { }

    // ✅ NEW — clear cached permissions so next login starts clean
    clearPermCache();

    // ✨ CLIENT PORTAL LOGOUT
    if (isClientPortal) {
      localStorage.removeItem("clientPortalToken");
      localStorage.removeItem("clientUser");
      router.push("/client/login");
    } else {
      // ✨ INTERNAL LOGOUT (original)
      logout();
      router.push("/login");
    }

    toast.success("Logged out");
    router.refresh();
  };

  const handleKeyToggle = (e: React.KeyboardEvent, title: string) => {
    if (e.key === "Enter" || e.key === " ") {
      e.preventDefault();
      toggle(title);
    }
  };

  const clientDisplay = isClientPortal ? getClientUserDisplay() : null;

  // ─────────────────────────────────────────────────────────────────
  // RENDER
  // ─────────────────────────────────────────────────────────────────
  return (
    <>
      <button
        type="button"
        className={`${styles.menuToggle} ${drawerOpen ? styles.menuToggleOpen : ""}`}
        onClick={() => setDrawerOpen((v) => !v)}
        aria-label={drawerOpen ? "Close menu" : "Open menu"}
        aria-expanded={drawerOpen}
        aria-controls="qrs-sidebar"
      >
        <span className={styles.menuLine} />
        <span className={styles.menuLine} />
        <span className={styles.menuLine} />
      </button>

      <div
        className={`${styles.backdrop} ${drawerOpen ? styles.backdropOpen : ""}`}
        onClick={() => setDrawerOpen(false)}
        aria-hidden="true"
      />

      <aside
        id="qrs-sidebar"
        className={`${styles.sidebar} ${drawerOpen ? styles.sidebarOpen : ""}`}
        aria-label="Main navigation"
      >
        {/* ── Logo ── */}
        <Link
          href="/dashboard"
          className={styles.logo}
          aria-label="Go to Dashboard"
          style={{ cursor: "pointer", textDecoration: "none" }}
        >
          <div className={styles.logoImgWrap}>
            {logoBroken ? (
              /* 👇 fallback letter: 'Q' for QRS, 'T' for TQS */
              <span className={styles.logoFallback}>{scheme[0]}</span>
            ) : (
              <img
                /* 👇 CHANGED — was hardcoded "/qrs.png", now picks logo based on user's scheme */
                src={SCHEME_LOGOS[scheme]}
                alt={scheme}
                className={styles.logoImg}
                onError={() => setLogoBroken(true)}
              />
            )}
          </div>
        </Link>

        {/* ── Permission badge for non-admin ── */}
        {/* ✅ Hide badge for super admins (id 1, 8, etc.) and admin-role users */}
        {permissionsReady &&
          currentUserId &&
          !isSuperAdmin(currentUserId) &&
          !userHasAdminRole &&
          permittedMenuTitles !== null && (
            <div
              style={{
                margin: "8px 12px 0",
                padding: "6px 10px",
                background: "rgba(192, 132, 252, 0.12)",
                border: "1px solid rgba(192, 132, 252, 0.25)",
                borderRadius: 8,
                fontSize: 11,
                color: "#e9d5ff",
                fontWeight: 600,
                textAlign: "center",
                letterSpacing: "0.3px",
              }}
            >
              🔒 {visibleNav.length} module{visibleNav.length !== 1 ? "s" : ""}{" "}
              accessible
            </div>
          )}

        {/* ── Nav (with skeleton while loading) ── */}
        <nav className={styles.nav} aria-label="Sidebar">
          {!permissionsReady ? (
            <NavSkeleton />
          ) : visibleNav.length === 0 ? (
            <div
              style={{
                padding: "20px 14px",
                fontSize: 12,
                color: "rgba(255,255,255,0.5)",
                textAlign: "center",
                lineHeight: 1.5,
              }}
            >
              No modules accessible.
              <br />
              Contact your administrator.
            </div>
          ) : (
            visibleNav.map((item) => {
              if (item.children) {
                const isOpen = openMenus.includes(item.title);
                const isAnyActive = item.children.some(
                  (c) => c.href === pathname,
                );
                const submenuId = `submenu-${item.title.replace(/\s+/g, "-").toLowerCase()}`;

                return (
                  <div key={item.title}>
                    <button
                      type="button"
                      className={`${styles.link} ${isAnyActive ? styles.linkActive : ""}`}
                      onClick={() => toggle(item.title)}
                      onKeyDown={(e) => handleKeyToggle(e, item.title)}
                      aria-expanded={isOpen}
                      aria-controls={submenuId}
                    >
                      <span className={styles.linkInner}>
                        <span className={styles.iconWrap}>{item.icon}</span>
                        {item.title}
                      </span>
                      <ChevronRight
                        size={14}
                        className={`${styles.arrow} ${isOpen ? styles.arrowOpen : ""}`}
                        aria-hidden="true"
                      />
                    </button>

                    <div
                      id={submenuId}
                      className={`${styles.submenu} ${isOpen ? styles.submenuOpen : ""}`}
                      role="region"
                      aria-label={`${item.title} submenu`}
                    >
                      <div className={styles.subList}>
                        {item.children.map((child) => {
                          const isActive = pathname === child.href;
                          return (
                            <Link
                              key={child.href}
                              href={child.href}
                              aria-current={isActive ? "page" : undefined}
                              className={`${styles.subLink} ${isActive ? styles.subLinkActive : ""}`}
                            >
                              <span
                                className={styles.subDot}
                                aria-hidden="true"
                              />
                              {child.title}
                              {child.badge !== undefined && (
                                <span className={styles.badge}>
                                  {child.badge}
                                </span>
                              )}
                            </Link>
                          );
                        })}
                      </div>
                    </div>
                  </div>
                );
              }

              const isActive = pathname === item.href;
              return (
                <Link
                  key={item.href}
                  href={item.href}
                  aria-current={isActive ? "page" : undefined}
                  className={`${styles.link} ${isActive ? styles.linkActive : ""}`}
                >
                  <span className={styles.linkInner}>
                    <span className={styles.iconWrap}>{item.icon}</span>
                    {item.title}
                  </span>
                </Link>
              );
            })
          )}
        </nav>

        {/* ── Footer ── */}
        <div className={styles.footer}>
          <div className={styles.avatar} aria-hidden="true">
            {isClientPortal ? clientDisplay!.initial : user?.username?.[0]?.toUpperCase() || "A"}
          </div>
          <div className={styles.footerText}>
            <span className={styles.footerName}>
              {isClientPortal ? clientDisplay!.name : user?.username || "Admin"}
            </span>
            <span className={styles.footerRole}>
              {isClientPortal ? "Client" : user?.role || "Administrator"}
            </span>
          </div>
          <button
            type="button"
            className={styles.logoutBtn}
            onClick={handleLogout}
            title="Logout"
            aria-label="Logout"
          >
            <LogOut size={15} />
          </button>
        </div>
      </aside>
    </>
  );
}

// ═════════════════════════════════════════════════════════════════════
// Skeleton loader (shown while permissions are fetching)
// ═════════════════════════════════════════════════════════════════════
function NavSkeleton() {
  return (
    <div
      style={{
        display: "flex",
        flexDirection: "column",
        gap: 6,
        padding: "4px 4px",
      }}
    >
      {[1, 2, 3, 4, 5, 6].map((i) => (
        <div
          key={i}
          style={{
            height: 38,
            borderRadius: 10,
            background:
              "linear-gradient(90deg, rgba(255,255,255,0.04) 0%, rgba(255,255,255,0.10) 50%, rgba(255,255,255,0.04) 100%)",
            backgroundSize: "200% 100%",
            animation: `qrsShimmer 1.4s ease-in-out infinite`,
          }}
        />
      ))}
      <style>{`
        @keyframes qrsShimmer {
          0%   { background-position: 200% 0; }
          100% { background-position: -200% 0; }
        }
      `}</style>
    </div>
  );
}