import { fireEvent, render } from '@testing-library/vue';
import { describe, expect, test, vi } from 'vitest';
import {
  UiItem,
  UiItemGroup,
  UiItemSeparator,
  UiItemMedia,
  UiItemContent,
  UiItemTitle,
  UiItemDescription,
  UiItemAction,
  UiItemHeader,
  UiItemFooter,
} from '../index';
import { ItemActionVariants, ItemMediaVariants } from '../constants';

describe('UiItem', () => {
  describe('Rendering', () => {
    test('renders as div by default', () => {
      const { container } = render(UiItem, {
        slots: {
          default: 'Item content',
        },
      });

      const element = container.querySelector('[data-slot="item"]');
      expect(element?.tagName).toBe('DIV');
      expect(element).not.toHaveAttribute('role');
    });

    test('applies listitem role only when list semantics are requested', () => {
      const { container } = render(UiItem, {
        props: {
          withListSemantics: true,
        },
        slots: {
          default: 'Item content',
        },
      });

      const element = container.querySelector('[data-slot="item"]');
      expect(element).toHaveAttribute('role', 'listitem');
    });

    test('renders slot content', () => {
      const { getByText } = render(UiItem, {
        slots: {
          default: 'Item content',
        },
      });

      expect(getByText('Item content')).toBeInTheDocument();
    });

    test('applies default variant classes', () => {
      const { container } = render(UiItem, {
        slots: {
          default: 'Content',
        },
      });

      const element = container.querySelector('[data-slot="item"]');
      expect(element).toHaveClass('bg-transparent');
      expect(element).not.toHaveClass('border');
    });

    test('applies active variant classes', () => {
      const { container } = render(UiItem, {
        props: {
          variant: 'active',
        },
        slots: {
          default: 'Content',
        },
      });

      const element = container.querySelector('[data-slot="item"]');
      expect(element).toHaveClass('ui-active-indicator');
      expect(element).toHaveClass('ui-active-indicator-active');
      expect(element).toHaveClass('bg-background-surface-muted-subtle');
    });

    test('applies outline variant classes', () => {
      const { container } = render(UiItem, {
        props: {
          variant: 'outline',
        },
        slots: {
          default: 'Content',
        },
      });

      const element = container.querySelector('[data-slot="item"]');
      expect(element).toHaveClass('border');
      expect(element).toHaveClass('border-border-surface-default');
    });

    test('applies hover variant classes', () => {
      const { container } = render(UiItem, {
        props: {
          variant: 'hover',
        },
        slots: {
          default: 'Content',
        },
      });

      const element = container.querySelector('[data-slot="item"]');
      expect(element).toHaveClass('cursor-pointer');
      expect(element).toHaveClass('hover:bg-background-surface-muted-subtle');
    });

    test('applies hover-action variant classes', () => {
      const { container } = render(UiItem, {
        props: {
          variant: 'hover-action',
        },
        slots: {
          default: 'Content',
        },
      });

      const element = container.querySelector('[data-slot="item"]');
      expect(element).toHaveClass('cursor-pointer');
      expect(element).toHaveClass('hover:bg-background-surface-muted-subtle');
      expect(element).toHaveClass('has-[button:hover]:hover:bg-transparent');
      expect(element).toHaveClass(
        'has-[[data-slot=item-action]:hover]:hover:bg-transparent',
      );
    });

    test('does not apply action hover suppression outside hover-action variant', () => {
      const { container, getByRole } = render(UiItem, {
        props: {
          title: 'Item title',
          variant: 'hover',
        },
      });

      const element = container.querySelector('[data-slot="item"]');
      expect(element).not.toHaveClass(
        'has-[button:hover]:hover:bg-transparent',
      );
      expect(element).not.toHaveClass(
        'has-[[data-slot=item-action]:hover]:hover:bg-transparent',
      );
      expect(getByRole('button', { name: 'More actions' })).toHaveClass(
        'hover:bg-transparent',
      );
    });

    test('applies size classes', () => {
      const { container } = render(UiItem, {
        props: {
          size: 'sm',
        },
        slots: {
          default: 'Content',
        },
      });

      const element = container.querySelector('[data-slot="item"]');
      expect(element).toHaveClass('p-2');
    });

    test('renders convenience title and description props', () => {
      const { getByRole, getByText, container } = render(UiItem, {
        props: {
          title: 'Item title',
          description: 'Item description',
        },
      });

      expect(getByText('Item title')).toBeInTheDocument();
      expect(getByText('Item description')).toBeInTheDocument();
      expect(
        container.querySelector('[data-slot="item-content"]'),
      ).toBeInTheDocument();
      expect(
        container.querySelector('[data-slot="item-title"]'),
      ).toBeInTheDocument();
      expect(
        container.querySelector('[data-slot="item-description"]'),
      ).toBeInTheDocument();
      expect(getByRole('button', { name: 'More actions' })).toBeInTheDocument();
    });

    test('renders desc prop as description alias', () => {
      const { getByText } = render(UiItem, {
        props: {
          title: 'Item title',
          desc: 'Short description',
        },
      });

      expect(getByText('Short description')).toBeInTheDocument();
    });

    test('renders content slot inside convenience content area', () => {
      const { getByText, container } = render(UiItem, {
        props: {
          title: 'Item title',
        },
        slots: {
          content: '<span>Additional content</span>',
        },
      });

      expect(getByText('Additional content')).toBeInTheDocument();
      expect(
        container.querySelector('[data-slot="item-content"]'),
      ).toBeInTheDocument();
    });

    test('uses default slot as the structured content fallback wrapper', () => {
      const { getByText, queryByText } = render(UiItem, {
        props: {
          title: 'Generated title',
        },
        slots: {
          default: 'Custom content',
        },
      });

      expect(getByText('Custom content')).toBeInTheDocument();
      expect(queryByText('Generated title')).not.toBeInTheDocument();
    });

    test('renders icon and action slots with convenience content', () => {
      const { getByTestId, container } = render(UiItem, {
        props: {
          title: 'Item title',
        },
        slots: {
          icon: '<span data-testid="item-icon">Icon</span>',
          action: '<button data-testid="item-action">Action</button>',
        },
      });

      expect(getByTestId('item-icon')).toBeInTheDocument();
      expect(getByTestId('item-action')).toBeInTheDocument();
      expect(
        container.querySelector('[data-slot="item-media"]'),
      ).toBeInTheDocument();
      expect(
        container.querySelector('[data-slot="item-action"]'),
      ).toBeInTheDocument();
    });

    test('renders default ghost action with icon-sm for default and small sizes', () => {
      const defaultItem = render(UiItem, {
        props: {
          title: 'Default item',
        },
      });

      const defaultAction = defaultItem.getByRole('button', {
        name: 'More actions',
      });
      expect(defaultAction).toHaveClass('text-foreground');
      expect(defaultAction).toHaveClass('hover:bg-transparent');
      expect(defaultAction.className).toContain('[&_svg]:size-4');

      const smallItem = render(UiItem, {
        props: {
          title: 'Small item',
          size: 'sm',
        },
      });

      const smallAction = smallItem.container.querySelector(
        '[aria-label="More actions"]',
      );
      expect(smallAction?.className).toContain('[&_svg]:size-4');
    });

    test('keeps action hover only for hover-action variant', () => {
      const { container, getByRole } = render(UiItem, {
        props: {
          title: 'Item title',
          variant: 'hover-action',
        },
      });

      const element = container.querySelector('[data-slot="item"]');
      const action = getByRole('button', { name: 'More actions' });

      expect(element).toHaveClass('hover:bg-background-surface-muted-subtle');
      expect(element).toHaveClass('has-[button:hover]:hover:bg-transparent');
      expect(action).toHaveClass('hover:bg-background-surface-muted-subtle');
      expect(action).not.toHaveClass('hover:bg-transparent');
    });

    test('renders default ghost action with icon-xs for minimal size', () => {
      const { getByRole } = render(UiItem, {
        props: {
          title: 'Minimal item',
          size: 'minimal',
        },
      });

      const defaultAction = getByRole('button', { name: 'More actions' });
      expect(defaultAction.className).toContain('[&_svg]:size-3');
    });

    test('emits action when the default action is clicked', async () => {
      const onAction = vi.fn();
      const { getByRole } = render(UiItem, {
        props: {
          title: 'Item title',
          onAction,
        },
      });

      await fireEvent.click(getByRole('button', { name: 'More actions' }));

      expect(onAction).toHaveBeenCalledTimes(1);
      expect(onAction.mock.calls[0]?.[0]).toBeInstanceOf(MouseEvent);
    });

    test('does not infer hover background when action slot is present', () => {
      const { container } = render(UiItem, {
        props: {
          title: 'Item title',
        },
        slots: {
          action: '<button>Action</button>',
        },
      });

      const element = container.querySelector('[data-slot="item"]');
      expect(element).not.toHaveClass(
        'hover:bg-background-surface-muted-subtle',
      );
    });
  });

  describe('asChild behavior', () => {
    test('renders child element as root when asChild is true with anchor tag', () => {
      const { container } = render(UiItem, {
        props: {
          asChild: true,
        },
        slots: {
          default: '<a href="/test">Link Item</a>',
        },
      });

      const element = container.querySelector('[data-slot="item"]');
      expect(element).toBeInTheDocument();
      expect(element?.tagName).toBe('A');
      expect(element).toHaveAttribute('href', '/test');
    });

    test('renders child element as root when asChild is true with button tag', () => {
      const { container } = render(UiItem, {
        props: {
          asChild: true,
        },
        slots: {
          default: '<button type="button">Button Item</button>',
        },
      });

      const element = container.querySelector('[data-slot="item"]');
      expect(element).toBeInTheDocument();
      expect(element?.tagName).toBe('BUTTON');
      expect(element).toHaveAttribute('type', 'button');
    });

    test('applies computed classes to child element when asChild is true', () => {
      const { container } = render(UiItem, {
        props: {
          asChild: true,
          variant: 'outline',
          size: 'sm',
        },
        slots: {
          default: '<a href="/test">Link Item</a>',
        },
      });

      const element = container.querySelector('[data-slot="item"]');
      expect(element?.tagName).toBe('A');
      // Check base classes from itemVariants are applied
      expect(element).toHaveClass('flex');
      expect(element).toHaveClass('items-center');
      expect(element).toHaveClass('rounded-md');
      // Check variant classes are applied
      expect(element).toHaveClass('border-border-surface-default');
      // Check size classes are applied
      expect(element).toHaveClass('p-2');
    });

    test('applies default variant classes to child element when asChild is true', () => {
      const { container } = render(UiItem, {
        props: {
          asChild: true,
        },
        slots: {
          default: '<button>Button</button>',
        },
      });

      const element = container.querySelector('[data-slot="item"]');
      expect(element?.tagName).toBe('BUTTON');
      expect(element).toHaveClass('bg-transparent');
      expect(element).toHaveClass('p-3');
    });

    test('does not infer hover background when rendered as child', () => {
      const { container } = render(UiItem, {
        props: {
          asChild: true,
        },
        slots: {
          default: '<a href="/test">Link Item</a>',
        },
      });

      const element = container.querySelector('[data-slot="item"]');
      expect(element).not.toHaveClass(
        'hover:bg-background-surface-muted-subtle',
      );
    });
  });
});

