import { render, waitFor } from '@testing-library/vue';
import userEvent from '@testing-library/user-event';
import { describe, expect, test, vi } from 'vitest';
import {
  UiAlertDialog,
  UiAlertDialogAction,
  UiAlertDialogCancel,
  UiAlertDialogContent,
  UiAlertDialogDescription,
  UiAlertDialogFooter,
  UiAlertDialogHeader,
  UiAlertDialogTitle,
  UiAlertDialogTrigger,
} from '../index';

/**
 *
 * @param options
 * @param options.model
 */
function renderAlertDialog(options: { model?: boolean } = {}) {
  const { model } = options;

  return render({
    components: {
      UiAlertDialog,
      UiAlertDialogAction,
      UiAlertDialogCancel,
      UiAlertDialogContent,
      UiAlertDialogDescription,
      UiAlertDialogFooter,
      UiAlertDialogHeader,
      UiAlertDialogTitle,
      UiAlertDialogTrigger,
    },
    template: `
      <UiAlertDialog ${model !== undefined ? ':model="model"' : ''}>
        <UiAlertDialogTrigger>Open</UiAlertDialogTrigger>
        <UiAlertDialogContent>
          <UiAlertDialogHeader>
            <UiAlertDialogTitle>Are you absolutely sure?</UiAlertDialogTitle>
            <UiAlertDialogDescription>
              This action cannot be undone.
            </UiAlertDialogDescription>
          </UiAlertDialogHeader>
          <UiAlertDialogFooter>
            <UiAlertDialogCancel>Cancel</UiAlertDialogCancel>
            <UiAlertDialogAction>Continue</UiAlertDialogAction>
          </UiAlertDialogFooter>
        </UiAlertDialogContent>
      </UiAlertDialog>
    `,
    setup() {
      return { model };
    },
  });
}

/**
 *
 */
function renderAlertDialogWithSpy() {
  const onUpdateModel = vi.fn();

  const result = render({
    components: {
      UiAlertDialog,
      UiAlertDialogAction,
      UiAlertDialogCancel,
      UiAlertDialogContent,
      UiAlertDialogDescription,
      UiAlertDialogFooter,
      UiAlertDialogHeader,
      UiAlertDialogTitle,
      UiAlertDialogTrigger,
    },
    template: `
      <UiAlertDialog @update:model="onUpdateModel">
        <UiAlertDialogTrigger>Open</UiAlertDialogTrigger>
        <UiAlertDialogContent>
          <UiAlertDialogHeader>
            <UiAlertDialogTitle>Are you absolutely sure?</UiAlertDialogTitle>
            <UiAlertDialogDescription>
              This action cannot be undone.
            </UiAlertDialogDescription>
          </UiAlertDialogHeader>
          <UiAlertDialogFooter>
            <UiAlertDialogCancel>Cancel</UiAlertDialogCancel>
            <UiAlertDialogAction>Continue</UiAlertDialogAction>
          </UiAlertDialogFooter>
        </UiAlertDialogContent>
      </UiAlertDialog>
    `,
    setup() {
      return { onUpdateModel };
    },
  });

  return { ...result, onUpdateModel };
}

describe('UiAlertDialog', () => {
  test('opens when trigger is clicked', async () => {
    const user = userEvent.setup();
    const { getByRole, getByText } = renderAlertDialog();

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

    await waitFor(() => {
      expect(getByText('Are you absolutely sure?')).toBeInTheDocument();
    });
  });

  test('is visible when model is true', async () => {
    const { getByText } = renderAlertDialog({ model: true });

    await waitFor(() => {
      expect(getByText('Are you absolutely sure?')).toBeInTheDocument();
    });
  });

  test('keeps content centered during open and close animations', async () => {
    const { getByRole } = renderAlertDialog({ model: true });

    await waitFor(() => {
      expect(getByRole('alertdialog')).toBeInTheDocument();
    });

    const alertDialog = getByRole('alertdialog');

    expect(alertDialog).toHaveClass('data-[state=open]:slide-in-from-left-1/2');
    expect(alertDialog).toHaveClass('data-[state=open]:slide-in-from-top-1/2');
    expect(alertDialog).toHaveClass(
      'data-[state=closed]:slide-out-to-left-1/2',
    );
    expect(alertDialog).toHaveClass('data-[state=closed]:slide-out-to-top-1/2');
  });

  test('closes when Cancel button is clicked', async () => {
    const user = userEvent.setup();
    const { getByRole, queryByText } = renderAlertDialog();

    await user.click(getByRole('button', { name: 'Open' }));
    await waitFor(() => {
      expect(getByRole('alertdialog')).toBeInTheDocument();
    });

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

    await waitFor(() => {
      expect(queryByText('Are you absolutely sure?')).not.toBeInTheDocument();
    });
  });

  test('closes when Action button is clicked', async () => {
    const user = userEvent.setup();
    const { getByRole, queryByText } = renderAlertDialog();

    await user.click(getByRole('button', { name: 'Open' }));
    await waitFor(() => {
      expect(getByRole('alertdialog')).toBeInTheDocument();
    });

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

    await waitFor(() => {
      expect(queryByText('Are you absolutely sure?')).not.toBeInTheDocument();
    });
  });

  test('closes on Escape key press', async () => {
    const user = userEvent.setup();
    const { getByRole, queryByText } = renderAlertDialog();

    await user.click(getByRole('button', { name: 'Open' }));
    await waitFor(() => {
      expect(getByRole('alertdialog')).toBeInTheDocument();
    });

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

    await waitFor(() => {
      expect(queryByText('Are you absolutely sure?')).not.toBeInTheDocument();
    });
  });

  test('emits update:model when opened', async () => {
    const user = userEvent.setup();
    const { getByRole, onUpdateModel } = renderAlertDialogWithSpy();

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

    expect(onUpdateModel).toHaveBeenCalledWith(true);
  });

  test('emits update:model when closed via Cancel', async () => {
    const user = userEvent.setup();
    const { getByRole, onUpdateModel } = renderAlertDialogWithSpy();

    await user.click(getByRole('button', { name: 'Open' }));
    await waitFor(() => {
      expect(getByRole('alertdialog')).toBeInTheDocument();
    });

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

    expect(onUpdateModel).toHaveBeenCalledWith(false);
  });
});
