import { fireEvent, render } from '@testing-library/vue';
import { describe, expect, test, beforeEach, afterEach, vi } from 'vitest';
import UiSidebar from '../UiSidebar.vue';
import UiSidebarProvider from '../UiSidebarProvider.vue';
import {
  createSidebarShell,
  mockMatchMediaDesktop,
} from './sidebar-test-helpers';

beforeEach(() => {
  mockMatchMediaDesktop();
  localStorage.clear();
});
afterEach(() => vi.restoreAllMocks());

function getResizeHandle(container: HTMLElement): HTMLElement {
  const resizeHandle = container.querySelector<HTMLElement>(
    '[data-slot="sidebar-resize-handle"]',
  );

  if (!resizeHandle) {
    throw new Error('Expected sidebar resize handle to be rendered');
  }

  return resizeHandle;
}

describe('UiSidebar', () => {
  test('renders core sidebar region (collapsible none uses data-slot)', () => {
    const { container } = render(createSidebarShell('<span>Main</span>'));
    expect(container.querySelector('[data-slot="sidebar"]')).toBeTruthy();
    expect(container).toHaveTextContent('Main');
  });

  test('forwards variant and side to desktop shell when collapsible is offcanvas', () => {
    const { container } = render({
      components: { UiSidebarProvider, UiSidebar },
      template: `
        <UiSidebarProvider :default-open="true">
          <UiSidebar collapsible="offcanvas" variant="inset" side="right">X</UiSidebar>
        </UiSidebarProvider>
      `,
    });
    const region = container.querySelector(
      '[data-slot="sidebar"][data-variant="inset"]',
    );
    expect(region).toBeTruthy();
    expect(region).toHaveAttribute('data-side', 'right');
  });

  test('uses persisted resizable width from local storage', () => {
    localStorage.setItem('test-sidebar-width', '360');

    const { container } = render({
      components: { UiSidebarProvider, UiSidebar },
      template: `
        <UiSidebarProvider :default-open="true">
          <UiSidebar
            resizable
            width-storage-key="test-sidebar-width"
            collapsible="offcanvas"
          >
            X
          </UiSidebar>
        </UiSidebarProvider>
      `,
    });

    expect(
      container
        .querySelector<HTMLElement>('[data-slot="sidebar"]')
        ?.style.getPropertyValue('--sidebar-width'),
    ).toBe('360px');
  });

  test('uses configurable resize handle aria label', () => {
    const { container } = render({
      components: { UiSidebarProvider, UiSidebar },
      template: `
        <UiSidebarProvider :default-open="true">
          <UiSidebar
            resizable
            collapsible="offcanvas"
            resize-aria-label="Resize navigation"
          >
            X
          </UiSidebar>
        </UiSidebarProvider>
      `,
    });

    expect(
      container.querySelector('[data-slot="sidebar-resize-handle"]'),
    ).toHaveAttribute('aria-label', 'Resize navigation');
  });

  test('uses configurable keyboard resize step', async () => {
    const { container } = render({
      components: { UiSidebarProvider, UiSidebar },
      template: `
        <UiSidebarProvider :default-open="true">
          <UiSidebar
            resizable
            collapsible="offcanvas"
            :default-width="280"
            :keyboard-resize-by="24"
          >
            X
          </UiSidebar>
        </UiSidebarProvider>
      `,
    });

    const sidebar = container.querySelector<HTMLElement>(
      '[data-slot="sidebar"]',
    );
    const resizeHandle = getResizeHandle(container);

    await fireEvent.keyDown(resizeHandle, { key: 'ArrowRight' });

    expect(sidebar?.style.getPropertyValue('--sidebar-width')).toBe('304px');
  });

  test('supports resize options object API', async () => {
    const { container } = render({
      components: { UiSidebarProvider, UiSidebar },
      template: `
        <UiSidebarProvider :default-open="true">
          <UiSidebar
            :resize-options="{
              defaultWidth: 280,
              keyboardResizeBy: 24,
              resizeAriaLabel: 'Resize with options'
            }"
            collapsible="offcanvas"
          >
            X
          </UiSidebar>
        </UiSidebarProvider>
      `,
    });

    const sidebar = container.querySelector<HTMLElement>(
      '[data-slot="sidebar"]',
    );
    const resizeHandle = getResizeHandle(container);

    expect(resizeHandle).toHaveAttribute('aria-label', 'Resize with options');

    await fireEvent.keyDown(resizeHandle, { key: 'ArrowRight' });
    expect(sidebar?.style.getPropertyValue('--sidebar-width')).toBe('304px');
  });

  test('treats resize options presence as resizable', () => {
    const { container } = render({
      components: { UiSidebarProvider, UiSidebar },
      template: `
        <UiSidebarProvider :default-open="true">
          <UiSidebar
            :resize-options="{}"
            collapsible="offcanvas"
          >
            X
          </UiSidebar>
        </UiSidebarProvider>
      `,
    });

    expect(
      container.querySelector('[data-slot="sidebar-resize-handle"]'),
    ).toBeTruthy();
  });

  test('prefers resize options over legacy resize props', async () => {
    const { container } = render({
      components: { UiSidebarProvider, UiSidebar },
      template: `
        <UiSidebarProvider :default-open="true">
          <UiSidebar
            resizable
            :default-width="280"
            :keyboard-resize-by="12"
            :resize-options="{ keyboardResizeBy: 30 }"
            collapsible="offcanvas"
          >
            X
          </UiSidebar>
        </UiSidebarProvider>
      `,
    });

    const sidebar = container.querySelector<HTMLElement>(
      '[data-slot="sidebar"]',
    );
    const resizeHandle = getResizeHandle(container);

    await fireEvent.keyDown(resizeHandle, { key: 'ArrowRight' });
    expect(sidebar?.style.getPropertyValue('--sidebar-width')).toBe('310px');
  });

  test('resizes within the configured width boundary and persists the value', async () => {
    const { container } = render({
      components: { UiSidebarProvider, UiSidebar },
      template: `
        <UiSidebarProvider :default-open="true">
          <UiSidebar
            resizable
            width-storage-key="test-sidebar-width"
            collapsible="offcanvas"
            :default-width="280"
            :min-width="240"
            :max-width="480"
          >
            X
          </UiSidebar>
        </UiSidebarProvider>
      `,
    });

    const sidebar = container.querySelector<HTMLElement>(
      '[data-slot="sidebar"]',
    );
    const resizeHandle = getResizeHandle(container);

    expect(resizeHandle).toBeTruthy();

    await fireEvent.pointerDown(resizeHandle, { clientX: 0 });
    await fireEvent.pointerMove(window, { clientX: 500 });
    await fireEvent.pointerUp(window);

    expect(sidebar?.style.getPropertyValue('--sidebar-width')).toBe('480px');
    expect(localStorage.getItem('test-sidebar-width')).toBe('480');

    await fireEvent.pointerDown(resizeHandle, { clientX: 500 });
    await fireEvent.pointerMove(window, { clientX: -500 });
    await fireEvent.pointerUp(window);

    expect(sidebar?.style.getPropertyValue('--sidebar-width')).toBe('240px');
    expect(localStorage.getItem('test-sidebar-width')).toBe('240');
  });
});
