import { act, fireEvent, mockMatchMedia, render, screen, userEvent } from '../../test-utils';
import { WDS_LIVE_REGION_DELAY_MS } from '../../common/constants';
import { resetLiveRegionAnnouncementQueue } from '../../common/liveRegion/LiveRegion';
import { InfoPrompt, InfoPromptProps } from './InfoPrompt';

mockMatchMedia();

const CUSTOM_MEDIA = <span data-testid="custom-media">Custom Media</span>;

describe('InfoPrompt', () => {
  const defaultProps: InfoPromptProps = {
    description: 'Prompt description',
  };

  beforeEach(() => {
    resetLiveRegionAnnouncementQueue();
  });

  afterEach(() => {
    jest.clearAllTimers();
    jest.useRealTimers();
  });

  it('renders description', () => {
    render(<InfoPrompt {...defaultProps} />);
    expect(screen.getByText('Prompt description')).toBeInTheDocument();
  });

  it('renders rich-content description (ReactNode)', () => {
    render(
      <InfoPrompt
        description={
          <>
            See our <a href="/fees">fee schedule</a>.
          </>
        }
      />,
    );
    expect(screen.getByRole('link', { name: 'fee schedule', hidden: true })).toBeInTheDocument();
    expect(screen.getByRole('link', { name: 'fee schedule', hidden: true })).toHaveAttribute(
      'href',
      '/fees',
    );
  });

  it('re-announces when rich-content description text changes', () => {
    jest.useFakeTimers();

    const { rerender } = render(
      <InfoPrompt
        description={
          <>
            See our <a href="/fees">fee schedule</a>.
          </>
        }
      />,
    );

    act(() => {
      jest.advanceTimersByTime(WDS_LIVE_REGION_DELAY_MS);
    });

    const liveRegion = screen.getByRole('status');
    expect(liveRegion.firstElementChild).not.toHaveAttribute('aria-hidden');
    expect(liveRegion).toHaveTextContent('See our fee schedule.');

    rerender(
      <InfoPrompt
        description={
          <>
            Read the updated <a href="/fees">pricing guide</a>.
          </>
        }
      />,
    );

    expect(liveRegion.firstElementChild).toHaveAttribute('aria-hidden', 'true');

    act(() => {
      jest.advanceTimersByTime(WDS_LIVE_REGION_DELAY_MS);
    });

    expect(liveRegion.firstElementChild).not.toHaveAttribute('aria-hidden');
    expect(liveRegion).toHaveTextContent('Read the updated pricing guide.');
  });

  it('renders title when provided', () => {
    render(<InfoPrompt {...defaultProps} title="Test Title" />);
    expect(screen.getByText('Test Title')).toBeInTheDocument();
    expect(screen.getByText('Prompt description')).toBeInTheDocument();
  });

  it('renders with GiftBox icon as default for `proposition` sentiment', () => {
    render(<InfoPrompt {...defaultProps} sentiment="proposition" data-testid="info-prompt" />);
    expect(screen.getByText('Prompt description')).toBeInTheDocument();
    // GiftBox icon should be rendered for proposition sentiment
    const prompt = screen.getByTestId('info-prompt');
    expect(prompt.querySelector('svg')).toBeInTheDocument();
  });

  describe('sentiments', () => {
    [
      { sentiment: 'negative' as const, surfaceClass: 'wds-prompt--negative' },
      { sentiment: 'warning' as const, surfaceClass: 'wds-prompt--warning' },
      { sentiment: 'neutral' as const, surfaceClass: 'wds-prompt--neutral' },
      { sentiment: 'success' as const, surfaceClass: 'wds-prompt--success' },
      { sentiment: 'proposition' as const, surfaceClass: 'wds-prompt--proposition' },
    ].forEach(({ sentiment, surfaceClass }) => {
      describe(sentiment, () => {
        it('should apply correct styles', () => {
          render(<InfoPrompt {...defaultProps} sentiment={sentiment} data-testid="info-prompt" />);
          expect(screen.getByTestId('info-prompt')).toHaveClass(surfaceClass);
        });
      });
    });
  });

  describe('custom media', () => {
    it('should render custom media when provided', () => {
      render(<InfoPrompt {...defaultProps} media={{ asset: CUSTOM_MEDIA }} />);
      expect(screen.getByTestId('custom-media')).toBeInTheDocument();
    });

    it('should render custom media for any sentiment', () => {
      render(<InfoPrompt {...defaultProps} sentiment="neutral" media={{ asset: CUSTOM_MEDIA }} />);
      expect(screen.getByTestId('custom-media')).toBeInTheDocument();
    });
  });

  describe('action', () => {
    it('should render action link when action is provided with href', async () => {
      render(
        <InfoPrompt {...defaultProps} action={{ label: 'Learn more', href: '/learn-more' }} />,
      );
      const actionLink = await screen.findByRole('link', { name: 'Learn more' });
      expect(actionLink).toBeInTheDocument();
      expect(actionLink).toHaveAttribute('href', '/learn-more');
    });

    it('should render action link with target when provided', async () => {
      render(
        <InfoPrompt
          {...defaultProps}
          action={{ label: 'External link', href: 'https://example.com', target: '_blank' }}
        />,
      );
      const actionLink = await screen.findByRole('link', { name: /External link/i });
      expect(actionLink).toHaveAttribute('target', '_blank');
    });

    it('should call onClick when action button is clicked', async () => {
      const user = userEvent.setup();
      const onClick = jest.fn();
      render(<InfoPrompt {...defaultProps} action={{ label: 'Click me', onClick }} />);
      const actionButton = await screen.findByRole('button', { name: 'Click me' });
      await user.click(actionButton);
      expect(onClick).toHaveBeenCalledTimes(1);
    });

    it('should not render action when not provided', () => {
      render(<InfoPrompt {...defaultProps} />);
      expect(screen.queryByRole('link', { name: 'Learn more' })).not.toBeInTheDocument();
    });
  });

  describe('HTML attributes', () => {
    it('applies custom className, id, and data-testid', () => {
      render(
        <InfoPrompt
          {...defaultProps}
          className="custom-class"
          id="custom-id"
          data-testid="custom-test"
        />,
      );
      const el = screen.getByTestId('custom-test');
      expect(el).toHaveClass('custom-class');
      expect(el).toHaveAttribute('id', 'custom-id');
    });
  });

  describe('SentimentSurface integration', () => {
    it('maps success sentiment correctly for SentimentSurface', () => {
      render(<InfoPrompt {...defaultProps} sentiment="success" data-testid="prompt" />);
      const el = screen.getByTestId('prompt');
      expect(el).toHaveClass('wds-prompt--success');
      expect(el).toHaveClass('wds-sentiment-surface');
    });

    it('passes through other sentiments unchanged', () => {
      render(<InfoPrompt {...defaultProps} sentiment="negative" data-testid="prompt" />);
      const el = screen.getByTestId('prompt');
      expect(el).toHaveClass('wds-prompt--negative');
      expect(el).toHaveClass('wds-sentiment-surface');
    });
  });

  describe('touch interactions', () => {
    const originalLocation = window.location;

    beforeAll(() => {
      jest.spyOn(window, 'open').mockImplementation();
      Object.defineProperty(window, 'location', {
        configurable: true,
        value: {
          ...originalLocation,
          assign: jest.fn(),
        },
      });
    });

    afterEach(() => {
      jest.clearAllMocks();
    });

    afterAll(() => {
      Object.defineProperty(window, 'location', {
        configurable: true,
        value: originalLocation,
      });
    });

    it('should navigate to action href on touch tap', () => {
      render(
        <InfoPrompt
          {...defaultProps}
          action={{ label: 'Learn more', href: '/learn-more' }}
          data-testid="prompt"
        />,
      );
      const prompt = screen.getByTestId('prompt');

      fireEvent.touchStart(prompt);
      expect(window.location.assign).not.toHaveBeenCalled();
      fireEvent.touchEnd(prompt);
      expect(window.location.assign).toHaveBeenCalledWith('/learn-more');
    });

    it('should open in new tab when action target is _blank', () => {
      render(
        <InfoPrompt
          {...defaultProps}
          action={{ label: 'External link', href: 'https://example.com', target: '_blank' }}
          data-testid="prompt"
        />,
      );
      const prompt = screen.getByTestId('prompt');

      fireEvent.touchStart(prompt);
      expect(window.open).not.toHaveBeenCalled();
      fireEvent.touchEnd(prompt);
      expect(window.open).toHaveBeenCalledWith(
        'https://example.com',
        '_blank',
        'noopener, noreferrer',
      );
    });

    it('should not navigate if touch move occurs (scrolling)', () => {
      render(
        <InfoPrompt
          {...defaultProps}
          action={{ label: 'Learn more', href: '/learn-more' }}
          data-testid="prompt"
        />,
      );
      const prompt = screen.getByTestId('prompt');

      fireEvent.touchStart(prompt);
      expect(window.location.assign).not.toHaveBeenCalled();
      fireEvent.touchMove(prompt);
      expect(window.location.assign).not.toHaveBeenCalled();
      fireEvent.touchEnd(prompt);
      expect(window.location.assign).not.toHaveBeenCalled();
    });

    it('should not navigate if no action href is provided', () => {
      render(
        <InfoPrompt
          {...defaultProps}
          action={{ label: 'Click me', onClick: jest.fn() }}
          data-testid="prompt"
        />,
      );
      const prompt = screen.getByTestId('prompt');

      fireEvent.touchStart(prompt);
      fireEvent.touchEnd(prompt);
      expect(window.location.assign).not.toHaveBeenCalled();
      expect(window.open).not.toHaveBeenCalled();
    });
  });
});