describe('UiItemGroup', () => {
  test('renders with data-slot attribute', () => {
    const { container } = render(UiItemGroup, {
      slots: {
        default: 'Group content',
      },
    });

    const element = container.querySelector('[data-slot="item-group"]');
    expect(element).toBeInTheDocument();
    expect(element).not.toHaveAttribute('role');
  });

  test('renders children', () => {
    const { getByText } = render(UiItemGroup, {
      slots: {
        default: 'Group content',
      },
    });

    expect(getByText('Group content')).toBeInTheDocument();
  });
});

describe('UiItemSeparator', () => {
  test('renders with data-slot attribute', () => {
    const { container } = render(UiItemSeparator);

    const element = container.querySelector('[data-slot="item-separator"]');
    expect(element).toBeInTheDocument();
  });
});

describe('UiItemMedia', () => {
  test('renders with default variant', () => {
    const { container } = render(UiItemMedia, {
      slots: {
        default: 'Media content',
      },
    });

    const element = container.querySelector('[data-slot="item-media"]');
    expect(element).toBeInTheDocument();
    expect(element).toHaveClass('bg-transparent');
  });

  test('renders with icon variant', () => {
    const { container } = render(UiItemMedia, {
      props: {
        variant: 'icon',
      },
      slots: {
        default: 'Icon content',
      },
    });

    const element = container.querySelector('[data-slot="item-media"]');
    expect(element).toHaveClass('size-8');
  });

  test('renders with image variant', () => {
    const { container } = render(UiItemMedia, {
      props: {
        variant: 'image',
      },
      slots: {
        default: 'Image content',
      },
    });

    const element = container.querySelector('[data-slot="item-media"]');
    expect(element).toHaveClass('size-10');
    expect(element).toHaveClass('rounded-full');
  });

  test('image variant can render avatar content', () => {
    const { container } = render(UiItemMedia, {
      props: {
        variant: 'image',
      },
      slots: {
        default: '<div data-slot="avatar">Avatar</div>',
      },
    });

    expect(
      container.querySelector('[data-slot="item-media"] [data-slot="avatar"]'),
    ).toBeInTheDocument();
    expect(container.querySelector('[data-slot="item-media"]')).toHaveClass(
      'size-10',
    );
  });

  test('renders with avatar group variant', () => {
    const { container } = render(UiItemMedia, {
      props: {
        variant: 'avatar-group',
      },
      slots: {
        default: 'Avatar group content',
      },
    });

    const element = container.querySelector('[data-slot="item-media"]');
    expect(element).toHaveClass('rounded-none');
    expect(element).toHaveClass('overflow-visible');
  });

  test('avatar group variant can render avatar group content', () => {
    const { container } = render(UiItemMedia, {
      props: {
        variant: 'avatar-group',
      },
      slots: {
        default: '<div data-slot="avatar-group">Avatar group</div>',
      },
    });

    expect(
      container.querySelector(
        '[data-slot="item-media"] [data-slot="avatar-group"]',
      ),
    ).toBeInTheDocument();
  });

  test('media variants only include media-oriented variants', () => {
    expect(ItemMediaVariants).toEqual([
      'default',
      'icon',
      'image',
      'avatar-group',
    ]);
    expect(ItemMediaVariants).not.toContain('text');
    expect(ItemMediaVariants).not.toContain('button');
  });
});

