import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { afterEach, describe, expect, it, vi } from 'vitest';
import App from './App';
import { PREFERENCES_KEY } from './preferences';
import { catalogue, catalogueFetch } from './test/fixtures';

afterEach(() => vi.unstubAllGlobals());

describe('portal UI', () => {
  it('renders app cards and persists accessible pin/hide controls', async () => {
    vi.stubGlobal('fetch', vi.fn(catalogueFetch()));
    const user = userEvent.setup();
    render(<App />);
    expect(
      await screen.findByRole('heading', { name: 'PDF Workbench' })
    ).toBeInTheDocument();
    expect(
      screen.getByText(
        'Local processing · no file uploads declared · no telemetry declared'
      )
    ).toBeInTheDocument();
    const sourceLinks = screen.getAllByRole('link', {
      name: 'Source and license',
    });
    expect(sourceLinks).toHaveLength(2);
    expect(
      sourceLinks.find(
        (link) =>
          link.getAttribute('href') ===
          'https://git.add-ideas.de/zemion/pdf-tools'
      )
    ).toHaveAttribute('rel', 'noopener noreferrer');
    expect(
      sourceLinks.find((link) =>
        link.getAttribute('href')?.endsWith('/toolbox-portal/src/tag/v0.1.0')
      )
    ).toBeDefined();
    const launch = screen.getByRole('link', { name: /open tool/i });
    expect(
      new URL(launch.getAttribute('href')!).searchParams.get('toolbox')
    ).toContain('toolbox.catalog.json');

    await user.click(screen.getByRole('button', { name: 'Pin PDF Workbench' }));
    await waitFor(() =>
      expect(localStorage.getItem(PREFERENCES_KEY)).toContain(
        'de.add-ideas.pdf-tools'
      )
    );
    expect(screen.getByText('Pinned')).toBeInTheDocument();

    await user.click(
      screen.getByRole('button', { name: 'Hide PDF Workbench' })
    );
    expect(
      screen.queryByRole('heading', { name: 'PDF Workbench' })
    ).not.toBeInTheDocument();
    fireEvent.click(screen.getByRole('checkbox', { name: /show hidden/i }));
    expect(
      screen.getByRole('heading', { name: 'PDF Workbench' })
    ).toBeInTheDocument();
  });

  it('shows a recoverable catalogue error', async () => {
    const fetchMock = vi
      .fn()
      .mockResolvedValueOnce(new Response('no', { status: 503 }))
      .mockImplementation(catalogueFetch());
    vi.stubGlobal('fetch', fetchMock);
    render(<App />);
    expect(
      await screen.findByRole('heading', { name: 'Toolbox unavailable' })
    ).toBeInTheDocument();
    fireEvent.click(screen.getByRole('button', { name: 'Try again' }));
    expect(
      await screen.findByRole('heading', { name: 'PDF Workbench' })
    ).toBeInTheDocument();
  });

  it('shows a dedicated empty state for a valid catalogue', async () => {
    vi.stubGlobal(
      'fetch',
      vi.fn(
        catalogueFetch({
          '/toolbox.catalog.json': Response.json({ ...catalogue, apps: [] }),
        })
      )
    );
    render(<App />);
    expect(
      await screen.findByRole('heading', { name: 'No tools yet' })
    ).toBeInTheDocument();
  });

  it('contains preference focus and restores it to Personalize', async () => {
    vi.stubGlobal('fetch', vi.fn(catalogueFetch()));
    const user = userEvent.setup();
    const { container } = render(<App />);
    const personalize = screen.getByRole('button', { name: 'Personalize' });

    await user.click(personalize);
    const dialog = screen.getByRole('dialog', {
      name: 'Personalize your toolbox',
    });
    const close = screen.getByRole('button', { name: 'Close preferences' });
    expect(close).toHaveFocus();
    expect(container.querySelector('.site-shell')).toHaveAttribute('inert');
    expect(dialog).toBeInTheDocument();
    expect(screen.getByText(/does not transmit them/i)).toHaveTextContent(
      /Trusted code in another Toolbox app on this site can access same-origin storage/i
    );

    const reset = screen.getByRole('button', { name: 'Reset' });
    reset.focus();
    await user.tab();
    expect(close).toHaveFocus();

    await user.keyboard('{Escape}');
    expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
    expect(personalize).toHaveFocus();
  });
});
