import React from 'react';
import { fireEvent, screen, waitFor, within } from '@testing-library/react';
import { ABACUS_ACTION_STATUSES } from '@theorchard/accounting-apps-shared';
import { renderInAppContext } from '@theorchard/suite-testing';
import * as payeeQueries from 'src/apollo/queries/payees';
import { PayeeActions } from '../payee-management';

jest.mock('src/apollo/queries/payees', () => ({
    useGetPayeeCollaboratorContact: jest.fn(() => ({
        contact: {
            firstName: 'First',
            lastName: 'Last',
            email: 'contact@example.com',
        },
        collaborator: {
            id: 'collab-1',
            name: 'Collaborator Name',
            currentBalance: null,
        },
        loading: false,
    })),
    useGetPayeeDetailsById: jest.fn(() => ({
        payee: {
            payeeName: 'Payee One',
            payeeKycNotification: null,
            actionStates: [
                {
                    actionStatus: ABACUS_ACTION_STATUSES.RUNNING,
                    message: 'state-running',
                },
            ],
            payeeCollaborator: null,
        },
        bankDetails: null,
        loading: false,
    })),
    useGetPayeesList: jest.fn(),
}));

const releaseAbacusPayeeSpy = jest.fn();
jest.mock('src/apollo/mutations/payee', () => ({
    useReleaseAbacusPayee: jest.fn(() => ({
        releaseAbacusPayee: releaseAbacusPayeeSpy,
    })),
}));

const deleteKycSpy = jest.fn();
jest.mock('src/apollo/mutations/delete-payee-kyc-notification', () => ({
    useDeletePayeeKycNotificationMutation: jest.fn(() => ({
        deletePayeeKycNotification: deleteKycSpy,
    })),
}));

const updateAbacusStateSpy = jest.fn();
jest.mock('src/apollo/mutations/abacus-state', () => ({
    useUpdateAbacusState: jest.fn(() => ({
        updateAbacusState: updateAbacusStateSpy,
    })),
}));

const bankDetailsFixture = {
    contact: { firstName: 'Jane', lastName: 'Doe' },
    payoutMethod: {
        country: 'GB',
        currency: 'GBP',
        bankFieldDetails: [{ name: 'IBAN', value: 'GB29NWBK60161331926819' }],
    },
};

const basePayee = (actionStatus: string, withKyc = false) =>
    ({
        payeeId: 'p-1',
        payeeName: 'Payee One',
        payeeKycNotification: withKyc
            ? { fileUploadLink: 'https://example.test/kyc' }
            : null,
        actionStates: [
            {
                abacusStateId: 's-1',
                actionStatus,
                message: `state-${actionStatus}`,
            },
        ],
        payeeCollaborator: {
            collaborator: {
                id: 'collab-1',
                label: {
                    __typename: 'Vendor',
                },
            },
        },
    }) as any;

const renderActions = (actionStatus: string, withKyc = false) => {
    const refetch = jest.fn().mockResolvedValue(undefined);
    const utils = renderInAppContext(
        <PayeeActions
            payee={basePayee(actionStatus, withKyc)}
            refetchPayeeList={refetch}
        />
    );
    return { refetch, ...utils };
};