describe('UiItemContent', () => {
  test('renders with data-slot attribute', () => {
    const { container } = render(UiItemContent, {
      slots: {
        default: 'Content',
      },
    });

    const element = container.querySelector('[data-slot="item-content"]');
    expect(element).toBeInTheDocument();
  });

  test('has flex column layout', () => {
    const { container } = render(UiItemContent, {
      slots: {
        default: 'Content',
      },
    });

    const element = container.querySelector('[data-slot="item-content"]');
    expect(element).toHaveClass('flex');
    expect(element).toHaveClass('flex-col');
  });
});

describe('UiItemTitle', () => {
  test('renders with data-slot attribute', () => {
    const { container } = render(UiItemTitle, {
      slots: {
        default: 'Title text',
      },
    });

    const element = container.querySelector('[data-slot="item-title"]');
    expect(element).toBeInTheDocument();
  });

  test('renders title text', () => {
    const { getByText } = render(UiItemTitle, {
      slots: {
        default: 'My Title',
      },
    });

    expect(getByText('My Title')).toBeInTheDocument();
  });

  test('uses token typography utility for title weight', () => {
    const { container } = render(UiItemTitle, {
      slots: {
        default: 'Title',
      },
    });

    const element = container.querySelector('[data-slot="item-title"]');
    expect(element).toHaveClass('body-14-medium');
  });
});

