import { screen, render, mockMatchMedia, mockResizeObserver, waitFor } from '../test-utils';
import { Sentiment } from '../common';

import ExpressiveMoneyInput from './ExpressiveMoneyInput';
import type { Props as ExpressiveMoneyInputProps } from './ExpressiveMoneyInput';
import { Confetti } from '@transferwise/icons';

const defaultProps: ExpressiveMoneyInputProps = {
  label: 'You send',
  currency: 'GBP',
  amount: 1234.56,
  onAmountChange: jest.fn(),
};

const currencySelectorOptions = [
  {
    title: 'Popular',
    currencies: [
      { code: 'USD', label: 'US Dollar', keywords: ['dollar', 'us'] },
      { code: 'AUD', label: 'Australia Dollar', keywords: ['dollar', 'au'] },
    ],
  },
  {
    title: 'Others',
    currencies: [
      { code: 'GBP', label: 'Pound', keywords: ['british'] },
      { code: 'EUR', label: 'Euro', keywords: ['euro'] },
    ],
  },
];

mockMatchMedia();
mockResizeObserver();

describe('ExpressiveMoneyInput', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  describe('Basic rendering', () => {
    it('renders with basic props', () => {
      render(<ExpressiveMoneyInput {...defaultProps} />);

      expect(screen.getByText('You send')).toBeInTheDocument();
      expect(screen.getByDisplayValue('1,234.56')).toBeInTheDocument();
    });

    it('renders with custom label', () => {
      render(<ExpressiveMoneyInput {...defaultProps} label="Amount to transfer" />);

      expect(screen.getByText('Amount to transfer')).toBeInTheDocument();
    });

    it('renders without label', () => {
      render(<ExpressiveMoneyInput {...defaultProps} label={undefined} />);

      expect(screen.getByRole('textbox')).toBeInTheDocument();
      expect(screen.queryByText('You send')).not.toBeInTheDocument();
    });

    it('renders with different currencies', () => {
      render(<ExpressiveMoneyInput {...defaultProps} currency="USD" />);

      expect(screen.getByText('USD')).toBeInTheDocument();
    });

    it('renders with null amount', () => {
      render(<ExpressiveMoneyInput {...defaultProps} amount={null} />);

      const input = screen.getByRole('textbox');
      expect(input).toHaveValue('');
    });
  });

  describe('Inline prompt', () => {
    it('renders basic message without sentiment', () => {
      render(
        <ExpressiveMoneyInput
          {...defaultProps}
          inlinePrompt={{ message: 'This is a basic message' }}
        />,
      );

      expect(screen.getByText('This is a basic message')).toBeInTheDocument();
    });

    it('renders inline prompt with sentiment', () => {
      render(
        <ExpressiveMoneyInput
          {...defaultProps}
          inlinePrompt={{
            message: 'Success message',
            sentiment: Sentiment.POSITIVE,
          }}
        />,
      );

      expect(screen.getByText('Success message')).toBeInTheDocument();
    });

    it('renders inline prompt with media', () => {
      render(
        <ExpressiveMoneyInput
          {...defaultProps}
          inlinePrompt={{
            message: 'Message with icon',
            sentiment: Sentiment.POSITIVE,
            media: <Confetti data-testid="test-icon" />,
          }}
        />,
      );

      expect(screen.getByText('Message with icon')).toBeInTheDocument();
      expect(screen.getByTestId('test-icon')).toBeInTheDocument();
    });

    it('does not render inline prompt when not provided', () => {
      render(<ExpressiveMoneyInput {...defaultProps} />);

      expect(document.querySelector('.wds-inline-prompt')).not.toBeInTheDocument();
    });

    it('animates inline prompt appearance', async () => {
      const { rerender } = render(<ExpressiveMoneyInput {...defaultProps} />);

      expect(screen.queryByText('New message')).not.toBeInTheDocument();

      rerender(
        <ExpressiveMoneyInput {...defaultProps} inlinePrompt={{ message: 'New message' }} />,
      );

      await waitFor(() => {
        expect(screen.getByText('New message')).toBeInTheDocument();
      });
    });

    it('sets correct aria-describedby when inline prompt is present', () => {
      render(
        <ExpressiveMoneyInput
          {...defaultProps}
          inlinePrompt={{ message: 'Descriptive message' }}
        />,
      );

      const group = screen.getByRole('group');
      expect(group).toHaveAttribute('aria-describedby');
    });
  });

  describe('Chevron', () => {
    it('shows chevron when showChevron is true', () => {
      render(<ExpressiveMoneyInput {...defaultProps} showChevron />);

      const chevronContainer = document.querySelector('.wds-expressive-money-input-chevron');
      expect(chevronContainer).toBeInTheDocument();
    });

    it('hides chevron when showChevron is false', () => {
      render(<ExpressiveMoneyInput {...defaultProps} showChevron={false} />);

      const chevronContainer = document.querySelector('.wds-expressive-money-input-chevron');
      expect(chevronContainer).toBeInTheDocument();
    });

    it('does not show chevron by default', () => {
      render(<ExpressiveMoneyInput {...defaultProps} />);

      const chevronContainer = document.querySelector('.wds-expressive-money-input-chevron');
      expect(chevronContainer).toBeInTheDocument();
    });
  });

  describe('Accessibility', () => {
    it('has proper ARIA structure', () => {
      render(<ExpressiveMoneyInput {...defaultProps} />);

      const group = screen.getByRole('group');
      const label = screen.getByText('You send');
      const input = screen.getByRole('textbox');

      expect(group).toHaveAttribute('aria-labelledby');
      expect(label).toHaveAttribute('for');
      expect(input).toHaveAttribute('id');
    });

    it('associates label with input correctly', () => {
      render(<ExpressiveMoneyInput {...defaultProps} />);

      const label = screen.getByText('You send');
      const input = screen.getByRole('textbox');

      expect(label).toHaveAttribute('for', input.getAttribute('id'));
    });

    it('links currency selector to main label', () => {
      render(
        <ExpressiveMoneyInput
          {...defaultProps}
          currencySelector={{
            options: currencySelectorOptions,
            onChange: jest.fn(),
          }}
        />,
      );

      const currencySelector = screen.getByRole('combobox');
      const mainLabel = screen.getByText('You send');

      expect(currencySelector).toHaveAttribute('aria-describedby', mainLabel.getAttribute('id'));
    });

    it('describes amount input with currency selector', () => {
      render(
        <ExpressiveMoneyInput
          {...defaultProps}
          currencySelector={{
            options: currencySelectorOptions,
            onChange: jest.fn(),
          }}
        />,
      );

      const input = screen.getByRole('textbox');
      const currencySelector = screen.getByRole('combobox');

      expect(input).toHaveAttribute('aria-describedby', currencySelector.getAttribute('id'));
    });
  });
});
