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

import { api } from '@spoon/backend/convex/_generated/api.js';

import { AvatarDropdown } from '../../src/components/layout/header/controls/AvatarDropdown';

// Stable sentinels: the real Convex `api` proxy hands back a fresh reference on
// every access, so `===` comparisons in the mocks below would never match.
vi.mock('@spoon/backend/convex/_generated/api.js', () => ({
  api: {
    auth: {
      getUser: 'auth:getUser',
    },
    files: {
      getImageUrl: 'files:getImageUrl',
    },
  },
}));

const { mockUseConvexAuth, mockUseQuery } = vi.hoisted(() => ({
  mockUseConvexAuth: vi.fn(),
  mockUseQuery: vi.fn(),
}));

vi.mock('convex/react', () => ({
  useConvexAuth: mockUseConvexAuth,
  useQuery: mockUseQuery,
}));

vi.mock('@convex-dev/auth/react', () => ({
  useAuthActions: () => ({ signOut: vi.fn() }),
}));

vi.mock('next/navigation', () => ({
  useRouter: () => ({ push: vi.fn(), replace: vi.fn() }),
}));

vi.mock('@/lib/avatar', () => ({
  isRemoteImageUrl: () => false,
}));

// Render the dropdown primitives as pass-through wrappers so the menu content
// is deterministically present under jsdom (Radix pointer behavior is flaky
// here). We are exercising AvatarDropdown's logic, not Radix.
vi.mock('@spoon/ui', () => ({
  BasedAvatar: () => <div>avatar</div>,
  Button: (props: ComponentProps<'button'>) => <button {...props} />,
  DropdownMenu: ({ children }: { children?: ReactNode }) => <div>{children}</div>,
  DropdownMenuTrigger: ({ children }: { children?: ReactNode }) => (
    <>{children}</>
  ),
  DropdownMenuContent: ({ children }: { children?: ReactNode }) => (
    <div>{children}</div>
  ),
  DropdownMenuItem: ({ children }: { children?: ReactNode }) => <>{children}</>,
  DropdownMenuLabel: ({ children }: { children?: ReactNode }) => (
    <div>{children}</div>
  ),
  DropdownMenuSeparator: () => <hr />,
}));

const seedUser = (user: { name: string; email: string }) => {
  mockUseConvexAuth.mockReturnValue({
    isAuthenticated: true,
    isLoading: false,
  });
  mockUseQuery.mockImplementation((reference: unknown) => {
    if (reference === api.auth.getUser) return user;
    return undefined;
  });
};

afterEach(() => {
  cleanup();
  vi.clearAllMocks();
});

describe('AvatarDropdown', () => {
  it('falls back to the email when the name is an empty string', () => {
    seedUser({ name: '', email: 'me@x.com' });
    render(<AvatarDropdown />);
    expect(screen.getByText('me@x.com')).toBeInTheDocument();
  });

  it('shows the trimmed name when it is present', () => {
    seedUser({ name: 'Gib', email: 'me@x.com' });
    render(<AvatarDropdown />);
    expect(screen.getByText('Gib')).toBeInTheDocument();
    expect(screen.queryByText('me@x.com')).not.toBeInTheDocument();
  });

  it('links Edit Profile directly to the settings profile page', () => {
    seedUser({ name: 'Gib', email: 'me@x.com' });
    render(<AvatarDropdown />);
    const editLink = screen.getByRole('link', { name: /edit profile/i });
    expect(editLink).toHaveAttribute('href', '/settings/profile');
  });
});
