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

import MobileDrawer from './MobileDrawer';

vi.mock('react-i18next', () => ({
  useTranslation: () => ({
    t: (key: string) => key,
  }),
}));

const originalSetPointerCapture = HTMLElement.prototype.setPointerCapture;

function renderDrawer(onClose = vi.fn()) {
  const view = render(
    <MobileDrawer
      onClose={onClose}
      renderArea={() => <div>Area content</div>}
      renderProperties={() => <div>Properties content</div>}
      tab="area"
      onTabChange={vi.fn()}
    />
  );
  const handle = view.container.querySelector('[data-mobile-drawer-drag-handle]');
  const root = view.container.querySelector('[data-tutorial="right-pane"]');
  const panel = view.container.querySelector('[data-tutorial="right-pane"] > div:last-child');

  if (!(handle instanceof HTMLElement)) throw new Error('Expected drawer drag handle');
  if (!(root instanceof HTMLElement)) throw new Error('Expected drawer root');
  if (!(panel instanceof HTMLElement)) throw new Error('Expected drawer panel');

  return { ...view, handle, onClose, panel, root };
}

describe('MobileDrawer', () => {
  beforeEach(() => {
    HTMLElement.prototype.setPointerCapture = vi.fn();
  });

  afterEach(() => {
    cleanup();
    Object.defineProperty(HTMLElement.prototype, 'setPointerCapture', {
      configurable: true,
      value: originalSetPointerCapture,
    });
  });

  it('lowers and stays open when swiped down from the handle', () => {
    const { handle, onClose, panel } = renderDrawer();

    fireEvent.pointerDown(handle, { pointerId: 1, clientY: 120 });
    fireEvent.pointerMove(handle, { pointerId: 1, clientY: 230 });
    fireEvent.pointerUp(handle, { pointerId: 1, clientY: 230 });

    expect(onClose).not.toHaveBeenCalled();
    expect(panel.style.transform).toBe('translateY(110px)');
  });

  it('can be raised again after being lowered', () => {
    const { handle, onClose, panel } = renderDrawer();

    fireEvent.pointerDown(handle, { pointerId: 1, clientY: 120 });
    fireEvent.pointerMove(handle, { pointerId: 1, clientY: 230 });
    fireEvent.pointerUp(handle, { pointerId: 1, clientY: 230 });

    fireEvent.pointerDown(handle, { pointerId: 2, clientY: 230 });
    fireEvent.pointerMove(handle, { pointerId: 2, clientY: 170 });
    fireEvent.pointerUp(handle, { pointerId: 2, clientY: 170 });

    expect(onClose).not.toHaveBeenCalled();
    expect(panel.style.transform).toBe('translateY(50px)');
  });

  it('keeps the close control reachable when dragged down far', () => {
    const { handle, panel } = renderDrawer();

    Object.defineProperty(panel, 'offsetHeight', {
      configurable: true,
      value: 200,
    });

    fireEvent.pointerDown(handle, { pointerId: 1, clientY: 120 });
    fireEvent.pointerMove(handle, { pointerId: 1, clientY: 420 });
    fireEvent.pointerUp(handle, { pointerId: 1, clientY: 420 });

    expect(panel.style.transform).toBe('translateY(96px)');
  });

  it('leaves the rest of the mobile map usable while the panel is open', () => {
    const { panel, root } = renderDrawer();
    const spacer = root.firstElementChild;

    if (!(spacer instanceof HTMLElement)) throw new Error('Expected drawer spacer');

    expect(root.className).toContain('pointer-events-none');
    expect(panel.className).toContain('pointer-events-auto');
    expect(spacer.className).not.toContain('bg-black');
  });

  it('closes from the close button', () => {
    const { onClose } = renderDrawer();

    fireEvent.click(screen.getByLabelText('mobileDrawer.closeDrawer'));

    expect(onClose).toHaveBeenCalledTimes(1);
  });
});