describe('PayeeActions', () => {
    beforeEach(() => {
        (payeeQueries.useGetPayeeDetailsById as jest.Mock).mockImplementation(
            () => ({
                payee: {
                    payeeName: 'Payee One',
                    payeeKycNotification: null,
                    actionStates: [
                        {
                            actionStatus: ABACUS_ACTION_STATUSES.RUNNING,
                            message: 'state-running',
                        },
                    ],
                    payeeCollaborator: null,
                },
                bankDetails: bankDetailsFixture,
                loading: false,
            })
        );

        releaseAbacusPayeeSpy.mockClear();
        deleteKycSpy.mockClear();
        updateAbacusStateSpy.mockClear();
        jest.spyOn(window, 'open').mockImplementation(() => null);
    });

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

    const getActionButtons = (container: HTMLElement) =>
        Array.from(
            container.querySelectorAll('.PayeeManagement-actions button')
        );

    it('always renders the external link button and opens a new tab on click', () => {
        const { container } = renderActions(ABACUS_ACTION_STATUSES.INIT, true);
        const [externalLinkBtn] = getActionButtons(container);
        expect(externalLinkBtn).toBeTruthy();

        fireEvent.click(externalLinkBtn);
        expect(window.open).toHaveBeenCalledWith(
            'https://example.test/kyc',
            '_blank'
        );
    });

    it('INIT does not render the details glyph or modal', () => {
        const { container } = renderActions(ABACUS_ACTION_STATUSES.INIT, true);
        const buttons = getActionButtons(container);
        expect(buttons).toHaveLength(1);

        // Modal content should not be present
        expect(
            screen.queryByText('Collaborator Payee')
        ).not.toBeInTheDocument();
    });

    describe.each([
        ABACUS_ACTION_STATUSES.RUNNING,
        ABACUS_ACTION_STATUSES.COMPLETE,
    ])('%s (with Reject)', status => {
        it('shows details glyph and opens modal (no KYC -> moreDetails, with Reject)', async () => {
            const { container } = renderActions(status, false);
            const buttons = getActionButtons(container);
            expect(buttons.length).toBe(1);

            fireEvent.click(buttons[0]);
            expect(screen.getByText('Collaborator Payee')).toBeInTheDocument();

            // Header
            const header = within(
                screen.getAllByTestId('SuitePageHeader-wrapper')[0]
            );
            expect(header.getByText('Payee One')).toBeInTheDocument();
            expect(header.getByText('p-1')).toBeInTheDocument();

            // Contact rows
            const contact = within(screen.getAllByTestId('SuiteSection')[0]);
            expect(contact.getByText('First')).toBeInTheDocument();
            expect(contact.getByText('Last')).toBeInTheDocument();
            expect(
                contact.getByText('contact@example.com')
            ).toBeInTheDocument();

            expect(
                header.getByRole('button', { name: 'Reject' })
            ).toBeInTheDocument();

            // Bank details
            expect(
                screen.getByText('Bank Details Submitted')
            ).toBeInTheDocument();
            expect(
                screen.getByText('GB29NWBK60161331926819')
            ).toBeInTheDocument();

            // Close the modal
            fireEvent.click(screen.getByText('Cancel'));
        });

        it('with KYC -> warning glyph, modal opens, Reject opens Sidecar, confirm runs rejection flow', async () => {
            (
                payeeQueries.useGetPayeeDetailsById as jest.Mock
            ).mockReturnValueOnce({
                payee: {
                    payeeName: 'Payee One',
                    payeeKycNotification: {
                        fileUploadLink: 'https://example.test/kyc',
                    },
                    actionStates: [
                        {
                            actionStatus: status,
                            message: `state-${status}`,
                        },
                    ],
                    payeeCollaborator: null,
                },
                bankDetails: bankDetailsFixture,
                loading: false,
            });

            const { container, refetch } = renderActions(status, true);
            const buttons = getActionButtons(container);
            expect(buttons.length).toBeGreaterThanOrEqual(2);

            fireEvent.click(buttons[1]);
            expect(screen.getByText('Collaborator Payee')).toBeInTheDocument();

            const header = within(
                screen.getAllByTestId('SuitePageHeader-wrapper')[0]
            );
            fireEvent.click(header.getByRole('button', { name: 'Reject' }));
            expect(
                screen.getByText('Reject Payee Details')
            ).toBeInTheDocument();

            fireEvent.change(screen.getByPlaceholderText(/Add notes here/i), {
                target: { value: 'Bad bank details' },
            });

            fireEvent.click(
                screen.getByRole('button', { name: 'Confirm Rejection' })
            );

            await waitFor(() => {
                expect(releaseAbacusPayeeSpy).toHaveBeenCalled();
                expect(deleteKycSpy).toHaveBeenCalled();
                expect(updateAbacusStateSpy).toHaveBeenCalledWith({
                    variables: expect.objectContaining({
                        actionStatus: ABACUS_ACTION_STATUSES.REJECTED,
                        message: 'Bad bank details',
                    }),
                });
                expect(refetch).toHaveBeenCalled();
            });

            // Close the modal
            fireEvent.click(screen.getByText('Cancel'));
        });
    });

    describe.each([
        ABACUS_ACTION_STATUSES.REJECTED,
        ABACUS_ACTION_STATUSES.ERROR,
        ABACUS_ACTION_STATUSES.APPROVED,
    ])('%s (no Reject)', status => {
        it('shows details glyph and opens modal (no KYC -> moreDetails, no Reject)', async () => {
            const { container } = renderActions(status, false);
            const buttons = getActionButtons(container);
            expect(buttons.length).toBe(1);

            fireEvent.click(buttons[0]);
            expect(screen.getByText('Collaborator Payee')).toBeInTheDocument();

            // Header
            const header = within(
                screen.getAllByTestId('SuitePageHeader-wrapper')[0]
            );
            expect(header.getByText('Payee One')).toBeInTheDocument();
            expect(header.getByText('p-1')).toBeInTheDocument();

            // Contact rows
            const contact = within(screen.getAllByTestId('SuiteSection')[0]);
            expect(contact.getByText('First')).toBeInTheDocument();
            expect(contact.getByText('Last')).toBeInTheDocument();
            expect(
                contact.getByText('contact@example.com')
            ).toBeInTheDocument();

            expect(
                header.queryByRole('button', { name: 'Reject' })
            ).not.toBeInTheDocument();

            // Close the modal
            fireEvent.click(screen.getByText('Cancel'));
        });

        it('with KYC -> warning glyph, modal opens, no Reject', async () => {
            const { container } = renderActions(status, true);
            const buttons = getActionButtons(container);
            expect(buttons.length).toBeGreaterThanOrEqual(2);

            fireEvent.click(buttons[1]);
            expect(screen.getByText('Collaborator Payee')).toBeInTheDocument();

            const header = within(
                screen.getAllByTestId('SuitePageHeader-wrapper')[0]
            );
            expect(
                header.queryByRole('button', { name: 'Reject' })
            ).not.toBeInTheDocument();

            // Close the modal
            fireEvent.click(screen.getByText('Cancel'));
        });
    });
});
