/* Ported from apps/web/src/components/layout/MainLayout.tsx,
 * apps/web/src/components/layout/Sidebar.tsx,
 * apps/web/src/components/layout/SidebarUserMenu.tsx and
 * apps/web/src/components/layout/nav-active.ts.
 *
 * Substitutions (see CONVENTIONS.md):
 * - auth / tenant / impersonation / 2FA machinery → a hardcoded fictional
 *   user+tenant fixture (Alex Doe @ Acme). The loading / signin / 2FA branches
 *   never render in the prototype and are dropped.
 * - react-router → T.Router (the shim's <Link>; the app's NavLink only ever
 *   received a pre-merged className string from AppSidebar, so Link is
 *   equivalent). Routes outside the prototype toast instead of navigating.
 * - Shell widgets that render nothing for an active, non-trial tenant with no
 *   in-flight jobs (TrialCountdownBanner, ExpiryPaywall, DeveloperBar,
 *   RunMonitorProvider / RunMonitor / WorkflowJobsFeeder / ImportJobCard,
 *   usePlanAnalytics) are inert local stubs — see NEEDS-layout.md.
 * - TenantSwitcher is not ported: the fixture has a single tenant, so the
 *   static SidebarTenantRow branch always renders (TRA-1040 behaviour). */
(() => {
  const T = window.T;
  const { useState, useEffect, useCallback, createContext, useContext } = React;
  const { Link } = T.Router;
  const {
    AppSidebar,
    SidebarBrand,
    SidebarTenantRow,
    TrayoLogoMark,
    SidebarUserMenu: SharedSidebarUserMenu,
    avatarInitials,
    SidebarTriggerProvider,
  } = T.UI;
  const {
    Activity,
    ArrowLeftCircle,
    Bookmark,
    Building2,
    Command,
    CompanySettingsIcon,
    GearKeyholeIcon,
    GitBranch,
    Home,
    LifeBuoy,
    List,
    LogOut,
    Mail,
    Search,
    SlidersHorizontal,
    Sparkles,
    TrayoAgentIcon,
    User: UserIcon,
    Users,
    Zap,
  } = T.Icons;

  /* packages/feature-flags/src/index.ts */
  const HOME_REDESIGN_FLAG = 'home-redesign-enabled';

  /* ---------- fixture: replaces useAuth / tenant fetching ---------- */
  const FIXTURE_TENANT = {
    id: 'tenant-acme',
    name: 'Acme',
    logo_url: null,
    website: 'acme.dev',
    status: 'active',
    role: 'Admin',
  };
  const FIXTURE_USER = {
    id: 'user-alex',
    name: 'Alex Doe',
    email: 'alex@acme.dev',
  };
  const FIXTURE_AUTH = {
    user: FIXTURE_USER,
    userRole: 'admin',
    loading: false,
    isSuperAdmin: false,
    tenantId: FIXTURE_TENANT.id,
    tenants: [FIXTURE_TENANT],
    setTenant: () => {},
    logout: () => T.toast('Not part of this prototype'),
  };
  const useAuth = () => FIXTURE_AUTH;

  /* Routes that exist in the prototype; every other nav href toasts. */
  const LIVE_ROUTES = new Set([
    '/',
    '/user/home',
    '/user/home/signals',
    '/user/home/accounts',
    '/user/home/people',
    '/user/lists',
  ]);
  const isLiveRoute = (href) => LIVE_ROUTES.has(href.split('?')[0]);
  const toastNotInPrototype = () => T.toast('Not part of this prototype');

  /* Portal target for DrawerShell's `screen` mode — see the content column in
     MainLayout below. An id rather than a context because the consumer
     (drawers.jsx) only needs the element at render time, and a context would
     have to thread through every page between here and the drawer. */
  const CONTENT_COLUMN_ID = 't-skeleton-content-column';

  /* ---------- contexts (MainLayout.tsx) ---------- */
  const CompanyContext = createContext({
    company: null,
    setCompany: () => {},
    refreshCompany: async () => {},
  });
  const useCompany = () => useContext(CompanyContext);

  const AdminContext = createContext({
    selectedCompany: null,
    selectedUser: null,
    isImpersonating: false,
    patchSelectedCompany: () => {},
  });
  const useAdmin = () => useContext(AdminContext);

  /* ---------- nav-active.ts (ported verbatim) ---------- */
  function isNavItemActive(href, currentPath, _currentSearch) {
    const path = href.split('?')[0];
    const current =
      currentPath === '/user/home' ? '/user/home/signals' : currentPath;
    if (current === path) return true;
    // Treat detail routes as active for their list parent.
    if (path === '/user/people' && current.startsWith('/user/people/'))
      return true;
    if (path === '/user/accounts' && current.startsWith('/user/accounts/'))
      return true;
    return false;
  }

  /* ---------- SidebarUserMenu.tsx ---------- */

  // Mirror Header.tsx role labelling. `isSuperAdmin` is the EFFECTIVE flag
  // (false during impersonation per TRA-792).
  function displayRoleLabel(opts) {
    if (opts.isSuperAdmin) return 'Super admin';
    return opts.tenantRole;
  }

  function SidebarUserMenu() {
    const location = T.Router.useLocation();
    const { user, isSuperAdmin, tenantId, tenants, logout } = useAuth();
    const { selectedUser, isImpersonating } = useAdmin();
    const { resolvedTheme, setTheme } = T.useTheme();

    const tenantRole = tenants.find((t) => t.id === tenantId)?.role ?? null;
    const roleLabel = displayRoleLabel({ isSuperAdmin, tenantRole });
    const isTenantView = selectedUser && !selectedUser.id;

    const isOnAccountRoute = location.pathname.startsWith('/user/settings');

    /* Impersonation never happens in the prototype. */
    const handleExitImpersonation = toastNotInPrototype;

    const handleLogout = () => {
      logout();
    };

    if (!user) return null;

    // During impersonation/tenant-view, mirror the top context card so the
    // visible identity stays consistent across the sidebar.
    const displayName =
      (selectedUser?.id ? selectedUser?.name || selectedUser?.email : null) ??
      user.email;

    const items = [
      // Settings always hosts the Slack workspace connect section, so it's
      // always available outside tenant-view.
      ...(!isTenantView
        ? [
            {
              key: 'settings',
              icon: <GearKeyholeIcon size={16} />,
              label: 'Settings',
              href: '/user/settings',
              testId: 'user-menu-settings',
            },
          ]
        : []),
      {
        key: 'help',
        icon: <LifeBuoy size={16} />,
        label: 'Help & support',
        externalHref: 'mailto:support@trayo.ai?subject=Trayo%20AI%20App',
        target: '_blank',
        rel: 'noreferrer',
      },
      ...(isImpersonating
        ? [
            {
              key: 'exit-impersonation',
              icon: <ArrowLeftCircle size={16} />,
              label: 'Exit impersonation',
              onSelect: handleExitImpersonation,
            },
          ]
        : []),
      {
        key: 'sign-out',
        icon: <LogOut size={16} />,
        label: 'Sign out',
        onSelect: handleLogout,
        separated: true,
      },
    ];

    return (
      <SharedSidebarUserMenu
        name={isTenantView ? 'Tenant View' : displayName}
        role={isTenantView ? 'Viewing all users' : (roleLabel ?? undefined)}
        initials={avatarInitials(displayName)}
        avatar={
          isTenantView ? (
            <div className="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-gradient-to-br from-indigo-500 to-violet-600 text-white ring-2 ring-indigo-400/50">
              <Building2 size={16} />
            </div>
          ) : undefined
        }
        active={isOnAccountRoute}
        title={
          isImpersonating && selectedUser?.email
            ? `Viewing as ${selectedUser?.name ?? selectedUser.email} — superAdmin impersonation`
            : undefined
        }
        header={
          user.email ? (
            <React.Fragment>
              {isImpersonating && (
                <div className="text-[11px] uppercase tracking-wide text-text-muted">
                  Signed in as
                </div>
              )}
              <div className="truncate text-sm font-medium text-text-primary">
                {user.email}
              </div>
              {roleLabel && (
                <div className="truncate text-xs text-text-muted">
                  {roleLabel}
                </div>
              )}
            </React.Fragment>
          ) : undefined
        }
        theme={resolvedTheme}
        onThemeChange={setTheme}
        items={items}
        renderMenuLink={({
          href,
          className,
          role,
          onClick,
          children,
          'data-testid': testId,
        }) =>
          isLiveRoute(href) ? (
            <Link
              to={href}
              className={className}
              role={role}
              onClick={onClick}
              data-testid={testId}
            >
              {children}
            </Link>
          ) : (
            /* Route not in the prototype: keep the row, toast instead of
               navigating (still lets the menu's own onClick close it). */
            <a
              href={href}
              className={className}
              role={role}
              data-testid={testId}
              onClick={(e) => {
                e.preventDefault();
                if (onClick) onClick(e);
                toastNotInPrototype();
              }}
            >
              {children}
            </a>
          )
        }
        triggerTestId="sidebar-user-menu-trigger"
      />
    );
  }

  /* ---------- Sidebar.tsx ---------- */

  const renderLink = ({ href, className, children }) =>
    isLiveRoute(href) ? (
      <Link to={href} className={className}>
        {children}
      </Link>
    ) : (
      /* App uses NavLink; entries outside the prototype toast instead. */
      <a
        href={href}
        className={className}
        onClick={(e) => {
          e.preventDefault();
          toastNotInPrototype();
        }}
      >
        {children}
      </a>
    );

  function Sidebar({ className, open, onOpenChange, collapsed, onCollapsedChange }) {
    const location = T.Router.useLocation();
    const { user, loading: authLoading, tenants } = useAuth();
    const { selectedCompany, isImpersonating } = useAdmin();
    /* usePermissions / useTenantFeatures / useChatEnabled → the fixture user is
       a full tenant admin with chat enabled, so every nav entry renders. */
    const permissions = { has: () => true };
    const canSeeWorkflows = true;
    const chatEnabled = true;
    // TRA-1298 redesign gate: on → Home/Lists + the Outreach entry.
    const homeRedesign = T.useFeatureFlag(HOME_REDESIGN_FLAG);
    /* useEntitlements → enterprise defaults (no self-serve billing): the same
       fallbacks the app applies while entitlements are unloaded. */
    const entitlements = null;
    const planAllowsUsers = entitlements?.canManageUsers ?? true;
    const planAllowsSettings = entitlements?.canEditTenantSettings ?? true;
    const showBillingUpsell = entitlements?.hasSelfServeBilling ?? false;

    /* GET_TENANT_SUBSCRIPTION is skipped when showBillingUpsell is false. */
    const selfServePlan = null;

    // Active-route helper preserving the existing nested-route special-casing.
    const currentPath = location.pathname;
    const isActive = (href) =>
      isNavItemActive(href, currentPath, location.search);

    // Trayo chat link is gated by `useChatEnabled()`.
    const chatItem = chatEnabled
      ? [
          {
            label: 'Trayo Agent',
            href: '/user/chat',
            // The branded glyph, same as the floating chat bubble — one mark
            // for the agent everywhere.
            icon: <TrayoAgentIcon size={16} />,
          },
        ]
      : [];
    // Self-serve setup for the Trayo MCP. Visible to all users (not gated).
    const mcpItem = {
      label: 'MCP',
      href: '/user/mcp',
      icon: <Command size={16} strokeWidth={1.5} />,
    };

    // Redesign (TRA-1298) nav: Home's three tabs lead as first-class entries.
    const redesignItems = [
      {
        label: 'Signals',
        href: '/user/home/signals',
        icon: <Zap size={16} strokeWidth={1.5} />,
      },
      {
        label: 'Accounts',
        href: '/user/home/accounts',
        icon: <Building2 size={16} strokeWidth={1.5} />,
      },
      {
        label: 'People',
        href: '/user/home/people',
        icon: <Users size={16} strokeWidth={1.5} />,
      },
      {
        label: 'Find',
        href: '/user/find',
        icon: <Search size={16} strokeWidth={1.5} />,
      },
      {
        label: 'Lists',
        href: '/user/lists',
        icon: <Bookmark size={16} strokeWidth={1.5} />,
      },
      {
        label: 'Outreach',
        href: '/user/outreach',
        // Mail is what "Outreach" means everywhere else in the app.
        icon: <Mail size={16} strokeWidth={1.5} />,
      },
      {
        label: 'Signal editor',
        href: '/user/signals',
        icon: <SlidersHorizontal size={16} strokeWidth={1.5} />,
      },
      ...chatItem,
      mcpItem,
    ];

    // Legacy nav (redesign flag off): unreachable here (flag stub is true),
    // kept for fidelity with the source.
    const legacyItems = [
      {
        label: 'Newsfeed',
        href: '/user/events',
        icon: <Home size={16} strokeWidth={1.5} />,
      },
      {
        label: 'Find',
        href: '/user/find',
        icon: <Search size={16} strokeWidth={1.5} />,
      },
      { label: 'Accounts', href: '/user/accounts', icon: <List size={16} strokeWidth={1.5} /> },
      { label: 'Contacts', href: '/user/people', icon: <Users size={16} strokeWidth={1.5} /> },
      { label: 'Signals', href: '/user/signals', icon: <Activity size={16} strokeWidth={1.5} /> },
      ...chatItem,
      mcpItem,
    ];

    const homeItems = (homeRedesign ? redesignItems : legacyItems).map((item) => ({
      ...item,
      active: isActive(item.href),
    }));

    // Each admin item is AND-gated by the relevant role permission and plan
    // entitlement. The section header auto-hides when adminItems is empty.
    const adminItems = [
      ...(canSeeWorkflows
        ? [
            {
              label: 'Workflows',
              href: '/user/workflows',
              icon: <GitBranch size={16} strokeWidth={1.5} />,
            },
          ]
        : []),
      ...(permissions.has('users:read') && planAllowsUsers
        ? [
            {
              label: 'Team members',
              href: '/user/users',
              icon: <UserIcon size={16} strokeWidth={1.5} />,
            },
          ]
        : []),
      ...(permissions.has('users:manage') && planAllowsSettings
        ? [
            {
              label: 'Company settings',
              href: '/user/company',
              icon: <CompanySettingsIcon size={16} />,
            },
          ]
        : []),
    ].map((item) => ({ ...item, active: isActive(item.href) }));

    const planItems = showBillingUpsell
      ? [
          {
            label: 'Plans',
            href: '/user/plans',
            icon: <Sparkles size={16} strokeWidth={1.5} />,
            active: isActive('/user/plans'),
            badge: selfServePlan ? (
              <span
                data-testid="sidebar-plan-badge"
                className="rounded border border-current/40 px-1.5 py-0.5 text-2xs font-semibold uppercase tracking-wider"
              >
                {selfServePlan === 'GROWTH' ? 'Growth' : 'Pro'}
              </span>
            ) : undefined,
          },
        ]
      : [];

    const isUserRoute = currentPath.startsWith('/user');
    const showUserNavigation = isUserRoute && !authLoading;

    // Home/Admin are collapsible (header doubles as a toggle with a chevron).
    // Plans is a header-less group, so it stays static.
    const navSections = showUserNavigation
      ? [
          { label: 'Home', collapsible: true, items: homeItems },
          ...(adminItems.length > 0
            ? [
                {
                  label: 'Admin',
                  collapsible: true,
                  items: adminItems,
                },
              ]
            : []),
          ...(planItems.length > 0 ? [{ items: planItems }] : []),
        ]
      : [];

    const logo = (
      <Link
        to="/"
        aria-label="Home"
        className="rounded focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-400/60"
      >
        <SidebarBrand />
      </Link>
    );

    // The company identity row under the brand. Users in 2+ tenants get a real
    // switcher; single-tenant users get a static row with no switch affordance
    // (TRA-1040). The fixture has exactly one tenant, so TenantSwitcher is not
    // ported — the static branch always renders.
    const canSwitchTenant = !isImpersonating && tenants.length > 1;

    const accountSwitcher =
      showUserNavigation && selectedCompany ? (
        canSwitchTenant ? null /* <TenantSwitcher …/> — unreachable in the prototype */ : (
          <SidebarTenantRow
            name={selectedCompany?.name ?? ''}
            domain={selectedCompany?.website}
            logoUrl={selectedCompany?.logo_url}
          />
        )
      ) : undefined;

    // The credit meter is parked for everyone right now (see the app source),
    // so the user menu is the sole footer block.
    const userMenu =
      !authLoading && user ? (
        <div className="space-y-2.5">
          <SidebarUserMenu />
        </div>
      ) : undefined;

    return (
      <AppSidebar
        nav={navSections}
        renderLink={renderLink}
        logo={logo}
        logoMark={
          <Link
            to="/"
            aria-label="Home"
            className="rounded focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-400/60"
          >
            <TrayoLogoMark className="h-6 w-auto text-text-primary" />
          </Link>
        }
        accountSwitcher={accountSwitcher}
        userMenu={userMenu}
        open={open}
        onOpenChange={onOpenChange}
        collapsed={collapsed}
        onCollapsedChange={onCollapsedChange}
        className={className}
      />
    );
  }

  /* ---------- MainLayout.tsx ---------- */

  /* Inert shells: each of these renders nothing (or just children) for an
     active, non-trial tenant with no in-flight jobs — which is the prototype's
     permanent state. Kept as call sites so the shell DOM matches the app. */
  const TrialCountdownBanner = () => null; // fixture tenant is not on a trial
  const ExpiryPaywall = ({ children }) => children; // active tenant → passthrough
  const DeveloperBar = () => null; // renders null outside dev/preview builds
  const RunMonitorProvider = ({ children }) => children; // no job sessions ever
  const RunMonitor = () => null; // self-hides with no active/recent sessions
  const WorkflowJobsFeeder = () => null; // nothing to poll in the prototype

  /* App key: 'web-sidebar-collapsed' (t-skeleton-* per CONVENTIONS rule 6). */
  const SIDEBAR_COLLAPSED_KEY = 't-skeleton-sidebar-collapsed';

  function MainLayout({ children }) {
    const location = T.Router.useLocation();
    /* audience.jsx loads after this file — resolve the provider at render
       time, and degrade to a passthrough while it isn't ported yet. */
    const AudienceProvider =
      T.AudienceProvider || (({ children }) => children);

    // usePlanAnalytics (PostHog plan-tier super-props) → no-op in the port.

    // Mobile sidebar state
    const [isMobileSidebarOpen, setIsMobileSidebarOpen] = useState(false);
    // Stable opener handed to SidebarTriggerProvider so consumers (PageHeader's
    // inline burger) don't re-render on every MainLayout state change.
    const openMobileSidebar = useCallback(() => setIsMobileSidebarOpen(true), []);

    // Desktop collapsed sidebar state (persisted to localStorage)
    const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(
      () => localStorage.getItem(SIDEBAR_COLLAPSED_KEY) === 'true',
    );
    const handleSidebarCollapsed = useCallback((collapsed) => {
      setIsSidebarCollapsed(collapsed);
      localStorage.setItem(SIDEBAR_COLLAPSED_KEY, String(collapsed));
    }, []);

    /* Company context: the app fetches + caches this per user; the port pins
       the fixture. Auth / 2FA / impersonation / onboarding-bounce effects and
       their loading screens are skipped (CONVENTIONS rule 8). */
    const [company, setCompany] = useState(FIXTURE_TENANT);
    const selectedCompany = FIXTURE_TENANT;
    const selectedUser = FIXTURE_USER;
    const isImpersonating = false;

    // Auto-close mobile sidebar when route changes
    useEffect(() => {
      setIsMobileSidebarOpen(false);
    }, [location.pathname]);

    const refreshCompany = async () => {};
    const patchSelectedCompany = (patch) => {
      setCompany((prev) => (prev ? { ...prev, ...patch } : prev));
    };

    return (
      <CompanyContext.Provider value={{ company, setCompany, refreshCompany }}>
        <AdminContext.Provider
          value={{
            selectedCompany,
            selectedUser,
            isImpersonating,
            patchSelectedCompany,
          }}
        >
          {/* Audience + list-focus state (TRA-1298) is hoisted to the shell so
              it survives cross-page nav. The key forces a remount on
              tenant/user switch (never happens with the fixture). */}
          <AudienceProvider
            key={`${selectedCompany.id}:${selectedUser?.id ?? 'tenant'}`}
            tenantId={selectedCompany.id}
            userId={selectedUser?.id ?? 'tenant'}
          >
            {/* In the app, RunMonitorProvider takes renderSessionExtras wiring
                ImportJobCard / WorkflowJobSteps into the popup; with no job
                sessions in the prototype the whole surface is inert. */}
            <RunMonitorProvider>
              {/* TRA-1509: `h-svh` from `md` up, but only a `min-h-svh` FLOOR
                  below it — a phone needs the root document to scroll. */}
              <div className="flex min-h-svh md:h-svh app-shell-bg">
                {/* AppSidebar owns its own desktop/mobile responsive layout -
                    desktop renders inline, mobile renders as an off-canvas
                    drawer driven by `open` / `onOpenChange`. */}
                <Sidebar
                  open={isMobileSidebarOpen}
                  onOpenChange={setIsMobileSidebarOpen}
                  collapsed={isSidebarCollapsed}
                  onCollapsedChange={handleSidebarCollapsed}
                />
                {/* `relative` + the id are the prototype's screen-anchored
                    drawer target (DrawerShell `screen`). This box already has
                    exactly the geometry that mode wants — it starts at the
                    sidebar's right edge and runs to the viewport's right edge,
                    full height — so a drawer portaled in here lands flush with
                    the screen edge and its scrim stops at the nav, with no
                    fixed positioning and no need to track the sidebar's
                    collapsed width. */}
                <div
                  id={CONTENT_COLUMN_ID}
                  className="relative flex-1 flex flex-col min-w-0 min-h-0"
                >
                  <TrialCountdownBanner />
                  {/* `overflow-y-auto` is `md:`-scoped (TRA-1509): below md
                      this must NOT be a scroll container. */}
                  <main className="flex-1 md:overflow-y-auto p-4 md:p-6 md:pb-3.5 max-[480px]:p-2 min-h-0">
                    {/* Mobile hamburger is provided via context and rendered
                        INLINE by PageHeader (or MobileSidebarTrigger on pages
                        without one). */}
                    <SidebarTriggerProvider value={openMobileSidebar}>
                      <ExpiryPaywall>{children}</ExpiryPaywall>
                    </SidebarTriggerProvider>
                  </main>
                  {/* Dev bar is scoped to the content column so it never pushes
                      the sidebar's account block off the viewport bottom. */}
                  <div className="hidden lg:block flex-shrink-0">
                    <DeveloperBar apiUrl="" fixed={false} />
                  </div>
                </div>
              </div>
              <RunMonitor />
              <WorkflowJobsFeeder
                kinds={[
                  'account-import',
                  'people-import',
                  'hubspot-import',
                  'hubspot-export-accounts',
                  'hubspot-export-signal',
                ]}
                silentKinds={['people-import', 'account-import']}
              />
            </RunMonitorProvider>
          </AudienceProvider>
        </AdminContext.Provider>
      </CompanyContext.Provider>
    );
  }

  Object.assign(T, { MainLayout, Sidebar, CONTENT_COLUMN_ID });
})();
