import React from 'react';
import { MockedProvider } from '@apollo/client/testing';
import {
    act,
    fireEvent,
    screen,
    waitFor,
    within,
} from '@testing-library/react';
import '@testing-library/jest-dom';
import {
    accountPayeeBankDetailsIndividual,
    accountPayeeBankDetailsCompany,
} from 'src/__fixtures__/graphql/account-payee-bank-details';
import { useAccountPayeeBankDetailsQuery } from 'src/apollo/queries/account-payee-bank-details';
import PaymentDetailApproved, {
    PaymentDetailApprovedProps,
} from '../payment-detail-approved';
import { createIdentity, renderInAppContext } from '@theorchard/suite-testing';
import { EMPTY_CHAR } from '@theorchard/accounting-apps-shared';

jest.mock('src/apollo/queries/account-payee-bank-details', () => ({
    useAccountPayeeBankDetailsQuery: jest.fn(),
}));

describe('PaymentDetailApproved', () => {
    const defaultProps: PaymentDetailApprovedProps = {
        accountId: '12345',
    };

    const render = (props?: PaymentDetailApprovedProps) =>
        renderInAppContext(
            <MockedProvider>
                <PaymentDetailApproved {...defaultProps} {...props} />
            </MockedProvider>,
            {
                identity: createIdentity({}),
            }
        );

    it('renders loading state', () => {
        (useAccountPayeeBankDetailsQuery as jest.Mock).mockReturnValue({
            data: null,
            loading: true,
        });

        render();

        expect(screen.getByTestId('SkeletonLoader')).toBeInTheDocument();
    });

    // PhoneNumber loads libphonenumber-js asynchronously and updates its
    // state when the import resolves. Flushing a macrotask inside act()
    // drains that pending update so it does not fire outside act().
    const flushPhoneLib = () =>
        act(async () => {
            await new Promise(resolve => {
                setTimeout(resolve, 0);
            });
        });

    it('renders payment details correctly for an individual', async () => {
        (useAccountPayeeBankDetailsQuery as jest.Mock).mockReturnValue({
            data: accountPayeeBankDetailsIndividual,
            loading: false,
        });

        render();

        expect(screen.getByText('First name')).toBeInTheDocument();
        expect(screen.getByText('John')).toBeInTheDocument();
        expect(screen.getByText('Last Name')).toBeInTheDocument();
        expect(screen.getByText('Doe')).toBeInTheDocument();
        expect(screen.getByText('Approval notes')).toBeInTheDocument();
        expect(screen.getByText('Approved for payment1')).toBeInTheDocument();
        expect(screen.getByText('Phone Number')).toBeInTheDocument();

        await flushPhoneLib();
    });

    it('renders payment details correctly for a company', async () => {
        (useAccountPayeeBankDetailsQuery as jest.Mock).mockReturnValue({
            data: accountPayeeBankDetailsCompany,
            loading: false,
        });

        render();

        expect(screen.getByText('Company Name')).toBeInTheDocument();
        expect(screen.getByText('Company name')).toBeInTheDocument();
        expect(screen.getByText('Approval notes')).toBeInTheDocument();
        expect(screen.getByText('Approved for payment2')).toBeInTheDocument();
        expect(screen.getByText('Phone Number')).toBeInTheDocument();

        await flushPhoneLib();
    });

    describe('email and phone in banking', () => {
        test('renders correct phone number', async () => {
            (useAccountPayeeBankDetailsQuery as jest.Mock).mockReturnValue({
                data: accountPayeeBankDetailsIndividual,
                loading: false,
            });

            render();

            const phoneLabel = screen.getByText('Phone Number');
            const phoneNumberFieldEl = phoneLabel.closest(
                '.PageCol'
            )! as HTMLElement;
            const previewButtonEl = phoneNumberFieldEl.querySelector('.btn');

            if (previewButtonEl) fireEvent.click(previewButtonEl);

            await waitFor(() => {
                expect(
                    within(phoneNumberFieldEl).getByText('+1 800702328')
                ).toBeInTheDocument();
            });
        });

        test('renders EMPTY_CHAR if phone number is not provided', async () => {
            (useAccountPayeeBankDetailsQuery as jest.Mock).mockReturnValue({
                data: {
                    abacusAccount: {
                        ...accountPayeeBankDetailsIndividual.abacusAccount,
                        accountPayee: {
                            ...accountPayeeBankDetailsIndividual.abacusAccount
                                .accountPayee,
                            bankDetails: {
                                ...accountPayeeBankDetailsIndividual
                                    .abacusAccount.accountPayee.bankDetails,
                                contact: {
                                    ...accountPayeeBankDetailsIndividual
                                        .abacusAccount.accountPayee.bankDetails
                                        .contact,
                                    phone: null,
                                    phoneCountry: null,
                                },
                            },
                        } as any,
                    },
                },
                loading: false,
            });

            render();

            const phoneLabel = screen.getByText('Phone Number');
            const phoneNumberFieldEl = phoneLabel.closest(
                '.PageCol'
            )! as HTMLElement;

            await waitFor(() => {
                expect(
                    within(phoneNumberFieldEl).getByText(EMPTY_CHAR)
                ).toBeInTheDocument();
            });
        });

        test('renders correct email address', async () => {
            (useAccountPayeeBankDetailsQuery as jest.Mock).mockReturnValue({
                data: accountPayeeBankDetailsIndividual,
                loading: false,
            });

            render();

            const emailLabel = screen.getByText('Email');
            const emailFieldEl = emailLabel.closest('.PageCol')! as HTMLElement;

            expect(
                within(emailFieldEl).getByText('test@test.com')
            ).toBeInTheDocument();

            await flushPhoneLib();
        });

        test('render EMPTY_CHAR if email address is not provided', async () => {
            (useAccountPayeeBankDetailsQuery as jest.Mock).mockReturnValue({
                data: {
                    abacusAccount: {
                        ...accountPayeeBankDetailsIndividual.abacusAccount,
                        accountPayee: {
                            ...accountPayeeBankDetailsIndividual.abacusAccount
                                .accountPayee,
                            bankDetails: {
                                ...accountPayeeBankDetailsIndividual
                                    .abacusAccount.accountPayee.bankDetails,
                                contact: {
                                    ...accountPayeeBankDetailsIndividual
                                        .abacusAccount.accountPayee.bankDetails
                                        .contact,
                                    email: null,
                                },
                            },
                        } as any,
                    },
                },
                loading: false,
            });

            render();

            const emailLabel = screen.getByText('Email');
            const emailFieldEl = emailLabel.closest('.PageCol')! as HTMLElement;

            expect(
                within(emailFieldEl).getByText(EMPTY_CHAR)
            ).toBeInTheDocument();

            await flushPhoneLib();
        });
    });
});