describe('UiItemDescription', () => {
  test('renders with data-slot attribute', () => {
    const { container } = render(UiItemDescription, {
      slots: {
        default: 'Description text',
      },
    });

    const element = container.querySelector('[data-slot="item-description"]');
    expect(element).toBeInTheDocument();
  });

  test('renders description text', () => {
    const { getByText } = render(UiItemDescription, {
      slots: {
        default: 'My Description',
      },
    });

    expect(getByText('My Description')).toBeInTheDocument();
  });

  test('has muted foreground color', () => {
    const { container } = render(UiItemDescription, {
      slots: {
        default: 'Description',
      },
    });

    const element = container.querySelector('[data-slot="item-description"]');
    expect(element).toHaveClass('text-content-on-surface-muted');
  });
});

describe('UiItemAction', () => {
  test('renders with data-slot attribute', () => {
    const { container } = render(UiItemAction, {
      slots: {
        default: 'Action content',
      },
    });

    const element = container.querySelector('[data-slot="item-action"]');
    expect(element).toBeInTheDocument();
  });

  test('has flex layout with gap', () => {
    const { container } = render(UiItemAction, {
      slots: {
        default: 'Action',
      },
    });

    const element = container.querySelector('[data-slot="item-action"]');
    expect(element).toHaveClass('flex');
    expect(element).toHaveClass('gap-2');
  });

  test('renders with tooltip wrapper when tooltip is provided', () => {
    const { container } = render(UiItemAction, {
      props: {
        tooltip: 'Open actions',
      },
      slots: {
        default: '<button type="button">Open</button>',
      },
    });

    const element = container.querySelector('[data-slot="item-action"]');
    expect(element).toBeInTheDocument();
    expect(element).toHaveTextContent('Open');
  });

  test('renders with text variant', () => {
    const { container } = render(UiItemAction, {
      props: {
        variant: 'text',
      },
      slots: {
        default: 'Updated today',
      },
    });

    const element = container.querySelector('[data-slot="item-action"]');
    expect(element).toHaveClass('label-12-regular');
    expect(element).toHaveClass('text-content-on-surface-muted');
    expect(element).toHaveClass('gap-2');
  });

  test('renders with button variant', () => {
    const { container } = render(UiItemAction, {
      props: {
        variant: 'button',
      },
      slots: {
        default: 'Button action',
      },
    });

    const element = container.querySelector('[data-slot="item-action"]');
    expect(element).toHaveClass('size-6');
    expect(element).toHaveClass('rounded-sm');
    expect(element).toHaveClass('hover:bg-background-surface-muted-subtle');
  });

  test('suppresses wrapper hover layer when button variant contains a button', () => {
    const { container } = render(UiItemAction, {
      props: {
        variant: 'button',
      },
      slots: {
        default: '<button type="button">Open</button>',
      },
    });

    const element = container.querySelector('[data-slot="item-action"]');
    expect(element).toHaveClass('hover:bg-background-surface-muted-subtle');
    expect(element).toHaveClass('has-[button]:hover:bg-transparent');
    expect(element?.querySelector('button')).toBeInTheDocument();
  });

  test('applies variant classes inside tooltip wrapper', () => {
    const { container } = render(UiItemAction, {
      props: {
        variant: 'button',
        tooltip: 'Create item',
      },
      slots: {
        default: '<button type="button">Create</button>',
      },
    });

    const element = container.querySelector('[data-slot="item-action"]');
    expect(element).toHaveClass('size-6');
    expect(element).toHaveTextContent('Create');
  });

  test('action variants include text and button variants', () => {
    expect(ItemActionVariants).toEqual(['default', 'text', 'button']);
  });
});

