import '@testing-library/jest-dom/vitest';
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 {
  UiAccordion,
  UiAccordionItem,
  UiAccordionTrigger,
  UiAccordionContent,
} from '../index';

describe('UiAccordion', () => {
  test('emits update:modelValue with string when item is expanded (single mode)', async () => {
    const user = userEvent.setup();
    const onUpdateModelValue = vi.fn();

    const { getByRole } = render({
      components: {
        UiAccordion,
        UiAccordionItem,
        UiAccordionTrigger,
        UiAccordionContent,
      },
      template: `
        <UiAccordion type="single" collapsible @update:modelValue="onUpdateModelValue">
          <UiAccordionItem value="item-1">
            <UiAccordionTrigger>Item 1</UiAccordionTrigger>
            <UiAccordionContent>Content 1</UiAccordionContent>
          </UiAccordionItem>
          <UiAccordionItem value="item-2">
            <UiAccordionTrigger>Item 2</UiAccordionTrigger>
            <UiAccordionContent>Content 2</UiAccordionContent>
          </UiAccordionItem>
        </UiAccordion>
      `,
      setup() {
        return { onUpdateModelValue };
      },
    });

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

    expect(onUpdateModelValue).toHaveBeenCalledWith('item-1');
  });

  test('emits update:modelValue with array when item is toggled (multiple mode)', async () => {
    const user = userEvent.setup();
    const onUpdateModelValue = vi.fn();

    const { getByRole } = render({
      components: {
        UiAccordion,
        UiAccordionItem,
        UiAccordionTrigger,
        UiAccordionContent,
      },
      template: `
        <UiAccordion type="multiple" @update:modelValue="onUpdateModelValue">
          <UiAccordionItem value="item-1">
            <UiAccordionTrigger>Item 1</UiAccordionTrigger>
            <UiAccordionContent>Content 1</UiAccordionContent>
          </UiAccordionItem>
          <UiAccordionItem value="item-2">
            <UiAccordionTrigger>Item 2</UiAccordionTrigger>
            <UiAccordionContent>Content 2</UiAccordionContent>
          </UiAccordionItem>
        </UiAccordion>
      `,
      setup() {
        return { onUpdateModelValue };
      },
    });

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

    expect(onUpdateModelValue).toHaveBeenCalledWith(['item-1']);
  });

  test('reflects controlled modelValue', async () => {
    const { getByRole, getByText } = render({
      components: {
        UiAccordion,
        UiAccordionItem,
        UiAccordionTrigger,
        UiAccordionContent,
      },
      template: `
        <UiAccordion type="single" collapsible :model-value="value">
          <UiAccordionItem value="item-1">
            <UiAccordionTrigger>Item 1</UiAccordionTrigger>
            <UiAccordionContent>Content 1</UiAccordionContent>
          </UiAccordionItem>
          <UiAccordionItem value="item-2">
            <UiAccordionTrigger>Item 2</UiAccordionTrigger>
            <UiAccordionContent>Content 2</UiAccordionContent>
          </UiAccordionItem>
        </UiAccordion>
      `,
      setup() {
        const value = ref('item-2');
        return { value };
      },
    });

    await waitFor(() => {
      expect(getByRole('button', { name: 'Item 2' })).toHaveAttribute(
        'data-state',
        'open',
      );
      expect(getByText('Content 2')).toBeVisible();
    });
  });

  test('disabled items do not respond to clicks', async () => {
    const user = userEvent.setup();
    const { getByRole } = render({
      components: {
        UiAccordion,
        UiAccordionItem,
        UiAccordionTrigger,
        UiAccordionContent,
      },
      template: `
        <UiAccordion type="single" collapsible>
          <UiAccordionItem value="item-1" :disabled="true">
            <UiAccordionTrigger>Disabled Item</UiAccordionTrigger>
            <UiAccordionContent>Content</UiAccordionContent>
          </UiAccordionItem>
        </UiAccordion>
      `,
    });

    const trigger = getByRole('button', { name: 'Disabled Item' });
    expect(trigger).toBeDisabled();

    await user.click(trigger);

    await waitFor(() => {
      expect(trigger).toHaveAttribute('data-state', 'closed');
    });
  });

  test('collapses open item when collapsible is true (single mode)', async () => {
    const user = userEvent.setup();
    const { getByRole } = render({
      components: {
        UiAccordion,
        UiAccordionItem,
        UiAccordionTrigger,
        UiAccordionContent,
      },
      template: `
        <UiAccordion type="single" collapsible>
          <UiAccordionItem value="item-1">
            <UiAccordionTrigger>Item 1</UiAccordionTrigger>
            <UiAccordionContent>Content 1</UiAccordionContent>
          </UiAccordionItem>
        </UiAccordion>
      `,
    });

    const trigger = getByRole('button', { name: 'Item 1' });

    // Open the item
    await user.click(trigger);
    await waitFor(() => {
      expect(trigger).toHaveAttribute('data-state', 'open');
    });

    // Click again to collapse
    await user.click(trigger);
    await waitFor(() => {
      expect(trigger).toHaveAttribute('data-state', 'closed');
    });
  });

  test('expands items specified in defaultValue on initial render', async () => {
    const { getByRole, getByText } = render({
      components: {
        UiAccordion,
        UiAccordionItem,
        UiAccordionTrigger,
        UiAccordionContent,
      },
      template: `
        <UiAccordion type="single" default-value="item-2">
          <UiAccordionItem value="item-1">
            <UiAccordionTrigger>Item 1</UiAccordionTrigger>
            <UiAccordionContent>Content 1</UiAccordionContent>
          </UiAccordionItem>
          <UiAccordionItem value="item-2">
            <UiAccordionTrigger>Item 2</UiAccordionTrigger>
            <UiAccordionContent>Content 2</UiAccordionContent>
          </UiAccordionItem>
        </UiAccordion>
      `,
    });

    await waitFor(() => {
      expect(getByRole('button', { name: 'Item 1' })).toHaveAttribute(
        'data-state',
        'closed',
      );
      expect(getByRole('button', { name: 'Item 2' })).toHaveAttribute(
        'data-state',
        'open',
      );
      expect(getByText('Content 2')).toBeVisible();
    });
  });

  test('expands multiple items specified in defaultValue (multiple mode)', async () => {
    const { getByRole, getByText } = render({
      components: {
        UiAccordion,
        UiAccordionItem,
        UiAccordionTrigger,
        UiAccordionContent,
      },
      template: `
        <UiAccordion type="multiple" :default-value="['item-1', 'item-3']">
          <UiAccordionItem value="item-1">
            <UiAccordionTrigger>Item 1</UiAccordionTrigger>
            <UiAccordionContent>Content 1</UiAccordionContent>
          </UiAccordionItem>
          <UiAccordionItem value="item-2">
            <UiAccordionTrigger>Item 2</UiAccordionTrigger>
            <UiAccordionContent>Content 2</UiAccordionContent>
          </UiAccordionItem>
          <UiAccordionItem value="item-3">
            <UiAccordionTrigger>Item 3</UiAccordionTrigger>
            <UiAccordionContent>Content 3</UiAccordionContent>
          </UiAccordionItem>
        </UiAccordion>
      `,
    });

    await waitFor(() => {
      expect(getByRole('button', { name: 'Item 1' })).toHaveAttribute(
        'data-state',
        'open',
      );
      expect(getByRole('button', { name: 'Item 2' })).toHaveAttribute(
        'data-state',
        'closed',
      );
      expect(getByRole('button', { name: 'Item 3' })).toHaveAttribute(
        'data-state',
        'open',
      );
      expect(getByText('Content 1')).toBeVisible();
      expect(getByText('Content 3')).toBeVisible();
    });
  });
});
