import React from 'react';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { AddBeneficiaryModal } from '../AddBeneficiaryModal';
import * as api from '../../lib/api';

jest.mock('../../lib/api');
const mockCreateBeneficiary = api.createBeneficiary as jest.MockedFunction<
  typeof api.createBeneficiary
>;

describe('AddBeneficiaryModal', () => {
  const defaultProps = {
    isOpen: true,
    onClose: jest.fn(),
    onSuccess: jest.fn(),
  };

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

  const getNameInput = () => screen.getByPlaceholderText('e.g., Grandma Julia');
  const getAddressInput = () => screen.getByPlaceholderText('123 Main St, City, State');
  const getPhoneInput = () => screen.getByPlaceholderText('+1 (555) 123-4567');
  const getSubmitButton = () => screen.getByRole('button', { name: /Add Beneficiary/i });
  const getCancelButton = () => screen.getByRole('button', { name: /Cancel/i });

  it('does not render when isOpen is false', () => {
    render(<AddBeneficiaryModal {...defaultProps} isOpen={false} />);
    expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
  });

  it('renders modal when isOpen is true', () => {
    render(<AddBeneficiaryModal {...defaultProps} />);
    expect(screen.getByRole('dialog')).toBeInTheDocument();
    expect(screen.getByText('Add New Beneficiary')).toBeInTheDocument();
  });

  it('renders all form fields', () => {
    render(<AddBeneficiaryModal {...defaultProps} />);
    expect(getNameInput()).toBeInTheDocument();
    expect(getAddressInput()).toBeInTheDocument();
    expect(getPhoneInput()).toBeInTheDocument();
  });

  it('shows validation error when name is empty', async () => {
    render(<AddBeneficiaryModal {...defaultProps} />);

    fireEvent.click(getSubmitButton());

    await waitFor(() => {
      expect(screen.getByText('Please enter a name for the beneficiary')).toBeInTheDocument();
    });

    expect(mockCreateBeneficiary).not.toHaveBeenCalled();
    expect(defaultProps.onSuccess).not.toHaveBeenCalled();
  });

  it('calls createBeneficiary with correct data on submit', async () => {
    mockCreateBeneficiary.mockResolvedValueOnce({ id: 123, name: 'Test Name' });

    render(<AddBeneficiaryModal {...defaultProps} />);

    fireEvent.change(getNameInput(), { target: { value: 'Grandma Julia' } });
    fireEvent.change(getAddressInput(), { target: { value: '123 Main St' } });
    fireEvent.change(getPhoneInput(), { target: { value: '+1 555-123-4567' } });

    fireEvent.click(getSubmitButton());

    await waitFor(() => {
      expect(mockCreateBeneficiary).toHaveBeenCalledWith({
        name: 'Grandma Julia',
        address: '123 Main St',
        phone: '+1 555-123-4567',
      });
    });
  });

  it('calls onSuccess and onClose after successful creation', async () => {
    mockCreateBeneficiary.mockResolvedValueOnce({ id: 456, name: 'Test Person' });

    render(<AddBeneficiaryModal {...defaultProps} />);

    await userEvent.type(getNameInput(), 'Test Person');
    fireEvent.click(getSubmitButton());

    await waitFor(() => {
      expect(defaultProps.onSuccess).toHaveBeenCalledWith({
        id: 456,
        name: 'Test Person',
      });
      expect(defaultProps.onClose).toHaveBeenCalled();
    });
  });

  it('shows error message on API failure', async () => {
    mockCreateBeneficiary.mockRejectedValueOnce(new Error('Network error'));

    render(<AddBeneficiaryModal {...defaultProps} />);

    await userEvent.type(getNameInput(), 'Test Name');
    fireEvent.click(getSubmitButton());

    await waitFor(() => {
      expect(screen.getByText('Network error')).toBeInTheDocument();
    });

    expect(defaultProps.onClose).not.toHaveBeenCalled();
    expect(defaultProps.onSuccess).not.toHaveBeenCalled();
  });

  it('closes modal when Cancel button is clicked', () => {
    render(<AddBeneficiaryModal {...defaultProps} />);

    fireEvent.click(getCancelButton());

    expect(defaultProps.onClose).toHaveBeenCalled();
  });

  it('closes modal when clicking backdrop', () => {
    render(<AddBeneficiaryModal {...defaultProps} />);

    const backdrop = screen.getByRole('dialog');
    fireEvent.click(backdrop);

    expect(defaultProps.onClose).toHaveBeenCalled();
  });

  it('does not close modal when clicking inside the modal content', () => {
    render(<AddBeneficiaryModal {...defaultProps} />);

    const modalTitle = screen.getByText('Add New Beneficiary');
    fireEvent.click(modalTitle);

    expect(defaultProps.onClose).not.toHaveBeenCalled();
  });

  it('closes modal when Escape key is pressed', () => {
    render(<AddBeneficiaryModal {...defaultProps} />);

    fireEvent.keyDown(document, { key: 'Escape' });

    expect(defaultProps.onClose).toHaveBeenCalled();
  });

  it('disables form during submission', async () => {
    let resolvePromise: (value: { id: number; name: string }) => void;
    const promise = new Promise<{ id: number; name: string }>((resolve) => {
      resolvePromise = resolve;
    });
    mockCreateBeneficiary.mockReturnValueOnce(promise);

    render(<AddBeneficiaryModal {...defaultProps} />);

    await userEvent.type(getNameInput(), 'Test Name');
    fireEvent.click(getSubmitButton());

    await waitFor(() => {
      expect(getNameInput()).toBeDisabled();
      expect(getCancelButton()).toBeDisabled();
    });

    resolvePromise!({ id: 1, name: 'Test Name' });
  });

  it('clears form when modal is reopened', async () => {
    const { rerender } = render(<AddBeneficiaryModal {...defaultProps} />);

    await userEvent.type(getNameInput(), 'Some Name');

    rerender(<AddBeneficiaryModal {...defaultProps} isOpen={false} />);
    rerender(<AddBeneficiaryModal {...defaultProps} isOpen={true} />);

    expect(getNameInput()).toHaveValue('');
  });

  it('trims whitespace from inputs', async () => {
    mockCreateBeneficiary.mockResolvedValueOnce({ id: 789, name: 'Trimmed Name' });

    render(<AddBeneficiaryModal {...defaultProps} />);

    await userEvent.type(getNameInput(), '  Trimmed Name  ');
    fireEvent.click(getSubmitButton());

    await waitFor(() => {
      expect(mockCreateBeneficiary).toHaveBeenCalledWith({
        name: 'Trimmed Name',
        address: undefined,
        phone: undefined,
      });
    });
  });

  it('dismisses error when user starts typing', async () => {
    render(<AddBeneficiaryModal {...defaultProps} />);

    fireEvent.click(getSubmitButton());

    await waitFor(() => {
      expect(screen.getByText('Please enter a name for the beneficiary')).toBeInTheDocument();
    });

    await userEvent.type(getNameInput(), 'N');

    expect(screen.queryByText('Please enter a name for the beneficiary')).not.toBeInTheDocument();
  });
});