describe('UiItemHeader', () => {
  test('renders with data-slot attribute', () => {
    const { container } = render(UiItemHeader, {
      slots: {
        default: 'Header text',
      },
    });

    const element = container.querySelector('[data-slot="item-header"]');
    expect(element).toBeInTheDocument();
  });

  test('renders header text', () => {
    const { getByText } = render(UiItemHeader, {
      slots: {
        default: 'CATEGORY',
      },
    });

    expect(getByText('CATEGORY')).toBeInTheDocument();
  });
});

describe('UiItemFooter', () => {
  test('renders with data-slot attribute', () => {
    const { container } = render(UiItemFooter, {
      slots: {
        default: 'Footer text',
      },
    });

    const element = container.querySelector('[data-slot="item-footer"]');
    expect(element).toBeInTheDocument();
  });

  test('renders footer text', () => {
    const { getByText } = render(UiItemFooter, {
      slots: {
        default: 'Updated 2 hours ago',
      },
    });

    expect(getByText('Updated 2 hours ago')).toBeInTheDocument();
  });
});

describe('Composed Item', () => {
  test('renders a complete item with all sub-components', () => {
    const { getByText, container } = render({
      components: {
        UiItem,
        UiItemMedia,
        UiItemContent,
        UiItemTitle,
        UiItemDescription,
        UiItemAction,
      },
      template: `
        <UiItem variant="outline">
          <UiItemMedia variant="icon">
            <span data-testid="icon">Icon</span>
          </UiItemMedia>
          <UiItemContent>
            <UiItemTitle>Test Title</UiItemTitle>
            <UiItemDescription>Test Description</UiItemDescription>
          </UiItemContent>
          <UiItemAction>
            <button>Action</button>
          </UiItemAction>
        </UiItem>
      `,
    });

    expect(getByText('Test Title')).toBeInTheDocument();
    expect(getByText('Test Description')).toBeInTheDocument();
    expect(getByText('Action')).toBeInTheDocument();
    expect(container.querySelector('[data-slot="item"]')).toBeInTheDocument();
    expect(
      container.querySelector('[data-slot="item-media"]'),
    ).toBeInTheDocument();
    expect(
      container.querySelector('[data-slot="item-content"]'),
    ).toBeInTheDocument();
    expect(
      container.querySelector('[data-slot="item-title"]'),
    ).toBeInTheDocument();
    expect(
      container.querySelector('[data-slot="item-description"]'),
    ).toBeInTheDocument();
    expect(
      container.querySelector('[data-slot="item-action"]'),
    ).toBeInTheDocument();
  });
});
