import { render, waitFor } from '@testing-library/vue';
import userEvent from '@testing-library/user-event';
import { describe, expect, test, vi } from 'vitest';
import { ref } from 'vue';
import {
  UiNavigationMenu,
  UiNavigationMenuList,
  UiNavigationMenuItem,
  UiNavigationMenuTrigger,
  UiNavigationMenuContent,
  UiNavigationMenuLink,
  UiNavigationMenuIndicator,
} from '../index';

// Helper to render a basic navigation menu
/**
 *
 * @param options
 * @param options.triggerText
 * @param options.links
 * @param options.modelValue
 * @param options.defaultValue
 * @param options.disableHoverTrigger
 * @param options.disableClickTrigger
 * @param options.itemValue
 */
function renderNavigationMenu(
  options: {
    triggerText?: string;
    links?: string[];
    modelValue?: string;
    defaultValue?: string;
    disableHoverTrigger?: boolean;
    disableClickTrigger?: boolean;
    itemValue?: string;
  } = {},
) {
  const {
    triggerText = 'Products',
    links = ['Link 1', 'Link 2'],
    modelValue,
    defaultValue,
    disableHoverTrigger = false,
    disableClickTrigger = false,
    itemValue = 'products',
  } = options;

  return render({
    components: {
      UiNavigationMenu,
      UiNavigationMenuList,
      UiNavigationMenuItem,
      UiNavigationMenuTrigger,
      UiNavigationMenuContent,
      UiNavigationMenuLink,
    },
    template: `
      <UiNavigationMenu
        ${modelValue !== undefined ? ':model-value="modelValue"' : ''}
        ${defaultValue !== undefined ? ':default-value="defaultValue"' : ''}
        :disable-hover-trigger="disableHoverTrigger"
        :disable-click-trigger="disableClickTrigger"
      >
        <UiNavigationMenuList>
          <UiNavigationMenuItem :value="itemValue">
            <UiNavigationMenuTrigger>
              {{ triggerText }}
            </UiNavigationMenuTrigger>
            <UiNavigationMenuContent>
              <UiNavigationMenuLink v-for="link in links" :key="link">
                {{ link }}
              </UiNavigationMenuLink>
            </UiNavigationMenuContent>
          </UiNavigationMenuItem>
        </UiNavigationMenuList>
      </UiNavigationMenu>
    `,
    setup() {
      return {
        triggerText,
        links,
        modelValue,
        defaultValue,
        disableHoverTrigger,
        disableClickTrigger,
        itemValue,
      };
    },
  });
}

describe('UiNavigationMenu', () => {
  test('supports controlled modelValue', async () => {
    const { getByText } = render({
      components: {
        UiNavigationMenu,
        UiNavigationMenuList,
        UiNavigationMenuItem,
        UiNavigationMenuTrigger,
        UiNavigationMenuContent,
        UiNavigationMenuLink,
      },
      template: `
        <UiNavigationMenu :model-value="modelValue">
          <UiNavigationMenuList>
            <UiNavigationMenuItem value="products">
              <UiNavigationMenuTrigger>Products</UiNavigationMenuTrigger>
              <UiNavigationMenuContent>
                <UiNavigationMenuLink>Product Link</UiNavigationMenuLink>
              </UiNavigationMenuContent>
            </UiNavigationMenuItem>
          </UiNavigationMenuList>
        </UiNavigationMenu>
      `,
      setup() {
        const modelValue = ref('products');
        return { modelValue };
      },
    });

    await waitFor(() => {
      expect(getByText('Product Link')).toBeVisible();
    });
  });

  test('suppresses viewport rendering when viewport is false', () => {
    const { container } = render({
      components: {
        UiNavigationMenu,
        UiNavigationMenuList,
        UiNavigationMenuItem,
        UiNavigationMenuTrigger,
        UiNavigationMenuContent,
      },
      template: `
        <UiNavigationMenu :viewport="false">
          <UiNavigationMenuList>
            <UiNavigationMenuItem>
              <UiNavigationMenuTrigger>Products</UiNavigationMenuTrigger>
              <UiNavigationMenuContent>Content</UiNavigationMenuContent>
            </UiNavigationMenuItem>
          </UiNavigationMenuList>
        </UiNavigationMenu>
      `,
    });

    expect(
      container.querySelector('[data-slot="navigation-menu-viewport"]'),
    ).not.toBeInTheDocument();
  });

  test('emits update:modelValue when item changes', async () => {
    const user = userEvent.setup();
    const onUpdateModelValue = vi.fn();

    const { getByRole } = render({
      components: {
        UiNavigationMenu,
        UiNavigationMenuList,
        UiNavigationMenuItem,
        UiNavigationMenuTrigger,
        UiNavigationMenuContent,
        UiNavigationMenuLink,
      },
      template: `
        <UiNavigationMenu @update:model-value="onUpdateModelValue" :disable-hover-trigger="true">
          <UiNavigationMenuList>
            <UiNavigationMenuItem value="products">
              <UiNavigationMenuTrigger>Products</UiNavigationMenuTrigger>
              <UiNavigationMenuContent>
                <UiNavigationMenuLink>Link</UiNavigationMenuLink>
              </UiNavigationMenuContent>
            </UiNavigationMenuItem>
          </UiNavigationMenuList>
        </UiNavigationMenu>
      `,
      setup() {
        return { onUpdateModelValue };
      },
    });

    await user.click(getByRole('button', { name: 'Products' }));

    await waitFor(() => {
      expect(onUpdateModelValue).toHaveBeenCalledWith('products');
    });
  });
});

