import { afterEach, describe, expect, test, vi } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/svelte';

vi.mock('$app/state', () => ({
  page: { url: new URL('http://localhost/') }
}));

vi.mock('$app/navigation', () => ({
  goto: vi.fn()
}));

// Mutable handle so individual tests can flip the user between admin /
// non-admin without re-importing the module.
const userState = vi.hoisted(() => ({
  current: { id: '1', username: 'alice', is_admin: false } as {
    id: string;
    username: string;
    is_admin: boolean;
  } | null
}));

vi.mock('$lib/auth/store.svelte', () => ({
  user: { get value() { return userState.current; } },
  logout: vi.fn().mockResolvedValue(undefined)
}));

import Shell from './Shell.svelte';
import { logout } from '$lib/auth/store.svelte';
import { goto } from '$app/navigation';

afterEach(() => {
  vi.clearAllMocks();
  userState.current = { id: '1', username: 'alice', is_admin: false };
});

describe('Shell', () => {
  test('renders the username in the header', () => {
    render(Shell);
    expect(screen.getByText('alice')).toBeInTheDocument();
  });

  test('main nav renders the three primary surfaces in order', () => {
    render(Shell);
    // Library is a parent for Artists / Albums / Liked / History /
    // Playlists (tabs under /library); only the parent appears here.
    const labels = ['Home', 'Library', 'Discover'];
    for (const label of labels) {
      expect(screen.getByRole('link', { name: label })).toBeInTheDocument();
    }
    expect(screen.getByRole('link', { name: 'Home' })).toHaveAttribute('href', '/');
    // Library jumps to the Artists tab directly so SPA navigation
    // bypasses the /library 308-redirect.
    expect(screen.getByRole('link', { name: 'Library' })).toHaveAttribute('href', '/library/artists');
    expect(screen.getByRole('link', { name: 'Discover' })).toHaveAttribute('href', '/discover');
  });

  test('main nav omits Artists/Albums/Liked/History/Playlists/Search/Settings/Admin', () => {
    userState.current = { id: '1', username: 'alice', is_admin: true };
    render(Shell);
    // Library sub-pages reach their content via the /library tab bar
    // (in routes/library/+layout.svelte), not via the global Shell nav.
    // Search reaches /search via the global SearchInput on the right.
    // Settings + Admin live in the username dropdown — they're per-
    // operator chrome, not primary surfaces.
    const navLinks = screen
      .getAllByRole('link')
      .filter((el) => el.closest('nav[aria-label="Primary"]') !== null)
      .map((el) => el.textContent?.trim());
    for (const label of [
      'Artists', 'Albums', 'Liked', 'History', 'Playlists',
      'Search', 'Requests', 'Hidden', 'Settings', 'Admin'
    ]) {
      expect(navLinks).not.toContain(label);
    }
  });

  test('user-menu shows Settings + Log out for non-admin users', async () => {
    userState.current = { id: '1', username: 'alice', is_admin: false };
    render(Shell);
    await fireEvent.click(screen.getByRole('button', { name: /alice/i }));
    expect(screen.getByRole('menuitem', { name: 'Settings' })).toHaveAttribute('href', '/settings');
    expect(screen.getByRole('menuitem', { name: /log out/i })).toBeInTheDocument();
    expect(screen.queryByRole('menuitem', { name: 'Admin' })).not.toBeInTheDocument();
  });

  test('user-menu adds Admin between Settings and Log out for admin users', async () => {
    userState.current = { id: '1', username: 'alice', is_admin: true };
    render(Shell);
    await fireEvent.click(screen.getByRole('button', { name: /alice/i }));
    const items = screen.getAllByRole('menuitem').map((el) => el.textContent?.trim());
    expect(items).toEqual(['Settings', 'Admin', 'Log out']);
    expect(screen.getByRole('menuitem', { name: 'Admin' })).toHaveAttribute('href', '/admin');
  });

  test('user-menu "Log out" calls logout() and navigates to /login', async () => {
    render(Shell);
    await fireEvent.click(screen.getByRole('button', { name: /alice/i }));
    await fireEvent.click(screen.getByRole('menuitem', { name: /log out/i }));
    expect(logout).toHaveBeenCalledTimes(1);
    expect(goto).toHaveBeenCalledWith('/login', { replaceState: true });
  });
});
