import { cleanup, fireEvent, render, screen } from '@testing-library/react';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { PrimaryActionProvider, usePrimaryAction, usePrimaryActionHandler } from './primaryAction';

function Registration({ routeId, handler }: { routeId: string; handler: () => void }) {
  usePrimaryAction(routeId, handler);
  return null;
}

function Action({ routeId }: { routeId: string }) {
  const handler = usePrimaryActionHandler(routeId);
  return (
    <button type="button" disabled={!handler} onClick={handler}>
      Run {routeId}
    </button>
  );
}

describe('primary actions', () => {
  afterEach(() => {
    cleanup();
  });

  it('invokes the active handler for a matching route', () => {
    const handler = vi.fn();
    render(
      <PrimaryActionProvider>
        <Registration routeId="schedules" handler={handler} />
        <Action routeId="schedules" />
      </PrimaryActionProvider>
    );

    fireEvent.click(screen.getByRole('button', { name: 'Run schedules' }));

    expect(handler).toHaveBeenCalledOnce();
  });

  it('returns no handler for a nonmatching route', () => {
    const handler = vi.fn();
    render(
      <PrimaryActionProvider>
        <Registration routeId="schedules" handler={handler} />
        <Action routeId="channels" />
      </PrimaryActionProvider>
    );

    const action = screen.getByRole('button', { name: 'Run channels' });
    expect(action).toBeDisabled();
    fireEvent.click(action);
    expect(handler).not.toHaveBeenCalled();
  });

  it('invokes the latest handler without replacing the registration', () => {
    const firstHandler = vi.fn();
    const latestHandler = vi.fn();
    const view = render(
      <PrimaryActionProvider>
        <Registration routeId="schedules" handler={firstHandler} />
        <Action routeId="schedules" />
      </PrimaryActionProvider>
    );

    view.rerender(
      <PrimaryActionProvider>
        <Registration routeId="schedules" handler={latestHandler} />
        <Action routeId="schedules" />
      </PrimaryActionProvider>
    );
    fireEvent.click(screen.getByRole('button', { name: 'Run schedules' }));

    expect(firstHandler).not.toHaveBeenCalled();
    expect(latestHandler).toHaveBeenCalledOnce();
  });

  it("moves the same owner's registration to a new route and clears it on unmount", () => {
    const handler = vi.fn();
    const view = render(
      <PrimaryActionProvider>
        <Registration routeId="schedules" handler={handler} />
        <Action routeId="schedules" />
        <Action routeId="channels" />
      </PrimaryActionProvider>
    );

    view.rerender(
      <PrimaryActionProvider>
        <Registration routeId="channels" handler={handler} />
        <Action routeId="schedules" />
        <Action routeId="channels" />
      </PrimaryActionProvider>
    );

    expect(screen.getByRole('button', { name: 'Run schedules' })).toBeDisabled();
    expect(screen.getByRole('button', { name: 'Run channels' })).toBeEnabled();
    fireEvent.click(screen.getByRole('button', { name: 'Run channels' }));
    expect(handler).toHaveBeenCalledOnce();

    view.rerender(
      <PrimaryActionProvider>
        <Action routeId="schedules" />
        <Action routeId="channels" />
      </PrimaryActionProvider>
    );

    expect(screen.getByRole('button', { name: 'Run channels' })).toBeDisabled();
  });

  it('does not let an older owner rerender or cleanup reclaim a newer registration', () => {
    const olderHandler = vi.fn();
    const updatedOlderHandler = vi.fn();
    const newerHandler = vi.fn();
    const view = render(
      <PrimaryActionProvider>
        <Registration key="older" routeId="schedules" handler={olderHandler} />
        <Action routeId="schedules" />
      </PrimaryActionProvider>
    );

    view.rerender(
      <PrimaryActionProvider>
        <Registration key="older" routeId="schedules" handler={olderHandler} />
        <Registration key="newer" routeId="schedules" handler={newerHandler} />
        <Action routeId="schedules" />
      </PrimaryActionProvider>
    );
    view.rerender(
      <PrimaryActionProvider>
        <Registration key="older" routeId="schedules" handler={updatedOlderHandler} />
        <Registration key="newer" routeId="schedules" handler={newerHandler} />
        <Action routeId="schedules" />
      </PrimaryActionProvider>
    );
    fireEvent.click(screen.getByRole('button', { name: 'Run schedules' }));
    expect(newerHandler).toHaveBeenCalledOnce();

    view.rerender(
      <PrimaryActionProvider>
        <Registration key="newer" routeId="schedules" handler={newerHandler} />
        <Action routeId="schedules" />
      </PrimaryActionProvider>
    );
    fireEvent.click(screen.getByRole('button', { name: 'Run schedules' }));

    expect(olderHandler).not.toHaveBeenCalled();
    expect(updatedOlderHandler).not.toHaveBeenCalled();
    expect(newerHandler).toHaveBeenCalledTimes(2);
  });

  it('lets screens render harmlessly without a provider', () => {
    expect(() => render(<Registration routeId="schedules" handler={vi.fn()} />)).not.toThrow();
  });
});