describe('UiNavigationMenuTrigger', () => {
  test('forwards disableHoverTrigger — click opens when hover is disabled', async () => {
    const user = userEvent.setup();
    const { getByRole, getByText, queryByText } = renderNavigationMenu({
      disableHoverTrigger: true,
    });

    // Hover should not open the menu
    await user.hover(getByRole('button', { name: 'Products' }));
    expect(queryByText('Link 1')).not.toBeInTheDocument();

    // Click should open the menu
    await user.click(getByRole('button', { name: 'Products' }));

    await waitFor(() => {
      expect(getByText('Link 1')).toBeVisible();
    });
  });

  test('forwards disabled prop', () => {
    const { getByRole } = render({
      components: {
        UiNavigationMenu,
        UiNavigationMenuList,
        UiNavigationMenuItem,
        UiNavigationMenuTrigger,
        UiNavigationMenuContent,
      },
      template: `
        <UiNavigationMenu>
          <UiNavigationMenuList>
            <UiNavigationMenuItem>
              <UiNavigationMenuTrigger :disabled="true">Disabled</UiNavigationMenuTrigger>
              <UiNavigationMenuContent>Content</UiNavigationMenuContent>
            </UiNavigationMenuItem>
          </UiNavigationMenuList>
        </UiNavigationMenu>
      `,
    });

    expect(getByRole('button', { name: 'Disabled' })).toBeDisabled();
  });
});

describe('UiNavigationMenuContent', () => {
  test('content is present in DOM without interaction when forceMount is true', () => {
    const { getByText } = render({
      components: {
        UiNavigationMenu,
        UiNavigationMenuList,
        UiNavigationMenuItem,
        UiNavigationMenuTrigger,
        UiNavigationMenuContent,
        UiNavigationMenuLink,
      },
      template: `
        <UiNavigationMenu>
          <UiNavigationMenuList>
            <UiNavigationMenuItem value="products">
              <UiNavigationMenuTrigger>Products</UiNavigationMenuTrigger>
              <UiNavigationMenuContent :force-mount="true">
                <UiNavigationMenuLink>Force Mounted Link</UiNavigationMenuLink>
              </UiNavigationMenuContent>
            </UiNavigationMenuItem>
          </UiNavigationMenuList>
        </UiNavigationMenu>
      `,
    });

    expect(getByText('Force Mounted Link')).toBeInTheDocument();
  });

  test('forwards escapeKeyDown event', async () => {
    const user = userEvent.setup();
    const onEscapeKeyDown = vi.fn();

    const { getByRole, getByText } = render({
      components: {
        UiNavigationMenu,
        UiNavigationMenuList,
        UiNavigationMenuItem,
        UiNavigationMenuTrigger,
        UiNavigationMenuContent,
        UiNavigationMenuLink,
      },
      template: `
        <UiNavigationMenu>
          <UiNavigationMenuList>
            <UiNavigationMenuItem value="products">
              <UiNavigationMenuTrigger>Products</UiNavigationMenuTrigger>
              <UiNavigationMenuContent @escape-key-down="onEscapeKeyDown">
                <UiNavigationMenuLink>Link</UiNavigationMenuLink>
              </UiNavigationMenuContent>
            </UiNavigationMenuItem>
          </UiNavigationMenuList>
        </UiNavigationMenu>
      `,
      setup() {
        return { onEscapeKeyDown };
      },
    });

    await user.hover(getByRole('button', { name: 'Products' }));
    await waitFor(() => expect(getByText('Link')).toBeVisible());

    await user.keyboard('{Escape}');

    expect(onEscapeKeyDown).toHaveBeenCalled();
  });
});

