// @vitest-environment jsdom
import { describe, it, expect, vi, afterEach } from 'vitest';
import { render, cleanup, screen } from '@testing-library/react';

// Mock next/router so useRouter() does not crash without a RouterContext.
// Layout reads `router.pathname` only; the rest of the surface (`prefetch`,
// `events`, `push`) is for next/link's internals — provide stubs so prefetch
// does not throw when <Link> mounts.
vi.mock('next/router', () => ({
  useRouter: () => ({
    pathname: '/',
    asPath: '/',
    query: {},
    push: vi.fn(),
    replace: vi.fn(),
    prefetch: vi.fn().mockResolvedValue(undefined),
    events: { on: vi.fn(), off: vi.fn(), emit: vi.fn() },
  }),
}));

// Mock next/link to a plain <a>. The real next/link triggers prefetch on
// mount via the router; bypassing it removes a class of jsdom flake without
// changing the rendered DOM that the assertions inspect.
vi.mock('next/link', () => ({
  __esModule: true,
  default: ({ href, children, ...rest }) => {
    return (
      <a href={typeof href === 'string' ? href : ''} {...rest}>
        {children}
      </a>
    );
  },
}));

// Mock the theme context so useTheme() does not require a ThemeProvider.
vi.mock('../../lib/theme-context', () => ({
  useTheme: () => ({ theme: 'light', toggleTheme: vi.fn() }),
}));

import Layout from '../../components/Layout';

describe('Layout — logged-out rendering (regression: P0 #7)', () => {
  afterEach(() => cleanup());

  it('does NOT render the maintainer email when no user prop is passed', () => {
    const { container } = render(<Layout>page body</Layout>);
    expect(container.textContent).not.toContain('me@randallstillwell.com');
  });

  it('does NOT render the maintainer email when user is null', () => {
    const { container } = render(<Layout user={null}>page body</Layout>);
    expect(container.textContent).not.toContain('me@randallstillwell.com');
  });

  it('renders a Sign-in link to /login when user is null', () => {
    render(<Layout user={null}>page body</Layout>);
    const links = screen.getAllByRole('link', { name: /sign in/i });
    expect(links.length).toBeGreaterThanOrEqual(1);
    // Both desktop sidebar + mobile drawer render UserProfileDropdown,
    // so we expect TWO Sign-in links (one per copy).
    for (const link of links) {
      expect(link.getAttribute('href')).toBe('/login');
    }
  });

  it('flows the supplied user through to the rendered surface (TopSearchBar chip)', () => {
    // Pre-refinements (2026-06-04), the UserProfileDropdown in the
    // sidebar displayed the full email. The redesign-v2 refinements
    // moved the user-menu to the TopSearchBar's compact chip in the
    // top-right, which displays the username (or the email's local
    // part as a fallback) rather than the full address. The
    // maintainer-email regression-lock from P0 #7 still passes via
    // the three "does NOT render me@randallstillwell.com" cases
    // above; this case continues to assert that the user prop FLOWS
    // through, just against the new render surface.
    const { container } = render(
      <Layout user={{ email: 'foo@bar.com', role: 'user' }}>page body</Layout>
    );
    expect(container.textContent).toContain('foo');
    expect(container.textContent).not.toContain('me@randallstillwell.com');
  });

  it('does NOT render a "Guest" placeholder when logged out', () => {
    // Decision A says the logged-out copy is "Sign in", not "Guest".
    // This test prevents a future revert that ships "Guest" as the default
    // (which would still hide the maintainer email but skip the CTA).
    const { container } = render(<Layout user={null}>page body</Layout>);
    expect(container.textContent).not.toContain('Guest');
  });
});

describe('Layout — floating popovers use .glass-panel-strong (regression: Brief 3 of unify-glass-panel-surfaces)', () => {
  afterEach(() => cleanup());

  // The sidebar UserProfileDropdown and the mobile drawer are floating
  // glass surfaces. Brief 3 migrated them from inline
  // `background: var(--glass-surface-*)` + `backdropFilter` to the
  // canonical `.glass-panel-strong` className. If a future PR reverts
  // either to the inline pattern, the bespoke-glass-surface CI gate
  // (Brief 7) will catch the regression — these unit tests catch the
  // SAME regression at the component-render level, before the gate
  // would fire.

  it('mobile drawer panel carries .glass-panel-strong when isMobileMenuOpen', () => {
    const { container } = render(
      <Layout user={{ email: 'foo@bar.com', role: 'user' }}>page body</Layout>
    );
    // The drawer is rendered (translated off-screen via `-translate-x-full`)
    // even when closed, so we can assert on it from a default mount.
    const drawer = container.querySelector(
      '.glass-panel-strong.md\\:hidden.fixed.inset-y-0.left-0'
    );
    expect(drawer).not.toBeNull();
    expect(drawer.className).toContain('glass-panel-strong');
    expect(drawer.className).toContain('w-64');
  });

  it('mobile drawer panel does NOT carry an inline `background: var(--glass-surface-*)` style after Brief 3', () => {
    // Regression lock against reverting Brief 3 to handrolled inline
    // glass styles on the mobile drawer specifically. The drawer is
    // always in the DOM (translated off-screen when closed), so we can
    // query it from a default mount.
    const { container } = render(
      <Layout user={{ email: 'foo@bar.com', role: 'user' }}>page body</Layout>
    );
    const drawer = container.querySelector(
      '.glass-panel-strong.md\\:hidden.fixed.inset-y-0.left-0'
    );
    expect(drawer).not.toBeNull();
    const inlineStyle = drawer.getAttribute('style') ?? '';
    expect(inlineStyle).not.toMatch(/var\(--glass-surface-/);
    expect(inlineStyle).not.toMatch(/backdrop-filter/i);
    expect(inlineStyle).toContain('box-shadow');
    expect(inlineStyle).toContain('--elevation-pronounced');
  });
});