describe('UiNavigationMenuLink', () => {
  test('forwards active prop as data-active attribute', async () => {
    const user = userEvent.setup();
    const { getByRole, getByText } = render({
      components: {
        UiNavigationMenu,
        UiNavigationMenuList,
        UiNavigationMenuItem,
        UiNavigationMenuTrigger,
        UiNavigationMenuContent,
        UiNavigationMenuLink,
      },
      template: `
        <UiNavigationMenu>
          <UiNavigationMenuList>
            <UiNavigationMenuItem value="products">
              <UiNavigationMenuTrigger>Products</UiNavigationMenuTrigger>
              <UiNavigationMenuContent>
                <UiNavigationMenuLink :active="true">Active Link</UiNavigationMenuLink>
                <UiNavigationMenuLink :active="false">Inactive Link</UiNavigationMenuLink>
              </UiNavigationMenuContent>
            </UiNavigationMenuItem>
          </UiNavigationMenuList>
        </UiNavigationMenu>
      `,
    });

    await user.hover(getByRole('button', { name: 'Products' }));

    await waitFor(() => {
      expect(getByText('Active Link')).toHaveAttribute('data-active');
      expect(getByText('Inactive Link')).not.toHaveAttribute('data-active');
    });
  });

  test('forwards select event', async () => {
    const user = userEvent.setup();
    const onSelect = vi.fn();

    const { getByRole, getByText } = render({
      components: {
        UiNavigationMenu,
        UiNavigationMenuList,
        UiNavigationMenuItem,
        UiNavigationMenuTrigger,
        UiNavigationMenuContent,
        UiNavigationMenuLink,
      },
      template: `
        <UiNavigationMenu>
          <UiNavigationMenuList>
            <UiNavigationMenuItem value="products">
              <UiNavigationMenuTrigger>Products</UiNavigationMenuTrigger>
              <UiNavigationMenuContent>
                <UiNavigationMenuLink @select="onSelect">Click Me</UiNavigationMenuLink>
              </UiNavigationMenuContent>
            </UiNavigationMenuItem>
          </UiNavigationMenuList>
        </UiNavigationMenu>
      `,
      setup() {
        return { onSelect };
      },
    });

    await user.hover(getByRole('button', { name: 'Products' }));
    await waitFor(() => expect(getByText('Click Me')).toBeVisible());

    await user.click(getByText('Click Me'));

    expect(onSelect).toHaveBeenCalled();
  });

  test('sets aria-current="page" when active', async () => {
    const user = userEvent.setup();
    const { getByRole, getByText } = render({
      components: {
        UiNavigationMenu,
        UiNavigationMenuList,
        UiNavigationMenuItem,
        UiNavigationMenuTrigger,
        UiNavigationMenuContent,
        UiNavigationMenuLink,
      },
      template: `
        <UiNavigationMenu>
          <UiNavigationMenuList>
            <UiNavigationMenuItem value="products">
              <UiNavigationMenuTrigger>Products</UiNavigationMenuTrigger>
              <UiNavigationMenuContent>
                <UiNavigationMenuLink :active="true">Active Link</UiNavigationMenuLink>
                <UiNavigationMenuLink :active="false">Inactive Link</UiNavigationMenuLink>
              </UiNavigationMenuContent>
            </UiNavigationMenuItem>
          </UiNavigationMenuList>
        </UiNavigationMenu>
      `,
    });

    await user.hover(getByRole('button', { name: 'Products' }));

    await waitFor(() => {
      expect(getByText('Active Link')).toHaveAttribute('aria-current', 'page');
      expect(getByText('Inactive Link')).not.toHaveAttribute('aria-current');
    });
  });
});

describe('UiNavigationMenuIndicator', () => {
  test('indicator is present in DOM without interaction when forceMount is true', async () => {
    const { container } = render({
      components: {
        UiNavigationMenu,
        UiNavigationMenuList,
        UiNavigationMenuItem,
        UiNavigationMenuTrigger,
        UiNavigationMenuContent,
        UiNavigationMenuIndicator,
      },
      template: `
        <UiNavigationMenu>
          <UiNavigationMenuList>
            <UiNavigationMenuItem>
              <UiNavigationMenuTrigger>Products</UiNavigationMenuTrigger>
              <UiNavigationMenuContent>Content</UiNavigationMenuContent>
            </UiNavigationMenuItem>
            <UiNavigationMenuIndicator :force-mount="true" />
          </UiNavigationMenuList>
        </UiNavigationMenu>
      `,
    });

    await waitFor(() => {
      expect(
        container.querySelector('[data-slot="navigation-menu-indicator"]'),
      ).toBeInTheDocument();
    });
  });
});
